Changes related to the Reconciliation Framework are done in this patch.
The APIs needed to be implemented by the services registering to the
Reconciliation framework are present in the interface
ReconciliationNotificationListener.The APIs to register the service to
Reconciliation framework and the APIs to start the reconciliation /
cancellation of the ongoing reconciliation of a node are present in
ReconciliationManager.
Change-Id: I8bd1845db8b86f4a4314977e05c6b699592f5658
Signed-off-by: gobinath <gobinath@ericsson.com>
<module>topology-lldp-discovery</module>
<!-- experimental apps -->
<module>bulk-o-matic</module>
+ <module>reconciliation-framework</module>
</modules>
</project>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.opendaylight.openflowplugin</groupId>
+ <artifactId>applications</artifactId>
+ <version>0.5.0-SNAPSHOT</version>
+ </parent>
+ <groupId>org.opendaylight.openflowplugin.applications</groupId>
+ <artifactId>reconciliation-framework</artifactId>
+ <packaging>bundle</packaging>
+
+ <dependencies>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-binding-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.openflowplugin</groupId>
+ <artifactId>openflowplugin-common</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.openflowplugin</groupId>
+ <artifactId>openflowplugin-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-common</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-common-util</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.mdsal</groupId>
+ <artifactId>mdsal-singleton-common-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.openflowplugin.model</groupId>
+ <artifactId>model-flow-service</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-core</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-binding-broker-impl</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-binding-broker-impl</artifactId>
+ <scope>test</scope>
+ <type>test-jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.karaf.shell</groupId>
+ <artifactId>org.apache.karaf.shell.console</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ </plugin>
+ <plugin>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-maven-plugin</artifactId>
+ </plugin>
+ </plugins>
+ </build>
+
+
+ <scm>
+ <connection>scm:git:ssh://git.opendaylight.org:29418/openflowplugin.git</connection>
+ <developerConnection>scm:git:ssh://git.opendaylight.org:29418/openflowplugin.git</developerConnection>
+ <url>https://wiki.opendaylight.org/view/OpenDaylight_OpenFlow_Plugin:Main</url>
+ <tag>HEAD</tag>
+ </scm>
+
+</project>
--- /dev/null
+/*
+ * Copyright (c) 2017 Ericsson India Global Services Pvt Ltd. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.openflowplugin.applications.reconciliation;
+
+/*
+ * Interface to be returned after services registers itself with reconciliation framework
+ * Services call close() on NotificationRegistration for unregister
+ */
+public interface NotificationRegistration extends AutoCloseable {
+}
--- /dev/null
+/*
+ * Copyright (c) 2017 Ericsson India Global Services Pvt Ltd. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.openflowplugin.applications.reconciliation;
+
+import java.util.List;
+import java.util.Map;
+
+/*
+ * Provider to register the service to reconciliation framework
+ *
+ */
+public interface ReconciliationManager {
+ /*
+ * Application who are interested in reconciliation should use this API to
+ * register their services to the Reconciliation Framework
+ *
+ * @param object - the reference to the ReconciliationNotificationListener
+ */
+ NotificationRegistration registerService(ReconciliationNotificationListener service);
+
+ /*
+ * API exposed by RF for get list of registered services
+ *
+ * @return the Map containing registered services with priority as Key
+ */
+ Map<Integer, List<ReconciliationNotificationListener>> getRegisteredServices();
+}
--- /dev/null
+/*
+ * Copyright (c) 2017 Ericsson India Global Services Pvt Ltd. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.openflowplugin.applications.reconciliation;
+
+import com.google.common.util.concurrent.ListenableFuture;
+import java.util.concurrent.Future;
+import org.opendaylight.openflowplugin.api.openflow.device.DeviceInfo;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.openflowplugin.rf.state.rev170713.ResultState;
+
+/*
+ * Provider to start the reconciliation of a node
+ * Provider to end the reconciliation of a node
+ */
+public interface ReconciliationNotificationListener extends AutoCloseable {
+ /*
+ * This method will be a callback from RF to start the application
+ * reconciliation. If the reconciliation fails for the services with a priority, the reconciliation of the node for
+ * other services with lower priority has to be carried out or cancelled depending on the resultstate decided by
+ * the services so that it is "fail fast" and doesn't block. This enhancement would be done later and currently the
+ * reconciliation for all the services is carried out irrespective of the intent.
+ *
+ * @param nodeId - Node InstanceIdentifier
+ *
+ * @return the nodeId
+ */
+ ListenableFuture<Boolean> startReconciliation(DeviceInfo node);
+
+ /*
+ * This method will be a callback from RF when dpn disconnects during
+ * reconciliation
+ *
+ * @param nodeId - Node InstanceIdentifier
+ *
+ * @return the nodeId
+ */
+ ListenableFuture<Boolean> endReconciliation(DeviceInfo node);
+
+ /*
+ * Priority of the application. The priority should be a set of values from which the user should be able to select
+ * for the service instead of allowing any random values for priority from the user. Enhancement to be made to
+ * restrict the user to choose from among a set of priorities by making this enum. Also enhancement to be made to
+ * add the service reconciliation property as a configurable property.
+ *
+ * @return the priority of the service
+ */
+ int getPriority();
+
+ /*
+ * Name of the application
+ *
+ * @return the name of the service
+ */
+ String getName();
+
+ /*
+ * Application's intent when the application's reconciliation fails
+ *
+ * @return the intent of the service if the reconciliation fails
+ */
+ ResultState getResultState();
+}
--- /dev/null
+/*
+ * Copyright (c) 2017 Ericsson India Global Services Pvt Ltd. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.openflowplugin.applications.reconciliation.cli;
+
+import com.google.common.base.Preconditions;
+import java.util.List;
+import org.apache.karaf.shell.commands.Command;
+import org.apache.karaf.shell.console.OsgiCommandSupport;
+import org.opendaylight.openflowplugin.applications.reconciliation.ReconciliationManager;
+import org.opendaylight.openflowplugin.applications.reconciliation.ReconciliationNotificationListener;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@Command(scope = "reconciliation", name = "getRegisteredServices", description = "displaying services registered to "
+ + "Reconciliation Framework")
+/*
+ * CLI to display the service priority, service name and service status TODO
+ * service status
+ */
+public class GetRegisteredServices extends OsgiCommandSupport {
+ private static final Logger LOG = LoggerFactory.getLogger(GetRegisteredServices.class);
+ public static final String CLI_FORMAT = "%d %-20s ";
+
+ private ReconciliationManager reconciliationManager;
+
+ public void setReconciliationManager(ReconciliationManager reconciliationManager) {
+ this.reconciliationManager = Preconditions.checkNotNull(reconciliationManager,
+ "ReconciliationManager can not be null!");
+ }
+
+ @Override
+ protected Object doExecute() throws Exception {
+ LOG.debug("Executing getRegisteredServices to Reconciliation Framework command");
+ if (reconciliationManager.getRegisteredServices().isEmpty()) {
+ session.getConsole().println("No Services have registered to Reconciliation Framework");
+ } else {
+ for (List<ReconciliationNotificationListener> services : reconciliationManager.getRegisteredServices()
+ .values()) {
+ for (ReconciliationNotificationListener service : services) {
+ session.getConsole().println(String.format(CLI_FORMAT, service.getPriority(), service.getName()));
+ }
+ }
+ }
+
+ return null;
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2017 Ericsson India Global Services Pvt Ltd. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.openflowplugin.applications.reconciliation.impl;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentSkipListMap;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.stream.Collectors;
+import javax.annotation.Nonnull;
+import org.opendaylight.openflowplugin.api.openflow.device.DeviceInfo;
+import org.opendaylight.openflowplugin.api.openflow.mastership.MastershipChangeException;
+import org.opendaylight.openflowplugin.api.openflow.mastership.MastershipChangeServiceManager;
+import org.opendaylight.openflowplugin.api.openflow.mastership.ReconciliationFrameworkEvent;
+import org.opendaylight.openflowplugin.applications.reconciliation.NotificationRegistration;
+import org.opendaylight.openflowplugin.applications.reconciliation.ReconciliationManager;
+import org.opendaylight.openflowplugin.applications.reconciliation.ReconciliationNotificationListener;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.openflowplugin.rf.state.rev170713.ResultState;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ReconciliationManagerImpl implements ReconciliationManager, ReconciliationFrameworkEvent {
+ private static final Logger LOG = LoggerFactory.getLogger(ReconciliationManagerImpl.class);
+
+ private MastershipChangeServiceManager mastershipChangeServiceManager;
+ private Map<Integer, List<ReconciliationNotificationListener>> registeredServices = new ConcurrentSkipListMap<>();
+ private Map<DeviceInfo, ListenableFuture<ResultState>> futureMap = new ConcurrentHashMap<>();
+ private Map<ResultState,Integer> resultStateMap = new ConcurrentHashMap<>();
+ private AtomicReference<ResultState> decidedResultState = new AtomicReference<>(ResultState.DONOTHING);
+
+ public ReconciliationManagerImpl(MastershipChangeServiceManager mastershipChangeServiceManager) {
+ this.mastershipChangeServiceManager = Preconditions.checkNotNull(mastershipChangeServiceManager,
+ "MastershipChangeServiceManager can not be null!");
+ }
+
+ public void start() throws MastershipChangeException {
+ mastershipChangeServiceManager.reconciliationFrameworkRegistration(this);
+ LOG.info("ReconciliationManager has started successfully.");
+ }
+
+ @Override
+ public NotificationRegistration registerService(ReconciliationNotificationListener reconciliationTask) {
+ LOG.debug("Registered service {} with priority {} and intent {}", reconciliationTask.getName(),
+ reconciliationTask.getPriority(), reconciliationTask.getResultState());
+ registeredServices.computeIfAbsent(reconciliationTask.getPriority(), services -> new ArrayList<>())
+ .add(reconciliationTask);
+ ReconciliationServiceDelegate registration = new ReconciliationServiceDelegate(reconciliationTask, () -> {
+ LOG.debug("Service un-registered from Reconciliation framework {}", reconciliationTask.getName());
+ registeredServices.computeIfPresent(reconciliationTask.getPriority(), (priority, services) -> services)
+ .remove(reconciliationTask);
+ decideResultState(reconciliationTask.getResultState());
+ });
+ decideResultState(reconciliationTask.getResultState());
+ return registration;
+ }
+
+ private void decideResultState(ResultState resultState) {
+ Integer count = resultStateMap.get(resultState);
+ resultStateMap.put(resultState, count = (count == null ? 1 : count+1));
+ Map.Entry<ResultState,Integer> maxEntry = null;
+ for(Map.Entry<ResultState,Integer> entry : resultStateMap.entrySet()) {
+ if (maxEntry == null || entry.getValue() > maxEntry.getValue()) {
+ maxEntry = entry;
+ }
+ }
+ decidedResultState.set(maxEntry.getKey());
+ }
+
+ @Override
+ public Map<Integer, List<ReconciliationNotificationListener>> getRegisteredServices() {
+ ImmutableMap.Builder<Integer, List<ReconciliationNotificationListener>> builder = ImmutableMap.builder();
+ builder.putAll(registeredServices);
+ return builder.build();
+ }
+
+ @Override
+ public void close() throws Exception {
+ }
+
+ @Override
+ public ListenableFuture<ResultState> onDevicePrepared(@Nonnull DeviceInfo node) {
+ LOG.debug("Triggering reconciliation for node : {}", node.getNodeId());
+ return futureMap.computeIfAbsent(node, value -> reconcileNode(node));
+ }
+
+ @Override
+ public ListenableFuture<Void> onDeviceDisconnected(@Nonnull DeviceInfo node) {
+ LOG.info("Stopping reconciliation for node {}", node.getNodeId());
+ if (futureMap.containsKey(node)) {
+ return cancelNodeReconciliation(node);
+ }
+ return Futures.immediateFuture(null);
+ }
+
+ ListenableFuture<ResultState> reconcileNode(DeviceInfo node) {
+ ListenableFuture<ResultState> lastFuture = Futures.immediateFuture(null);
+ for (List<ReconciliationNotificationListener> services : registeredServices.values()) {
+ lastFuture = reconcileServices(lastFuture, services, node);
+ }
+ return lastFuture;
+ }
+
+ ListenableFuture<ResultState> reconcileServices(ListenableFuture<ResultState> prevFuture,
+ List<ReconciliationNotificationListener> servicesForPriority, DeviceInfo node) {
+ return Futures.transformAsync(prevFuture, prevResult -> {
+ return Futures.transform(Futures.allAsList(servicesForPriority.stream().
+ map(service -> service.startReconciliation(node)).collect(Collectors.toList())),
+ results->decidedResultState.get());
+ });
+ }
+
+ ListenableFuture<Void> cancelNodeReconciliation(DeviceInfo node) {
+ ListenableFuture<Void> lastFuture = Futures.immediateFuture(null);
+ futureMap.get(node).cancel(true);
+ futureMap.remove(node);
+ for (List<ReconciliationNotificationListener> services : registeredServices.values()) {
+ lastFuture = cancelServiceReconciliation(lastFuture, services, node);
+ }
+ return lastFuture;
+ }
+
+ ListenableFuture<Void> cancelServiceReconciliation(ListenableFuture<Void> prevFuture,
+ List<ReconciliationNotificationListener> servicesForPriority, DeviceInfo node) {
+ return Futures.transformAsync(prevFuture, prevResult -> {
+ return Futures.transform(Futures.allAsList(servicesForPriority.stream().
+ map(service -> service.endReconciliation(node)).collect(Collectors.toList())),
+ results->null);
+ });
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2017 Ericsson India Global Services Pvt Ltd. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.openflowplugin.applications.reconciliation.impl;
+
+import com.google.common.base.Preconditions;
+import org.opendaylight.openflowplugin.applications.reconciliation.NotificationRegistration;
+import org.opendaylight.openflowplugin.applications.reconciliation.ReconciliationNotificationListener;
+
+public class ReconciliationServiceDelegate implements NotificationRegistration {
+
+ private ReconciliationNotificationListener reconciliationNotificationListener;
+ private AutoCloseable unregisterService;
+
+ public ReconciliationServiceDelegate( ReconciliationNotificationListener reconciliationNotificationListener,
+ AutoCloseable unregisterService) {
+ this.reconciliationNotificationListener = Preconditions.checkNotNull(reconciliationNotificationListener,
+ "ReconciliationNotificationListener can not be null!");
+ this.unregisterService = unregisterService;
+ }
+
+ @Override
+ public void close() throws Exception {
+ this.unregisterService.close();
+ this.reconciliationNotificationListener.close();
+ }
+}
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
+ xmlns:odl="http://opendaylight.org/xmlns/blueprint/v1.0.0"
+ odl:use-default-for-reference-types="true">
+
+ <reference id="reconciliationManagerRef"
+ interface="org.opendaylight.openflowplugin.applications.reconciliation.ReconciliationManager"
+ availability="optional"/>
+ <command-bundle xmlns="http://karaf.apache.org/xmlns/shell/v1.1.0">
+ <command>
+ <action class="org.opendaylight.openflowplugin.applications.reconciliation.cli.GetRegisteredServices">
+ <property name="reconciliationManager"
+ ref="reconciliationManagerRef"/>
+ </action>
+ </command>
+ </command-bundle>
+
+</blueprint>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
+ xmlns:cm="http://aries.apache.org/blueprint/xmlns/blueprint-cm/v1.1.0"
+ xmlns:odl="http://opendaylight.org/xmlns/blueprint/v1.0.0"
+ odl:use-default-for-reference-types="true">
+
+ <reference id="mastershipChangeServiceManager"
+ interface="org.opendaylight.openflowplugin.api.openflow.mastership.MastershipChangeServiceManager"/>
+ <bean id="reconciliationManagerImpl"
+ class="org.opendaylight.openflowplugin.applications.reconciliation.impl.ReconciliationManagerImpl"
+ init-method="start"
+ destroy-method="close">
+ <argument ref="mastershipChangeServiceManager"/>
+ </bean>
+ <service ref="reconciliationManagerImpl"
+ interface="org.opendaylight.openflowplugin.applications.reconciliation.ReconciliationManager"
+ odl:type="default"/>
+
+</blueprint>
\ No newline at end of file
and is available at http://www.eclipse.org/legal/epl-v10.html
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
-
<modelVersion>4.0.0</modelVersion>
<parent>
<type>xml</type>
<version>${project.version}</version>
</dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>odl-openflowplugin-app-reconciliation-framework</artifactId>
+ <classifier>features</classifier>
+ <type>xml</type>
+ <version>${project.version}</version>
+ </dependency>
</dependencies>
</dependencyManagement>
</project>
<type>xml</type>
<classifier>features</classifier>
</dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>odl-openflowplugin-app-reconciliation-framework</artifactId>
+ <version>${project.version}</version>
+ <type>xml</type>
+ <classifier>features</classifier>
+ </dependency>
</dependencies>
</project>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+<modelVersion>4.0.0</modelVersion>
+<parent>
+ <groupId>org.opendaylight.odlparent</groupId>
+ <artifactId>single-feature-parent</artifactId>
+ <version>2.0.0</version>
+ <relativePath/>
+</parent>
+
+<groupId>org.opendaylight.openflowplugin</groupId>
+<artifactId>odl-openflowplugin-app-reconciliation-framework</artifactId>
+<packaging>feature</packaging>
+<version>0.5.0-SNAPSHOT</version>
+
+<name>OpenDaylight :: Openflow Plugin :: Application - Reconciliation Framework</name>
+
+<dependencies>
+ <!-- feature dependencies -->
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>odl-openflowplugin-southbound</artifactId>
+ <version>${project.version}</version>
+ <classifier>features</classifier>
+ <type>xml</type>
+ </dependency>
+
+ <dependency>
+ <groupId>org.opendaylight.openflowplugin.applications</groupId>
+ <artifactId>reconciliation-framework</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
+</dependencies>
+
+</project>
<classifier>features</classifier>
<type>xml</type>
</dependency>
+ <dependency>
+ <groupId>org.opendaylight.openflowplugin</groupId>
+ <artifactId>odl-openflowplugin-app-reconciliation-framework</artifactId>
+ <version>${project.version}</version>
+ <classifier>features</classifier>
+ <type>xml</type>
+ </dependency>
</dependencies>
</project>
<module>odl-openflowplugin-app-notifications</module>
<module>odl-openflowplugin-app-forwardingrules-manager</module>
<module>odl-openflowplugin-app-forwardingrules-sync</module>
+ <module>odl-openflowplugin-app-reconciliation-framework</module>
</modules>
</project>