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
8 package org.opendaylight.genius.interfacemanager.servicebindings.flowbased.listeners;
10 import static org.opendaylight.mdsal.binding.util.Datastore.OPERATIONAL;
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;
58 public class FlowBasedServicesConfigListener implements ClusteredDataTreeChangeListener<ServicesInfo>,
61 private static final Logger LOG = LoggerFactory.getLogger(FlowBasedServicesConfigListener.class);
62 private static final Logger EVENT_LOGGER = LoggerFactory.getLogger("GeniusEventLogger");
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;
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;
88 serviceRecoveryRegistry.addRecoverableListener(interfaceServiceRecoveryHandler.buildServiceRegistryKey(),
92 protected InstanceIdentifier<ServicesInfo> getWildCardPath() {
93 return InstanceIdentifier.create(ServiceBindings.class).child(ServicesInfo.class);
97 public void registerListener() {
98 registerListener(LogicalDatastoreType.CONFIGURATION, dataBroker);
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);
107 public void deregisterListener() {
112 public void close() {
113 if (listenerRegistration != null) {
115 listenerRegistration.close();
117 listenerRegistration = null;
123 public void onDataTreeChanged(@NonNull final Collection<DataTreeModification<ServicesInfo>> collection) {
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()))
136 private InstanceIdentifier<BoundServices> getBoundServicesInstanceIdentifier(
137 final InstanceIdentifier<ServicesInfo> rootIdentifier, final BoundServicesKey boundServicesKey) {
138 return rootIdentifier.child(BoundServices.class , boundServicesKey);
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());
149 final ServicesInfoKey servicesInfoKey = rootNode.getDataAfter().key();
150 final BoundServices boundServicesBefore = dataObjectModification.getDataBefore();
151 final BoundServices boundServicesAfter = dataObjectModification.getDataAfter();
153 switch (dataObjectModification.getModificationType()) {
155 if (boundServicesBefore != null) {
156 remove(servicesInfoKey, boundServicesBefore, boundServices);
159 case SUBTREE_MODIFIED:
160 if (boundServicesBefore != null) {
161 update(servicesInfoKey, getBoundServicesInstanceIdentifier(rootIdentifier,
162 boundServicesBefore.key()), boundServicesBefore, boundServicesAfter, boundServices);
166 if (boundServicesBefore == null) {
167 add(servicesInfoKey, boundServicesAfter, boundServices);
169 update(servicesInfoKey, getBoundServicesInstanceIdentifier(rootIdentifier,
170 boundServicesBefore.key()), boundServicesBefore, boundServicesAfter, boundServices);
174 LOG.error("Unhandled Modificiation Type{} for {}", dataObjectModification.getModificationType(),
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)) {
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(),
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);
199 protected void update(ServicesInfoKey serviceKey, InstanceIdentifier<BoundServices> key,
200 BoundServices boundServiceOld, BoundServices boundServiceNew,
201 List<BoundServices> boundServicesList) {
202 if (!Objects.equals(boundServiceOld, boundServiceNew)) {
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.
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);
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);
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)) {
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(),
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);
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;
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;
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: {}",
274 boundServicesState = FlowBasedServicesUtils.buildBoundServicesState(ifState, serviceMode);
275 FlowBasedServicesUtils.addBoundServicesState(tx, interfaceName, boundServicesState);
277 flowBasedServicesAddable.bindService(futures, interfaceName, boundServicesNew, boundServicesList,
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;
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;
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());
314 if (boundServicesList == null || boundServicesList.isEmpty()) {
315 FlowBasedServicesUtils.removeBoundServicesState(tx, interfaceName, serviceMode);
317 EVENT_LOGGER.debug("IFM-ServiceBindingConfig, UNBIND {}, {}", interfaceName,
318 boundServicesNew.getServiceName());
319 flowBasedServicesConfigRemovable.unbindService(futures, interfaceName, boundServicesNew,
320 boundServicesList, boundServiceState);