2 * Copyright (c) 2016, 2017 Ericsson India Global Services Pvt Ltd. and others. All rights reserved.
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
9 package org.opendaylight.genius.interfacemanager.servicebindings.flowbased.listeners;
11 import static org.opendaylight.genius.infra.Datastore.OPERATIONAL;
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;
59 public class FlowBasedServicesConfigListener implements ClusteredDataTreeChangeListener<ServicesInfo>,
62 private static final Logger LOG = LoggerFactory.getLogger(FlowBasedServicesConfigListener.class);
63 private static final Logger EVENT_LOGGER = LoggerFactory.getLogger("GeniusEventLogger");
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;
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;
89 serviceRecoveryRegistry.addRecoverableListener(interfaceServiceRecoveryHandler.buildServiceRegistryKey(),
93 protected InstanceIdentifier<ServicesInfo> getWildCardPath() {
94 return InstanceIdentifier.create(ServiceBindings.class).child(ServicesInfo.class);
98 public void registerListener() {
99 registerListener(LogicalDatastoreType.CONFIGURATION, dataBroker);
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);
108 public void deregisterListener() {
113 public void close() {
114 if (listenerRegistration != null) {
116 listenerRegistration.close();
118 listenerRegistration = null;
124 public void onDataTreeChanged(@NonNull final Collection<DataTreeModification<ServicesInfo>> collection) {
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()))
137 private InstanceIdentifier<BoundServices> getBoundServicesInstanceIdentifier(
138 final InstanceIdentifier<ServicesInfo> rootIdentifier, final BoundServicesKey boundServicesKey) {
139 return rootIdentifier.child(BoundServices.class , boundServicesKey);
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());
150 final ServicesInfoKey servicesInfoKey = rootNode.getDataAfter().key();
151 final BoundServices boundServicesBefore = dataObjectModification.getDataBefore();
152 final BoundServices boundServicesAfter = dataObjectModification.getDataAfter();
154 switch (dataObjectModification.getModificationType()) {
156 if (boundServicesBefore != null) {
157 remove(servicesInfoKey, boundServicesBefore, boundServices);
160 case SUBTREE_MODIFIED:
161 if (boundServicesBefore != null) {
162 update(servicesInfoKey, getBoundServicesInstanceIdentifier(rootIdentifier,
163 boundServicesBefore.key()), boundServicesBefore, boundServicesAfter, boundServices);
167 if (boundServicesBefore == null) {
168 add(servicesInfoKey, boundServicesAfter, boundServices);
170 update(servicesInfoKey, getBoundServicesInstanceIdentifier(rootIdentifier,
171 boundServicesBefore.key()), boundServicesBefore, boundServicesAfter, boundServices);
175 LOG.error("Unhandled Modificiation Type{} for {}", dataObjectModification.getModificationType(),
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)) {
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(),
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);
200 protected void update(ServicesInfoKey serviceKey, InstanceIdentifier<BoundServices> key,
201 BoundServices boundServiceOld, BoundServices boundServiceNew,
202 List<BoundServices> boundServicesList) {
203 if (!Objects.equals(boundServiceOld, boundServiceNew)) {
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.
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);
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);
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)) {
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(),
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);
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;
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;
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: {}",
275 boundServicesState = FlowBasedServicesUtils.buildBoundServicesState(ifState, serviceMode);
276 FlowBasedServicesUtils.addBoundServicesState(tx, interfaceName, boundServicesState);
278 flowBasedServicesAddable.bindService(futures, interfaceName, boundServicesNew, boundServicesList,
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;
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;
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());
315 if (boundServicesList == null || boundServicesList.isEmpty()) {
316 FlowBasedServicesUtils.removeBoundServicesState(tx, interfaceName, serviceMode);
318 EVENT_LOGGER.debug("IFM-ServiceBindingConfig, UNBIND {}, {}", interfaceName,
319 boundServicesNew.getServiceName());
320 flowBasedServicesConfigRemovable.unbindService(futures, interfaceName, boundServicesNew,
321 boundServicesList, boundServiceState);