Convert interfacemanager to use mdsal-binding-util
[genius.git] / interfacemanager / interfacemanager-impl / src / main / java / org / opendaylight / genius / interfacemanager / servicebindings / flowbased / listeners / FlowBasedServicesConfigListener.java
1 /*
2  * Copyright (c) 2016, 2017 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.listeners;
9
10 import static org.opendaylight.mdsal.binding.util.Datastore.OPERATIONAL;
11
12 import com.google.common.util.concurrent.ListenableFuture;
13 import java.util.ArrayList;
14 import java.util.Collection;
15 import java.util.List;
16 import java.util.Objects;
17 import java.util.concurrent.Callable;
18 import javax.annotation.PreDestroy;
19 import javax.inject.Inject;
20 import javax.inject.Singleton;
21 import org.apache.aries.blueprint.annotation.service.Reference;
22 import org.eclipse.jdt.annotation.NonNull;
23 import org.eclipse.jdt.annotation.Nullable;
24 import org.opendaylight.genius.interfacemanager.IfmConstants;
25 import org.opendaylight.genius.interfacemanager.commons.InterfaceManagerCommonUtils;
26 import org.opendaylight.genius.interfacemanager.recovery.impl.InterfaceServiceRecoveryHandler;
27 import org.opendaylight.genius.interfacemanager.servicebindings.flowbased.config.factory.FlowBasedServicesConfigAddable;
28 import org.opendaylight.genius.interfacemanager.servicebindings.flowbased.config.factory.FlowBasedServicesConfigRemovable;
29 import org.opendaylight.genius.interfacemanager.servicebindings.flowbased.config.factory.FlowBasedServicesRendererFactoryResolver;
30 import org.opendaylight.genius.interfacemanager.servicebindings.flowbased.utilities.FlowBasedServicesUtils;
31 import org.opendaylight.genius.mdsalutil.NwConstants;
32 import org.opendaylight.genius.utils.clustering.EntityOwnershipUtils;
33 import org.opendaylight.infrautils.jobcoordinator.JobCoordinator;
34 import org.opendaylight.mdsal.binding.api.ClusteredDataTreeChangeListener;
35 import org.opendaylight.mdsal.binding.api.DataBroker;
36 import org.opendaylight.mdsal.binding.api.DataObjectModification;
37 import org.opendaylight.mdsal.binding.api.DataTreeIdentifier;
38 import org.opendaylight.mdsal.binding.api.DataTreeModification;
39 import org.opendaylight.mdsal.binding.util.ManagedNewTransactionRunner;
40 import org.opendaylight.mdsal.binding.util.ManagedNewTransactionRunnerImpl;
41 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
42 import org.opendaylight.serviceutils.srm.RecoverableListener;
43 import org.opendaylight.serviceutils.srm.ServiceRecoveryRegistry;
44 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.ServiceBindings;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.ServiceModeBase;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.bound.services.state.list.BoundServicesState;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.service.bindings.ServicesInfo;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.service.bindings.ServicesInfoKey;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.service.bindings.services.info.BoundServices;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.service.bindings.services.info.BoundServicesKey;
52 import org.opendaylight.yangtools.concepts.ListenerRegistration;
53 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
54 import org.slf4j.Logger;
55 import org.slf4j.LoggerFactory;
56
57 @Singleton
58 public class FlowBasedServicesConfigListener implements ClusteredDataTreeChangeListener<ServicesInfo>,
59         RecoverableListener {
60
61     private static final Logger LOG = LoggerFactory.getLogger(FlowBasedServicesConfigListener.class);
62     private static final Logger EVENT_LOGGER = LoggerFactory.getLogger("GeniusEventLogger");
63
64     private ListenerRegistration<FlowBasedServicesConfigListener> listenerRegistration;
65     private final DataBroker dataBroker;
66     private final ManagedNewTransactionRunner txRunner;
67     private final EntityOwnershipUtils entityOwnershipUtils;
68     private final JobCoordinator coordinator;
69     private final FlowBasedServicesRendererFactoryResolver flowBasedServicesRendererFactoryResolver;
70     private final InterfaceManagerCommonUtils interfaceManagerCommonUtils;
71
72     @Inject
73     public FlowBasedServicesConfigListener(@Reference final DataBroker dataBroker,
74                                            final EntityOwnershipUtils entityOwnershipUtils,
75                                            @Reference final JobCoordinator coordinator,
76                                            final FlowBasedServicesRendererFactoryResolver
77                                                        flowBasedServicesRendererFactoryResolver,
78                                            final InterfaceManagerCommonUtils interfaceManagerCommonUtils,
79                                            final InterfaceServiceRecoveryHandler interfaceServiceRecoveryHandler,
80                                            @Reference final ServiceRecoveryRegistry serviceRecoveryRegistry) {
81         this.dataBroker = dataBroker;
82         this.txRunner = new ManagedNewTransactionRunnerImpl(dataBroker);
83         this.entityOwnershipUtils = entityOwnershipUtils;
84         this.coordinator = coordinator;
85         this.flowBasedServicesRendererFactoryResolver = flowBasedServicesRendererFactoryResolver;
86         this.interfaceManagerCommonUtils = interfaceManagerCommonUtils;
87         registerListener();
88         serviceRecoveryRegistry.addRecoverableListener(interfaceServiceRecoveryHandler.buildServiceRegistryKey(),
89                 this);
90     }
91
92     protected InstanceIdentifier<ServicesInfo> getWildCardPath() {
93         return InstanceIdentifier.create(ServiceBindings.class).child(ServicesInfo.class);
94     }
95
96     @Override
97     public void registerListener() {
98         registerListener(LogicalDatastoreType.CONFIGURATION, dataBroker);
99     }
100
101     public void registerListener(final LogicalDatastoreType dsType, final DataBroker db) {
102         final DataTreeIdentifier<ServicesInfo> treeId = DataTreeIdentifier.create(dsType, getWildCardPath());
103         listenerRegistration = db.registerDataTreeChangeListener(treeId, FlowBasedServicesConfigListener.this);
104     }
105
106     @Override
107     public  void deregisterListener() {
108         close();
109     }
110
111     @PreDestroy
112     public void close() {
113         if (listenerRegistration != null) {
114             try {
115                 listenerRegistration.close();
116             } finally {
117                 listenerRegistration = null;
118             }
119         }
120     }
121
122     @Override
123     public void onDataTreeChanged(@NonNull final Collection<DataTreeModification<ServicesInfo>> collection) {
124         collection.forEach(
125             servicesInfoDataTreeModification -> servicesInfoDataTreeModification.getRootNode()
126                     .getModifiedChildren().stream()
127                     .filter(dataObjectModification -> dataObjectModification.getDataType().equals(
128                             BoundServices.class))
129                     .forEach(dataObjectModification -> onBoundServicesChanged(
130                             (DataObjectModification<BoundServices>) dataObjectModification,
131                             servicesInfoDataTreeModification.getRootPath().getRootIdentifier(),
132                             servicesInfoDataTreeModification.getRootNode()))
133         );
134     }
135
136     private InstanceIdentifier<BoundServices> getBoundServicesInstanceIdentifier(
137             final InstanceIdentifier<ServicesInfo> rootIdentifier, final BoundServicesKey boundServicesKey) {
138         return rootIdentifier.child(BoundServices.class , boundServicesKey);
139     }
140
141     private synchronized void onBoundServicesChanged(final DataObjectModification<BoundServices> dataObjectModification,
142                                                      final InstanceIdentifier<ServicesInfo> rootIdentifier,
143                                                      final DataObjectModification<ServicesInfo> rootNode) {
144         if (rootNode.getDataAfter() != null) {
145             List<BoundServices> boundServices = new ArrayList<>();
146             if (rootNode.getDataAfter().getBoundServices() != null) {
147                 boundServices = new ArrayList<>(rootNode.getDataAfter().getBoundServices().values());
148             }
149             final ServicesInfoKey servicesInfoKey = rootNode.getDataAfter().key();
150             final BoundServices boundServicesBefore = dataObjectModification.getDataBefore();
151             final BoundServices boundServicesAfter =  dataObjectModification.getDataAfter();
152
153             switch (dataObjectModification.getModificationType()) {
154                 case DELETE:
155                     if (boundServicesBefore != null) {
156                         remove(servicesInfoKey, boundServicesBefore, boundServices);
157                     }
158                     break;
159                 case SUBTREE_MODIFIED:
160                     if (boundServicesBefore != null) {
161                         update(servicesInfoKey, getBoundServicesInstanceIdentifier(rootIdentifier,
162                             boundServicesBefore.key()), boundServicesBefore, boundServicesAfter, boundServices);
163                     }
164                     break;
165                 case WRITE:
166                     if (boundServicesBefore == null) {
167                         add(servicesInfoKey, boundServicesAfter, boundServices);
168                     } else {
169                         update(servicesInfoKey, getBoundServicesInstanceIdentifier(rootIdentifier,
170                             boundServicesBefore.key()), boundServicesBefore, boundServicesAfter, boundServices);
171                     }
172                     break;
173                 default:
174                     LOG.error("Unhandled Modificiation Type{} for {}", dataObjectModification.getModificationType(),
175                         rootIdentifier);
176
177             }
178         }
179     }
180
181     protected void remove(@NonNull final ServicesInfoKey serviceKey, @NonNull  final BoundServices boundServiceOld,
182                           final List<BoundServices> boundServicesList) {
183         if (!entityOwnershipUtils.isEntityOwner(IfmConstants.INTERFACE_SERVICE_BINDING_ENTITY,
184                 IfmConstants.INTERFACE_SERVICE_BINDING_ENTITY)) {
185             return;
186         }
187         LOG.info("Service Binding Entry removed for Interface: {}, ServiceName: {}, ServicePriority {}",
188                 serviceKey.getInterfaceName(), boundServiceOld.getServiceName(), boundServiceOld.getServicePriority());
189         LOG.trace("Service Binding Entry removed for Interface: {}, Data: {}", serviceKey.getInterfaceName(),
190             boundServiceOld);
191         FlowBasedServicesConfigRemovable flowBasedServicesConfigRemovable =
192                 flowBasedServicesRendererFactoryResolver.getFlowBasedServicesRendererFactory(
193                         serviceKey.getServiceMode()).getFlowBasedServicesRemoveRenderer();
194         RendererConfigRemoveWorker configWorker = new RendererConfigRemoveWorker(serviceKey.getInterfaceName(),
195             serviceKey.getServiceMode(), flowBasedServicesConfigRemovable, boundServiceOld, boundServicesList);
196         coordinator.enqueueJob(serviceKey.getInterfaceName(), configWorker, IfmConstants.JOB_MAX_RETRIES);
197     }
198
199     protected void update(ServicesInfoKey serviceKey, InstanceIdentifier<BoundServices> key,
200                           BoundServices boundServiceOld, BoundServices boundServiceNew,
201                           List<BoundServices> boundServicesList) {
202         if (!Objects.equals(boundServiceOld, boundServiceNew)) {
203             /*
204              * In some cases ACL needs to change metadata passed from dispatcher tables to ACL tables dynamically.
205              * For this update operation has been enhanced to support same. This is only supported for ACL for now
206              * and the functionality will remain same for all other applications as it was earlier.
207              */
208             if (boundServiceNew.getServicePriority() != null && (
209                 boundServiceNew.getServicePriority().toJava() == NwConstants.ACL_SERVICE_INDEX
210                     || boundServiceNew.getServicePriority().toJava() == NwConstants.EGRESS_ACL_SERVICE_INDEX)
211                     && !Objects.equals(boundServiceOld, boundServiceNew)) {
212                 LOG.info("Bound services flow update for service {}", boundServiceNew.getServiceName());
213                 add(serviceKey, boundServiceNew, boundServicesList);
214             } else {
215                 LOG.warn("Service Binding entry update not allowed for: {}, ServiceName: {}",
216                         serviceKey.getInterfaceName(), boundServiceNew.getServiceName());
217                 LOG.trace("Service Binding Entry update not allowed for Interface: {}, Old Data: {}, New Data: {}",
218                         serviceKey.getInterfaceName(), boundServiceNew, boundServiceOld);
219             }
220         }
221     }
222
223     protected void add(ServicesInfoKey serviceKey, BoundServices boundServicesNew,
224                        List<BoundServices> boundServicesList) {
225         if (!entityOwnershipUtils.isEntityOwner(IfmConstants.INTERFACE_SERVICE_BINDING_ENTITY,
226                 IfmConstants.INTERFACE_SERVICE_BINDING_ENTITY)) {
227             return;
228         }
229         LOG.info("Service Binding Entry created for Interface: {}, ServiceName: {}, ServicePriority {}",
230                 serviceKey.getInterfaceName(), boundServicesNew.getServiceName(),
231                 boundServicesNew.getServicePriority());
232         LOG.trace("Service Binding Entry created for Interface: {}, Data: {}", serviceKey.getInterfaceName(),
233             boundServicesNew);
234         FlowBasedServicesConfigAddable flowBasedServicesAddable = flowBasedServicesRendererFactoryResolver
235             .getFlowBasedServicesRendererFactory(serviceKey.getServiceMode()).getFlowBasedServicesAddRenderer();
236         RendererConfigAddWorker configWorker = new RendererConfigAddWorker(serviceKey.getInterfaceName(),
237             serviceKey.getServiceMode(), flowBasedServicesAddable, boundServicesNew, boundServicesList);
238         coordinator.enqueueJob(serviceKey.getInterfaceName(), configWorker, IfmConstants.JOB_MAX_RETRIES);
239     }
240
241     private class RendererConfigAddWorker implements Callable<List<? extends ListenableFuture<?>>> {
242         private final String interfaceName;
243         Class<? extends ServiceModeBase> serviceMode;
244         FlowBasedServicesConfigAddable flowBasedServicesAddable;
245         BoundServices boundServicesNew;
246         List<BoundServices> boundServicesList;
247
248         RendererConfigAddWorker(String interfaceName, Class<? extends ServiceModeBase> serviceMode,
249                                 FlowBasedServicesConfigAddable flowBasedServicesAddable,
250                                 BoundServices boundServicesNew, List<BoundServices> boundServicesList) {
251             this.interfaceName = interfaceName;
252             this.serviceMode = serviceMode;
253             this.flowBasedServicesAddable = flowBasedServicesAddable;
254             this.boundServicesNew = boundServicesNew;
255             this.boundServicesList = boundServicesList;
256         }
257
258         @Override
259         public List<? extends ListenableFuture<?>> call() {
260             List<ListenableFuture<?>> futures = new ArrayList<>();
261             futures.add(txRunner.callWithNewReadWriteTransactionAndSubmit(OPERATIONAL, tx -> {
262                 BoundServicesState boundServicesState = FlowBasedServicesUtils
263                         .getBoundServicesState(tx, interfaceName, serviceMode);
264                 EVENT_LOGGER.debug("IFM-ServiceBindingConfig, BIND {}, {}", interfaceName,
265                         boundServicesNew.getServiceName());
266                 // if service-binding state is not present, construct the same using ifstate
267                 if (boundServicesState == null) {
268                     Interface ifState = interfaceManagerCommonUtils.getInterfaceState(interfaceName);
269                     if (ifState == null) {
270                         LOG.debug("Interface not operational, will bind service whenever interface comes up: {}",
271                                 interfaceName);
272                         return;
273                     }
274                     boundServicesState = FlowBasedServicesUtils.buildBoundServicesState(ifState, serviceMode);
275                     FlowBasedServicesUtils.addBoundServicesState(tx, interfaceName, boundServicesState);
276                 }
277                 flowBasedServicesAddable.bindService(futures, interfaceName, boundServicesNew, boundServicesList,
278                         boundServicesState);
279             }));
280             return futures;
281         }
282     }
283
284     private class RendererConfigRemoveWorker implements Callable<List<? extends ListenableFuture<?>>> {
285         private final String interfaceName;
286         Class<? extends ServiceModeBase> serviceMode;
287         FlowBasedServicesConfigRemovable flowBasedServicesConfigRemovable;
288         BoundServices boundServicesNew;
289         @Nullable List<BoundServices> boundServicesList;
290
291         RendererConfigRemoveWorker(String interfaceName, Class<? extends ServiceModeBase> serviceMode,
292                                    FlowBasedServicesConfigRemovable flowBasedServicesConfigRemovable,
293                                    BoundServices boundServicesNew, @Nullable List<BoundServices> boundServicesList) {
294             this.interfaceName = interfaceName;
295             this.serviceMode = serviceMode;
296             this.flowBasedServicesConfigRemovable = flowBasedServicesConfigRemovable;
297             this.boundServicesNew = boundServicesNew;
298             this.boundServicesList = boundServicesList;
299         }
300
301         @Override
302         public List<? extends ListenableFuture<?>> call() {
303             List<ListenableFuture<?>> futures = new ArrayList<>();
304             futures.add(txRunner.callWithNewReadWriteTransactionAndSubmit(OPERATIONAL, tx -> {
305                 // if this is the last service getting unbound, remove service-state cache information
306                 BoundServicesState boundServiceState = FlowBasedServicesUtils.getBoundServicesState(
307                         tx, interfaceName, serviceMode);
308                 if (boundServiceState == null) {
309                     LOG.warn("bound-service-state is not present for interface:{}, service-mode:{}, "
310                                     + "service-name:{}, service-priority:{}", interfaceName, serviceMode,
311                             boundServicesNew.getServiceName(), boundServicesNew.getServicePriority());
312                     return;
313                 }
314                 if (boundServicesList == null || boundServicesList.isEmpty()) {
315                     FlowBasedServicesUtils.removeBoundServicesState(tx, interfaceName, serviceMode);
316                 }
317                 EVENT_LOGGER.debug("IFM-ServiceBindingConfig, UNBIND {}, {}", interfaceName,
318                         boundServicesNew.getServiceName());
319                 flowBasedServicesConfigRemovable.unbindService(futures, interfaceName, boundServicesNew,
320                         boundServicesList, boundServiceState);
321             }));
322             return futures;
323         }
324     }
325 }