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