RoleManager,SalRoleService Impl and Test 73/28973/4
authorKamal Rameshan <kramesha@cisco.com>
Sun, 30 Aug 2015 00:03:50 +0000 (17:03 -0700)
committerKamal Rameshan <kramesha@cisco.com>
Sat, 7 Nov 2015 01:15:59 +0000 (17:15 -0800)
1. Created a Role manager and RoleContext which would get invoked as part of the device init phase
2. Introduced dependency to EntityOwnership service , to get the ownership (implemented in controller)
4. RoleManager requests ownership from EntityOwnership and on notification makes a role change call SalRoleService
5. SalRoleService makes a role change to the device
6. Tests

Change-Id: Ie443d519be985b867d5c6f9ce8473fbd6cdf4dca
Signed-off-by: Kamal Rameshan <kramesha@cisco.com>
Signed-off-by: Moiz Raja <moraja@cisco.com>
35 files changed:
features-li/src/main/features/features.xml
openflowplugin-api/pom.xml
openflowplugin-api/src/main/java/org/opendaylight/openflowplugin/api/openflow/OpenFlowPluginProvider.java
openflowplugin-api/src/main/java/org/opendaylight/openflowplugin/api/openflow/device/DeviceContext.java
openflowplugin-api/src/main/java/org/opendaylight/openflowplugin/api/openflow/device/DeviceState.java
openflowplugin-api/src/main/java/org/opendaylight/openflowplugin/api/openflow/role/RoleChangeListener.java [new file with mode: 0644]
openflowplugin-api/src/main/java/org/opendaylight/openflowplugin/api/openflow/role/RoleContext.java [new file with mode: 0644]
openflowplugin-api/src/main/java/org/opendaylight/openflowplugin/api/openflow/role/RoleManager.java [new file with mode: 0644]
openflowplugin-api/src/main/java/org/opendaylight/openflowplugin/api/openflow/rpc/RpcManager.java
openflowplugin-controller-config/src/main/resources/initial/42-openflowplugin-new.xml
openflowplugin-impl/pom.xml
openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/OpenFlowPluginProviderImpl.java
openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/device/DeviceContextImpl.java
openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/device/DeviceManagerImpl.java
openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/device/DeviceStateImpl.java
openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/device/TransactionChainManager.java
openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/role/OpenflowOwnershipListener.java [new file with mode: 0644]
openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/role/RoleChangeException.java [new file with mode: 0644]
openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/role/RoleContextImpl.java [new file with mode: 0644]
openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/role/RoleManagerImpl.java [new file with mode: 0644]
openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/rpc/RpcManagerImpl.java
openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/services/RoleService.java [new file with mode: 0644]
openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/services/SalRoleServiceImpl.java
openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/statistics/StatisticsManagerImpl.java
openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/util/MdSalRegistratorUtils.java
openflowplugin-impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/config/openflow/plugin/impl/rev150327/OpenFlowProviderModule.java
openflowplugin-impl/src/main/yang/openflow-plugin-impl.yang
openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/common/NodeConnectorTranslatorUtilTest.java
openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/device/DeviceContextImplTest.java
openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/device/DeviceManagerImplTest.java
openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/role/RoleContextImplTest.java [new file with mode: 0644]
openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/rpc/RpcContextImplTest.java
openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/rpc/RpcManagerImplTest.java
openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/services/SalRoleServiceImplTest.java [new file with mode: 0644]
openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/util/MdSalRegistratorUtilsTest.java

index 9b2615676b36d1a8902298e6ffa69827350a1d2f..613c91b3239e513dd34875cd10d6248576335ee8 100644 (file)
         <feature version="${project.version}">odl-openflowplugin-nsf-services-li</feature>
         <feature version="${openflowjava.version}">odl-openflowjava-protocol</feature>
         <bundle>mvn:org.opendaylight.openflowplugin/openflowplugin-common/{{VERSION}}</bundle>
+        <bundle>mvn:org.opendaylight.openflowplugin/openflowplugin-api/{{VERSION}}</bundle>
         <!-- TODO : remove dependency on openflowplugin in the future -->
         <bundle>mvn:org.opendaylight.openflowplugin/openflowplugin/{{VERSION}}</bundle>
         <bundle>mvn:org.opendaylight.openflowplugin/openflowplugin-impl/{{VERSION}}</bundle>
-        <bundle>mvn:org.opendaylight.openflowplugin/openflowplugin-api/{{VERSION}}</bundle>
         <bundle>mvn:org.opendaylight.openflowplugin/openflowplugin-extension-api/{{VERSION}}</bundle>
        <bundle>mvn:org.opendaylight.controller/liblldp/{{VERSION}}</bundle>
         <configfile finalname="etc/opendaylight/karaf/42-openflowplugin-Li.xml">mvn:org.opendaylight.openflowplugin/openflowplugin-controller-config/{{VERSION}}/xml/config-Li</configfile>
index 76353c531e6f6ca31aca53e51945db0f9fc550b2..884f1bce4ed7e5190e7f5dcab590e6f1c6f19b18 100644 (file)
             <groupId>org.opendaylight.openflowjava</groupId>
             <artifactId>openflow-protocol-spi</artifactId>
         </dependency>
+        <dependency>
+            <groupId>org.opendaylight.controller</groupId>
+            <artifactId>sal-common-api</artifactId>
+        </dependency>
     </dependencies>
 
 </project>
index b22dbe3f760116ad019e8037e620fd0320eb0331..db725f20e5cb3ee699b8b870a25f47c39a407256 100644 (file)
@@ -13,6 +13,7 @@ import org.opendaylight.controller.md.sal.binding.api.BindingService;
 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
 import org.opendaylight.controller.md.sal.binding.api.NotificationPublishService;
 import org.opendaylight.controller.md.sal.binding.api.NotificationService;
+import org.opendaylight.controller.md.sal.common.api.clustering.EntityOwnershipService;
 import org.opendaylight.controller.sal.binding.api.RpcProviderRegistry;
 import org.opendaylight.openflowjava.protocol.spi.connection.SwitchConnectionProvider;
 
@@ -57,4 +58,6 @@ public interface OpenFlowPluginProvider extends AutoCloseable, BindingService {
 
     void setIsStatisticsPollingOff(final boolean isStatisticsPollingOff);
 
+    void setEntityOwnershipService(EntityOwnershipService entityOwnershipService);
+
     }
index 6eb8b322a89c7db5dc7981b3e1985926cd55e542..740ba139d29bb86a402b4589fef9f376ee16e53b 100644 (file)
@@ -26,6 +26,7 @@ import org.opendaylight.openflowplugin.api.openflow.registry.ItemLifeCycleRegist
 import org.opendaylight.openflowplugin.api.openflow.registry.flow.DeviceFlowRegistry;
 import org.opendaylight.openflowplugin.api.openflow.registry.group.DeviceGroupRegistry;
 import org.opendaylight.openflowplugin.api.openflow.registry.meter.DeviceMeterRegistry;
+import org.opendaylight.openflowplugin.api.openflow.rpc.RpcContext;
 import org.opendaylight.openflowplugin.api.openflow.statistics.ofpspecific.MessageSpy;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.MultipartReply;
 import org.opendaylight.yangtools.yang.binding.DataObject;
@@ -190,5 +191,14 @@ public interface DeviceContext extends AutoCloseable,
      * @return registry point for item life cycle sources of device
      */
     ItemLifeCycleRegistry getItemLifeCycleSourceRegistry();
+
+    void setRpcContext(RpcContext rpcContext);
+
+    RpcContext getRpcContext();
+
+    /**
+     * Callback when confirmed that device is disconnected from cluster
+      */
+    void onDeviceDisconnectedFromCluster();
 }
 
index 6698575d05438067dbd5de1be8407bc674447001..4db91c184bb6043b34a39d0677301115694a46f3 100644 (file)
@@ -12,6 +12,7 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.GetFeaturesOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.role.service.rev150727.OfpRole;
 import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier;
 
 /**
@@ -122,5 +123,9 @@ public interface DeviceState {
 
     void setDeviceSynchronized(boolean deviceSynchronized);
 
+    void setRole(OfpRole ofpRole);
+
+    OfpRole getRole();
+
 
 }
diff --git a/openflowplugin-api/src/main/java/org/opendaylight/openflowplugin/api/openflow/role/RoleChangeListener.java b/openflowplugin-api/src/main/java/org/opendaylight/openflowplugin/api/openflow/role/RoleChangeListener.java
new file mode 100644 (file)
index 0000000..2248ccf
--- /dev/null
@@ -0,0 +1,28 @@
+/**
+ * Copyright (c) 2015 Cisco Systems, Inc. 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.api.openflow.role;
+
+import org.opendaylight.controller.md.sal.common.api.clustering.Entity;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.role.service.rev150727.OfpRole;
+
+/**
+ * Created by kramesha on 9/19/15.
+ */
+public interface RoleChangeListener extends AutoCloseable {
+    /**
+     * Gets called by the EntityOwnershipCandidate after role change received from EntityOwnershipService
+     * @param oldRole
+     * @param newRole
+     */
+    void onRoleChanged(OfpRole oldRole, OfpRole newRole);
+
+    Entity getEntity();
+
+    void onDeviceDisconnectedFromCluster();
+
+}
diff --git a/openflowplugin-api/src/main/java/org/opendaylight/openflowplugin/api/openflow/role/RoleContext.java b/openflowplugin-api/src/main/java/org/opendaylight/openflowplugin/api/openflow/role/RoleContext.java
new file mode 100644 (file)
index 0000000..102a7e6
--- /dev/null
@@ -0,0 +1,21 @@
+/**
+ * Copyright (c) 2015 Cisco Systems, Inc. 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.api.openflow.role;
+
+import com.google.common.util.concurrent.FutureCallback;
+import org.opendaylight.openflowplugin.api.openflow.device.RequestContextStack;
+import org.opendaylight.openflowplugin.api.openflow.device.handlers.DeviceContextClosedHandler;
+
+/**
+ * Created by kramesha on 9/12/15.
+ */
+public interface RoleContext extends RoleChangeListener, DeviceContextClosedHandler, RequestContextStack {
+
+    void facilitateRoleChange(FutureCallback<Boolean> futureCallback);
+
+}
diff --git a/openflowplugin-api/src/main/java/org/opendaylight/openflowplugin/api/openflow/role/RoleManager.java b/openflowplugin-api/src/main/java/org/opendaylight/openflowplugin/api/openflow/role/RoleManager.java
new file mode 100644 (file)
index 0000000..72b6767
--- /dev/null
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. 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.api.openflow.role;
+
+import org.opendaylight.openflowplugin.api.openflow.device.handlers.DeviceInitializationPhaseHandler;
+import org.opendaylight.openflowplugin.api.openflow.device.handlers.DeviceInitializator;
+
+/**
+ * Created by kramesha on 8/31/15.
+ */
+public interface RoleManager extends DeviceInitializator, DeviceInitializationPhaseHandler, AutoCloseable {
+    public static final String ENTITY_TYPE = "openflow";
+}
index 1ff35e17f6c9df5420dec1644797a5329d1c52af..b4d0d5ad28e4825e98c048386088ff37a66b0519 100644 (file)
@@ -18,6 +18,6 @@ import org.opendaylight.openflowplugin.api.openflow.device.handlers.DeviceInitia
  * <p>
  * Created by Martin Bobak &lt;mbobak@cisco.com&gt; on 25.2.2015.
  */
-public interface RpcManager extends DeviceInitializator, DeviceInitializationPhaseHandler {
+public interface RpcManager extends DeviceInitializator, DeviceInitializationPhaseHandler, AutoCloseable {
 
 }
index b041fc3b1562a7aa85345d62a69bc3530b0e4a81..d81a392ded5a8d5161340acb63b3b4ec6636092c 100644 (file)
@@ -82,6 +82,10 @@ and is available at http://www.eclipse.org/legal/epl-v10.html
                         <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">binding:binding-new-notification-publish-service</type>
                         <name>binding-notification-publish-adapter</name>
                     </notification-publish-adapter>
+                    <entity-ownership-service>
+                        <type xmlns:entity-ownership="urn:opendaylight:params:xml:ns:yang:controller:md:sal:core:spi:entity-ownership-service">entity-ownership:entity-ownership-service</type>
+                        <name>entity-ownership-service</name>
+                    </entity-ownership-service>
                     <rpc-requests-quota>20000</rpc-requests-quota>
                     <switch-features-mandatory>false</switch-features-mandatory>
                     <global-notification-quota>64000</global-notification-quota>
index 4e5abeba75690b8e054f1516e698f858e46963f2..e423d826f4cf361bc68063dca74b7079fa28aa92 100644 (file)
             <groupId>org.opendaylight.openflowjava</groupId>
             <artifactId>openflowjava-util</artifactId>
         </dependency>
-
+        <dependency>
+            <groupId>org.opendaylight.controller</groupId>
+            <artifactId>sal-common-api</artifactId>
+        </dependency>
     </dependencies>
 </project>
 
index b8fb10f80c51072a9040dffb9f343bfd46e32668..646a5b78d8524929aaceed64132f24904b96de61 100644 (file)
@@ -26,11 +26,13 @@ import javax.management.ObjectName;
 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
 import org.opendaylight.controller.md.sal.binding.api.NotificationPublishService;
 import org.opendaylight.controller.md.sal.binding.api.NotificationService;
+import org.opendaylight.controller.md.sal.common.api.clustering.EntityOwnershipService;
 import org.opendaylight.controller.sal.binding.api.RpcProviderRegistry;
 import org.opendaylight.openflowjava.protocol.spi.connection.SwitchConnectionProvider;
 import org.opendaylight.openflowplugin.api.openflow.OpenFlowPluginProvider;
 import org.opendaylight.openflowplugin.api.openflow.connection.ConnectionManager;
 import org.opendaylight.openflowplugin.api.openflow.device.DeviceManager;
+import org.opendaylight.openflowplugin.api.openflow.role.RoleManager;
 import org.opendaylight.openflowplugin.api.openflow.rpc.RpcManager;
 import org.opendaylight.openflowplugin.api.openflow.statistics.StatisticsManager;
 import org.opendaylight.openflowplugin.api.openflow.statistics.ofpspecific.MessageIntelligenceAgency;
@@ -38,6 +40,7 @@ import org.opendaylight.openflowplugin.extension.api.ExtensionConverterRegistrat
 import org.opendaylight.openflowplugin.extension.api.OpenFlowPluginExtensionRegistratorProvider;
 import org.opendaylight.openflowplugin.impl.connection.ConnectionManagerImpl;
 import org.opendaylight.openflowplugin.impl.device.DeviceManagerImpl;
+import org.opendaylight.openflowplugin.impl.role.RoleManagerImpl;
 import org.opendaylight.openflowplugin.impl.rpc.RpcManagerImpl;
 import org.opendaylight.openflowplugin.impl.statistics.StatisticsManagerImpl;
 import org.opendaylight.openflowplugin.impl.statistics.ofpspecific.MessageIntelligenceAgencyImpl;
@@ -60,12 +63,14 @@ public class OpenFlowPluginProviderImpl implements OpenFlowPluginProvider, OpenF
     private final int rpcRequestsQuota;
     private final long globalNotificationQuota;
     private DeviceManager deviceManager;
+    private RoleManager roleManager;
     private RpcManager rpcManager;
     private RpcProviderRegistry rpcProviderRegistry;
     private StatisticsManager statisticsManager;
     private ConnectionManager connectionManager;
     private NotificationService notificationProviderService;
     private NotificationPublishService notificationPublishService;
+    private EntityOwnershipService entityOwnershipService;
 
     private ExtensionConverterManager extensionConverterManager;
 
@@ -117,6 +122,11 @@ public class OpenFlowPluginProviderImpl implements OpenFlowPluginProvider, OpenF
         return switchFeaturesMandatory;
     }
 
+    @Override
+    public void setEntityOwnershipService(EntityOwnershipService entityOwnershipService) {
+        this.entityOwnershipService = entityOwnershipService;
+    }
+
     public void setSwitchFeaturesMandatory(final boolean switchFeaturesMandatory) {
         this.switchFeaturesMandatory = switchFeaturesMandatory;
     }
@@ -157,16 +167,20 @@ public class OpenFlowPluginProviderImpl implements OpenFlowPluginProvider, OpenF
         registerMXBean(messageIntelligenceAgency);
 
         deviceManager = new DeviceManagerImpl(dataBroker, messageIntelligenceAgency, switchFeaturesMandatory, globalNotificationQuota);
+        roleManager = new RoleManagerImpl(rpcProviderRegistry, entityOwnershipService);
         statisticsManager = new StatisticsManagerImpl(rpcProviderRegistry, isStatisticsPollingOff);
         rpcManager = new RpcManagerImpl(rpcProviderRegistry, rpcRequestsQuota);
 
+        // CM -> DM -> Role -> SM -> RPC -> DM
         connectionManager.setDeviceConnectedHandler(deviceManager);
-        deviceManager.setDeviceInitializationPhaseHandler(statisticsManager);
-        deviceManager.setNotificationService(this.notificationProviderService);
-        deviceManager.setNotificationPublishService(this.notificationPublishService);
+        deviceManager.setDeviceInitializationPhaseHandler(roleManager);
+        roleManager.setDeviceInitializationPhaseHandler(statisticsManager);
         statisticsManager.setDeviceInitializationPhaseHandler(rpcManager);
         rpcManager.setDeviceInitializationPhaseHandler(deviceManager);
 
+        deviceManager.setNotificationService(this.notificationProviderService);
+        deviceManager.setNotificationPublishService(this.notificationPublishService);
+
         TranslatorLibraryUtil.setBasicTranslatorLibrary(deviceManager);
         deviceManager.initialize();
 
@@ -207,5 +221,8 @@ public class OpenFlowPluginProviderImpl implements OpenFlowPluginProvider, OpenF
     @Override
     public void close() throws Exception {
         //TODO: close all contexts, switchConnections (, managers)
+        rpcManager.close();
+        statisticsManager.close();
+        roleManager.close();
     }
 }
index b539535a6b23f6776cbeaaf8930e17632972a1b3..dbc9b54f1a9255a819aaeb6776e96474f16c83a1 100644 (file)
@@ -48,6 +48,7 @@ import org.opendaylight.openflowplugin.api.openflow.registry.flow.FlowRegistryKe
 import org.opendaylight.openflowplugin.api.openflow.registry.group.DeviceGroupRegistry;
 import org.opendaylight.openflowplugin.api.openflow.registry.meter.DeviceMeterRegistry;
 import org.opendaylight.openflowplugin.api.openflow.rpc.ItemLifeCycleKeeper;
+import org.opendaylight.openflowplugin.api.openflow.rpc.RpcContext;
 import org.opendaylight.openflowplugin.api.openflow.rpc.listener.ItemLifecycleListener;
 import org.opendaylight.openflowplugin.api.openflow.statistics.ofpspecific.MessageSpy;
 import org.opendaylight.openflowplugin.impl.common.ItemLifeCycleSourceImpl;
@@ -125,7 +126,7 @@ public class DeviceContextImpl implements DeviceContext {
     private final TranslatorLibrary translatorLibrary;
     private Map<Long, NodeConnectorRef> nodeConnectorCache;
     private ItemLifeCycleRegistry itemLifeCycleSourceRegistry;
-
+    private RpcContext rpcContext;
 
     @VisibleForTesting
     DeviceContextImpl(@Nonnull final ConnectionContext primaryConnectionContext,
@@ -175,6 +176,13 @@ public class DeviceContextImpl implements DeviceContext {
         transactionChainManager.initialSubmitWriteTransaction();
     }
 
+    /**
+     * This method is called fron
+     */
+    void cancelTransaction() {
+        transactionChainManager.cancelWriteTransaction();
+    }
+
     @Override
     public Long getReservedXid() {
         return outboundQueueProvider.reserveEntry();
@@ -403,6 +411,13 @@ public class DeviceContextImpl implements DeviceContext {
             deviceContextClosedHandler.onDeviceContextClosed(this);
         }
 
+        LOG.info("Closing transaction chain manager without cleaning inventory operational");
+        transactionChainManager.closeWithoutCleanup();
+    }
+
+    @Override
+    public void onDeviceDisconnectedFromCluster() {
+        LOG.info("Removing device from operational and closing transaction Manager for device:{}", getDeviceState().getNodeId());
         transactionChainManager.close();
     }
 
@@ -487,4 +502,16 @@ public class DeviceContextImpl implements DeviceContext {
     public ItemLifeCycleRegistry getItemLifeCycleSourceRegistry() {
         return itemLifeCycleSourceRegistry;
     }
+
+    @Override
+    public void setRpcContext(RpcContext rpcContext) {
+        this.rpcContext = rpcContext;
+    }
+
+    @Override
+    public RpcContext getRpcContext() {
+        return rpcContext;
+    }
+
+
 }
index b0b8ef61105587f817612d2e7013b3d93a211392..569c00a82853e4aa190c3a155ce1b74fb4b16dd6 100644 (file)
@@ -99,6 +99,7 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731
 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.multipart.reply.multipart.reply.body.multipart.reply.table.features._case.MultipartReplyTableFeatures;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.port.statistics.rev131214.FlowCapableNodeConnectorStatisticsData;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.port.statistics.rev131214.FlowCapableNodeConnectorStatisticsDataBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.role.service.rev150727.OfpRole;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.table.types.rev131026.table.features.TableFeatures;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 import org.opendaylight.yangtools.yang.common.RpcError;
@@ -170,8 +171,14 @@ public class DeviceManagerImpl implements DeviceManager, AutoCloseable {
         // final phase - we have to add new Device to MD-SAL DataStore
         Preconditions.checkNotNull(deviceContext);
         try {
-            ((DeviceContextImpl) deviceContext).initialSubmitTransaction();
-            deviceContext.onPublished();
+
+            if (deviceContext.getDeviceState().getRole() != OfpRole.BECOMESLAVE) {
+                ((DeviceContextImpl) deviceContext).initialSubmitTransaction();
+                deviceContext.onPublished();
+            } else {
+                ((DeviceContextImpl) deviceContext).cancelTransaction();
+            }
+
         } catch (final Exception e) {
             LOG.warn("Node {} can not be add to OPERATIONAL DataStore yet because {} ", deviceContext.getDeviceState().getNodeId(), e.getMessage());
             LOG.trace("Problem with add node {} to OPERATIONAL DataStore", deviceContext.getDeviceState().getNodeId(), e);
@@ -191,17 +198,20 @@ public class DeviceManagerImpl implements DeviceManager, AutoCloseable {
         DeviceTransactionChainManagerProvider.TransactionChainManagerRegistration transactionChainManagerRegistration = deviceTransactionChainManagerProvider.provideTransactionChainManager(connectionContext);
         TransactionChainManager transactionChainManager = transactionChainManagerRegistration.getTransactionChainManager();
 
-        //this actually is new registration for currently processed connection context
         if (transactionChainManagerRegistration.ownedByInvokingConnectionContext()) {
+            //this actually is new registration for currently processed connection context
             initializeDeviceContext(connectionContext, transactionChainManager);
         }
-        //this means there already exists connection described by same NodeId and it is not current connection contexts' registration
         else if (TransactionChainManager.TransactionChainManagerStatus.WORKING.equals(transactionChainManager.getTransactionChainManagerStatus())) {
+            //this means there already exists connection described by same NodeId and it is not current connection contexts' registration
+            LOG.info("In deviceConnected, ownedByInvokingConnectionContext is false and  TransactionChainManagerStatus.WORKING. Closing connection to device to start again.");
             connectionContext.closeConnection(false);
         }
-        //previous connection is shutting down, we will try to register handler listening on new transaction chain ready
         else if (!transactionChainManager.attemptToRegisterHandler(readyForNewTransactionChainHandler)) {
+            //previous connection is shutting down, we will try to register handler listening on new transaction chain ready
             // new connection wil be closed if handler registration fails
+            LOG.info("In deviceConnected, ownedByInvokingConnectionContext is false, TransactionChainManagerStatus is not shutting down or readyForNewTransactionChainHandler is null. " +
+                    "Closing connection to device to start again.");
             connectionContext.closeConnection(false);
         }
     }
@@ -319,8 +329,8 @@ public class DeviceManagerImpl implements DeviceManager, AutoCloseable {
 
     void deviceCtxLevelUp(final DeviceContext deviceContext) {
         deviceContext.getDeviceState().setValid(true);
-        deviceInitPhaseHandler.onDeviceContextLevelUp(deviceContext);
         LOG.trace("Device context level up called.");
+        deviceInitPhaseHandler.onDeviceContextLevelUp(deviceContext);
     }
 
     static void chainTableTrunkWriteOF10(final DeviceContext deviceContext, final ListenableFuture<List<RpcResult<List<MultipartReply>>>> deviceFeaturesFuture) {
index 28803566206472d8a058b730e75c7b4ad1115444..0a69374b5807040517c59797f2c05281baf65b0c 100644 (file)
@@ -19,6 +19,7 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.N
 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.FeaturesReply;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.GetFeaturesOutput;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.GetFeaturesOutputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.role.service.rev150727.OfpRole;
 import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier;
 
 /**
@@ -46,6 +47,7 @@ class DeviceStateImpl implements DeviceState {
     private boolean tableStatisticsAvailable;
     private boolean portStatisticsAvailable;
     private boolean queueStatisticsAvailable;
+    private volatile OfpRole role;
 
     public DeviceStateImpl(@CheckForNull final FeaturesReply featuresReply, @Nonnull final NodeId nodeId) {
         Preconditions.checkArgument(featuresReply != null);
@@ -156,4 +158,13 @@ class DeviceStateImpl implements DeviceState {
         deviceSynchronized = _deviceSynchronized;
     }
 
+    @Override
+    public OfpRole getRole() {
+        return role;
+    }
+
+    @Override
+    public void setRole(OfpRole role) {
+        this.role = role;
+    }
 }
index 4706cc1df8d789a24605306c3a32c3563d5fc6eb..9bd51876c7ce3678e8ef3c3441acffff421428b2 100644 (file)
@@ -128,6 +128,12 @@ class TransactionChainManager implements TransactionChainListener, AutoCloseable
         return true;
     }
 
+    public void cancelWriteTransaction() {
+        // there is no cancel txn in ping-pong broker. So we need to drop the chain and recreate it.
+        // since the chain is created per device, there won't be any other txns other than ones we created.
+        recreateTxChain();
+    }
+
     <T extends DataObject> void addDeleteOperationTotTxChain(final LogicalDatastoreType store,
                                                              final InstanceIdentifier<T> path) {
         final WriteTransaction writeTx = getTransactionSafely();
@@ -160,6 +166,7 @@ class TransactionChainManager implements TransactionChainListener, AutoCloseable
         }
     }
 
+
     private WriteTransaction getTransactionSafely() {
         if (wTx == null && !TransactionChainManagerStatus.SHUTTING_DOWN.equals(transactionChainManagerStatus)) {
             synchronized (txLock) {
@@ -204,6 +211,10 @@ class TransactionChainManager implements TransactionChainListener, AutoCloseable
     }
 
     private void notifyReadyForNewTransactionChainAndCloseFactory() {
+        if(managerRegistration == null){
+            LOG.warn("managerRegistration is null");
+            return;
+        }
         synchronized (this) {
             try {
                 LOG.debug("Closing registration in manager.");
@@ -220,6 +231,15 @@ class TransactionChainManager implements TransactionChainListener, AutoCloseable
         LOG.debug("Transaction chain factory closed.");
     }
 
+    public void closeWithoutCleanup() {
+        LOG.debug("closing txChainManager without cleanup of node {} from operational DS.", nodeII);
+        synchronized (txLock) {
+            this.transactionChainManagerStatus = TransactionChainManagerStatus.SHUTTING_DOWN;
+            notifyReadyForNewTransactionChainAndCloseFactory();
+            wTx = null;
+        }
+    }
+
     public enum TransactionChainManagerStatus {
         WORKING, SHUTTING_DOWN;
     }
diff --git a/openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/role/OpenflowOwnershipListener.java b/openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/role/OpenflowOwnershipListener.java
new file mode 100644 (file)
index 0000000..9d38a06
--- /dev/null
@@ -0,0 +1,122 @@
+/**
+ * Copyright (c) 2015 Cisco Systems, Inc. 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.impl.role;
+
+import java.util.Map;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import org.opendaylight.controller.md.sal.common.api.clustering.Entity;
+import org.opendaylight.controller.md.sal.common.api.clustering.EntityOwnershipChange;
+import org.opendaylight.controller.md.sal.common.api.clustering.EntityOwnershipListener;
+import org.opendaylight.controller.md.sal.common.api.clustering.EntityOwnershipListenerRegistration;
+import org.opendaylight.controller.md.sal.common.api.clustering.EntityOwnershipService;
+import org.opendaylight.openflowplugin.api.openflow.role.RoleChangeListener;
+import org.opendaylight.openflowplugin.api.openflow.role.RoleManager;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.role.service.rev150727.OfpRole;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Created by kramesha on 9/14/15.
+ */
+public class OpenflowOwnershipListener implements EntityOwnershipListener, AutoCloseable {
+
+    private static final Logger LOG = LoggerFactory.getLogger(OpenflowOwnershipListener.class);
+
+    private EntityOwnershipService entityOwnershipService;
+    private EntityOwnershipListenerRegistration entityOwnershipListenerRegistration;
+    private Map<Entity, RoleChangeListener> roleChangeListenerMap = new ConcurrentHashMap<>();
+    private final Map<Entity, Boolean> ownershipMap = new ConcurrentHashMap<>();
+    private final ExecutorService roleChangeExecutor = Executors.newSingleThreadExecutor();
+
+    public OpenflowOwnershipListener(EntityOwnershipService entityOwnershipService) {
+        this.entityOwnershipService = entityOwnershipService;
+    }
+
+    public void init() {
+        entityOwnershipListenerRegistration = entityOwnershipService.registerListener(RoleManager.ENTITY_TYPE, this);
+    }
+
+    @Override
+    public void ownershipChanged(EntityOwnershipChange ownershipChange) {
+        LOG.debug("Received EntityOwnershipChange:{}", ownershipChange);
+
+        RoleChangeListener roleChangeListener = roleChangeListenerMap.get(ownershipChange.getEntity());
+
+        ownershipMap.put(ownershipChange.getEntity(), ownershipChange.isOwner());
+
+        if (roleChangeListener != null) {
+            LOG.debug("Found local entity:{}", ownershipChange.getEntity());
+
+            // if this was the master and entity does not have a master
+            if (ownershipChange.wasOwner() && !ownershipChange.isOwner() && !ownershipChange.hasOwner()) {
+                // possible the last node to be disconnected from device.
+                // eligible for the device to get deleted from inventory.
+                LOG.debug("Initiate removal from operational. Possibly the last node to be disconnected for :{}. ", ownershipChange);
+                ownershipMap.remove(ownershipChange.getEntity());
+                roleChangeListener.onDeviceDisconnectedFromCluster();
+
+            } else {
+                OfpRole newRole = ownershipChange.isOwner() ? OfpRole.BECOMEMASTER : OfpRole.BECOMESLAVE;
+                OfpRole oldRole = ownershipChange.wasOwner() ? OfpRole.BECOMEMASTER : OfpRole.BECOMESLAVE;
+                // send even if they are same. we do the check for duplicates in SalRoleService and maintain a lastKnownRole
+                roleChangeListener.onRoleChanged(oldRole, newRole);
+            }
+        }
+    }
+
+    public void registerRoleChangeListener(final RoleChangeListener roleChangeListener) {
+        roleChangeListenerMap.put(roleChangeListener.getEntity(), roleChangeListener);
+
+        final Entity entity = roleChangeListener.getEntity();
+        final OpenflowOwnershipListener self = this;
+
+        if(this.hasOwner(entity)) {
+            LOG.debug("An owner exist for entity {}", entity);
+            roleChangeExecutor.submit(new Callable<Object>() {
+                @Override
+                public Object call() throws Exception {
+                    if(self.isOwner(entity)){
+                        LOG.debug("Ownership is here for entity {} becoming master", entity);
+                        roleChangeListener.onRoleChanged(OfpRole.BECOMEMASTER, OfpRole.BECOMEMASTER);
+                    } else {
+                        LOG.debug("Ownership is NOT here for entity {} becoming alave", entity);
+                        roleChangeListener.onRoleChanged(OfpRole.BECOMESLAVE, OfpRole.BECOMESLAVE);
+
+                    }
+
+                    return null;
+                }
+            });
+        }
+    }
+
+    @Override
+    public void close() throws Exception {
+        if (entityOwnershipListenerRegistration != null) {
+            entityOwnershipListenerRegistration.close();
+        }
+    }
+
+    private boolean hasOwner(Entity entity){
+        return ownershipMap.containsKey(entity);
+    }
+
+    private boolean isOwner(Entity entity){
+        if(hasOwner(entity)){
+            Boolean isOwner = ownershipMap.get(entity);
+            if(isOwner != null) {
+                return isOwner.booleanValue();
+            }
+        }
+
+        return false;
+    }
+}
diff --git a/openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/role/RoleChangeException.java b/openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/role/RoleChangeException.java
new file mode 100644 (file)
index 0000000..1ddcffe
--- /dev/null
@@ -0,0 +1,32 @@
+/**
+ * Copyright (c) 2015 Cisco Systems, Inc. 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.impl.role;
+
+/**
+ * Created by kramesha on 8/21/15.
+ */
+public class RoleChangeException extends Exception {
+    private static final long serialVersionUID = -615991366447313972L;
+
+    /**
+     * default ctor
+     *
+     * @param message
+     */
+    public RoleChangeException(String message) {
+        super(message);
+    }
+
+    /**
+     * @param message
+     * @param cause
+     */
+    public RoleChangeException(String message, Throwable cause) {
+        super(message, cause);
+    }
+}
diff --git a/openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/role/RoleContextImpl.java b/openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/role/RoleContextImpl.java
new file mode 100644 (file)
index 0000000..8920933
--- /dev/null
@@ -0,0 +1,179 @@
+/**
+ * Copyright (c) 2015 Cisco Systems, Inc. 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.impl.role;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.JdkFutureAdapters;
+import java.util.concurrent.Future;
+import javax.annotation.Nullable;
+import org.opendaylight.controller.md.sal.common.api.clustering.CandidateAlreadyRegisteredException;
+import org.opendaylight.controller.md.sal.common.api.clustering.Entity;
+import org.opendaylight.controller.md.sal.common.api.clustering.EntityOwnershipCandidateRegistration;
+import org.opendaylight.controller.md.sal.common.api.clustering.EntityOwnershipService;
+import org.opendaylight.controller.sal.binding.api.RpcProviderRegistry;
+import org.opendaylight.openflowplugin.api.openflow.connection.ConnectionContext;
+import org.opendaylight.openflowplugin.api.openflow.device.DeviceContext;
+import org.opendaylight.openflowplugin.api.openflow.device.RequestContext;
+import org.opendaylight.openflowplugin.api.openflow.role.RoleContext;
+import org.opendaylight.openflowplugin.api.openflow.role.RoleManager;
+import org.opendaylight.openflowplugin.impl.rpc.AbstractRequestContext;
+import org.opendaylight.openflowplugin.impl.services.SalRoleServiceImpl;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeRef;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.role.service.rev150727.OfpRole;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.role.service.rev150727.SalRoleService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.role.service.rev150727.SetRoleInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.role.service.rev150727.SetRoleInputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.role.service.rev150727.SetRoleOutput;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Created by kramesha on 9/12/15.
+ */
+public class RoleContextImpl implements RoleContext {
+    private static final Logger LOG = LoggerFactory.getLogger(RoleContextImpl.class);
+
+    private EntityOwnershipService entityOwnershipService;
+    private EntityOwnershipCandidateRegistration entityOwnershipCandidateRegistration;
+    private final RpcProviderRegistry rpcProviderRegistry;
+    private DeviceContext deviceContext;
+    private Entity entity;
+    private OpenflowOwnershipListener openflowOwnershipListener;
+    private SalRoleService salRoleService;
+    private FutureCallback<Boolean> roleChangeCallback;
+
+
+    public RoleContextImpl(DeviceContext deviceContext, RpcProviderRegistry rpcProviderRegistry,
+                           EntityOwnershipService entityOwnershipService, OpenflowOwnershipListener openflowOwnershipListener) {
+        this.entityOwnershipService = entityOwnershipService;
+        this.rpcProviderRegistry = rpcProviderRegistry;
+        this.deviceContext = deviceContext;
+        entity = new Entity(RoleManager.ENTITY_TYPE, deviceContext.getPrimaryConnectionContext().getNodeId().getValue());
+
+        this.openflowOwnershipListener =  openflowOwnershipListener;
+        salRoleService = new SalRoleServiceImpl(this, deviceContext);
+
+        //make a call to entity ownership service and listen for notifications from the service
+        requestOpenflowEntityOwnership();
+    }
+
+    @Override
+    public void facilitateRoleChange(FutureCallback<Boolean> roleChangeCallback) {
+        this.roleChangeCallback = roleChangeCallback;
+        if (!isDeviceConnected()) {
+            throw new IllegalStateException(
+                    "Device is disconnected. Giving up on Role Change:" + deviceContext.getDeviceState().getNodeId());
+        }
+    }
+
+    private void requestOpenflowEntityOwnership() {
+
+        LOG.debug("requestOpenflowEntityOwnership for entity {}", entity);
+        try {
+            entityOwnershipCandidateRegistration = entityOwnershipService.registerCandidate(entity);
+
+            // The role change listener must be registered after registering a candidate
+            openflowOwnershipListener.registerRoleChangeListener(this);
+            LOG.info("RoleContextImpl : Candidate registered with ownership service for device :{}", deviceContext.getPrimaryConnectionContext().getNodeId().getValue());
+        } catch (CandidateAlreadyRegisteredException e) {
+            // we can log and move for this error, as listener is present and role changes will be served.
+            LOG.error("Candidate - Entity already registered with Openflow candidate ", entity, e );
+        }
+    }
+
+    @Override
+    public void onRoleChanged(final OfpRole oldRole, final OfpRole newRole) {
+
+        // called notification thread from md-sal
+
+        LOG.debug("Role change received from ownership listener from {} to {} for device:{}", oldRole, newRole,
+                deviceContext.getPrimaryConnectionContext().getNodeId());
+
+        final SetRoleInput setRoleInput = (new SetRoleInputBuilder())
+                .setControllerRole(newRole)
+                .setNode(new NodeRef(deviceContext.getDeviceState().getNodeInstanceIdentifier()))
+                .build();
+
+        Future<RpcResult<SetRoleOutput>> setRoleOutputFuture = salRoleService.setRole(setRoleInput);
+
+        Futures.addCallback(JdkFutureAdapters.listenInPoolThread(setRoleOutputFuture), new FutureCallback<RpcResult<SetRoleOutput>>() {
+            @Override
+            public void onSuccess(RpcResult<SetRoleOutput> setRoleOutputRpcResult) {
+                LOG.debug("Rolechange {} successful made on switch :{}", newRole,
+                        deviceContext.getPrimaryConnectionContext().getNodeId());
+                deviceContext.getDeviceState().setRole(newRole);
+                if (roleChangeCallback != null) {
+                    roleChangeCallback.onSuccess(true);
+                }
+            }
+
+            @Override
+            public void onFailure(Throwable throwable) {
+                LOG.error("Error in setRole {} for device {} ", newRole,
+                        deviceContext.getPrimaryConnectionContext().getNodeId(), throwable);
+                if (roleChangeCallback != null) {
+                    roleChangeCallback.onFailure(throwable);
+                }
+            }
+        });
+    }
+
+    @Override
+    public void close() throws Exception {
+        if (entityOwnershipCandidateRegistration != null) {
+            LOG.debug("Closing EntityOwnershipCandidateRegistration for {}", entity);
+            LOG.error("Who called this close?????", new Throwable());
+            entityOwnershipCandidateRegistration.close();
+        }
+    }
+
+    @Override
+    public void onDeviceContextClosed(DeviceContext deviceContext) {
+        try {
+            LOG.debug("onDeviceContextClosed called");
+            this.close();
+        } catch (Exception e) {
+            LOG.error("Exception in onDeviceContextClosed of RoleContext", e);
+        }
+    }
+
+    @Override
+    public Entity getEntity() {
+        return entity;
+    }
+
+    @Override
+    public void onDeviceDisconnectedFromCluster() {
+        LOG.debug("Called onDeviceDisconnectedFromCluster in DeviceContext for entity:{}", entity);
+        deviceContext.onDeviceDisconnectedFromCluster();
+    }
+
+    private boolean isDeviceConnected() {
+        return ConnectionContext.CONNECTION_STATE.WORKING.equals(
+                deviceContext.getPrimaryConnectionContext().getConnectionState());
+    }
+
+    @Nullable
+    @Override
+    public <T> RequestContext<T> createRequestContext() {
+        final AbstractRequestContext<T> ret = new AbstractRequestContext<T>(deviceContext.getReservedXid()) {
+            @Override
+            public void close() {
+            }
+        };
+        return ret;
+    }
+
+    @VisibleForTesting
+    public void setSalRoleService(SalRoleService salRoleService) {
+        this.salRoleService = salRoleService;
+    }
+}
diff --git a/openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/role/RoleManagerImpl.java b/openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/role/RoleManagerImpl.java
new file mode 100644 (file)
index 0000000..3092943
--- /dev/null
@@ -0,0 +1,96 @@
+/**
+ * Copyright (c) 2015 Cisco Systems, Inc. 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.impl.role;
+
+import com.google.common.util.concurrent.FutureCallback;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import javax.annotation.CheckForNull;
+import org.opendaylight.controller.md.sal.common.api.clustering.EntityOwnershipService;
+import org.opendaylight.controller.sal.binding.api.RpcProviderRegistry;
+import org.opendaylight.openflowplugin.api.OFConstants;
+import org.opendaylight.openflowplugin.api.openflow.device.DeviceContext;
+import org.opendaylight.openflowplugin.api.openflow.device.handlers.DeviceInitializationPhaseHandler;
+import org.opendaylight.openflowplugin.api.openflow.role.RoleContext;
+import org.opendaylight.openflowplugin.api.openflow.role.RoleManager;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Gets invoked from RpcManagerInitial, registers a candidate with EntityOwnershipService.
+ * On receipt of the ownership notification, makes an rpc call to SalRoleSevice.
+ *
+ * Hands over to StatisticsManager at the end.
+ */
+public class RoleManagerImpl implements RoleManager {
+    private static final Logger LOG = LoggerFactory.getLogger(RoleManagerImpl.class);
+
+    private DeviceInitializationPhaseHandler deviceInitializationPhaseHandler;
+    private EntityOwnershipService entityOwnershipService;
+    private final RpcProviderRegistry rpcProviderRegistry;
+    private final ConcurrentHashMap<DeviceContext, RoleContext> contexts = new ConcurrentHashMap<>();
+    private final OpenflowOwnershipListener openflowOwnershipListener;
+
+    public RoleManagerImpl(RpcProviderRegistry rpcProviderRegistry, EntityOwnershipService entityOwnershipService) {
+        this.entityOwnershipService = entityOwnershipService;
+        this.rpcProviderRegistry = rpcProviderRegistry;
+        this.openflowOwnershipListener = new OpenflowOwnershipListener(entityOwnershipService);
+        LOG.debug("Registering OpenflowOwnershipListener listening to all entity ownership changes");
+        openflowOwnershipListener.init();
+    }
+
+    @Override
+    public void setDeviceInitializationPhaseHandler(DeviceInitializationPhaseHandler handler) {
+        deviceInitializationPhaseHandler = handler;
+    }
+
+    @Override
+    public void onDeviceContextLevelUp(@CheckForNull final DeviceContext deviceContext) {
+        LOG.debug("RoleManager called for device:{}", deviceContext.getPrimaryConnectionContext().getNodeId());
+        if (deviceContext.getDeviceState().getFeatures().getVersion() < OFConstants.OFP_VERSION_1_3) {
+            // Roles are not supported before OF1.3, so move forward.
+            deviceInitializationPhaseHandler.onDeviceContextLevelUp(deviceContext);
+        }
+
+        RoleContext roleContext = new RoleContextImpl(deviceContext, rpcProviderRegistry, entityOwnershipService, openflowOwnershipListener);
+        contexts.put(deviceContext, roleContext);
+        LOG.debug("Created role context");
+
+        // if the device context gets closed (mostly on connection close), we would need to cleanup
+        deviceContext.addDeviceContextClosedHandler(roleContext);
+
+        roleContext.facilitateRoleChange(new FutureCallback<Boolean>() {
+            @Override
+            public void onSuccess(Boolean aBoolean) {
+                LOG.debug("roleChangeFuture success for device:{}. Moving to StatisticsManager", deviceContext.getDeviceState().getNodeId());
+                deviceInitializationPhaseHandler.onDeviceContextLevelUp(deviceContext);
+            }
+
+            @Override
+            public void onFailure(Throwable throwable) {
+                LOG.error("RoleChange on device {} was not successful after several attempts. " +
+                        "Closing the device Context, reconnect the device and start over",
+                        deviceContext.getPrimaryConnectionContext().getNodeId().getValue(), throwable);
+//                try {
+//                    deviceContext.close();
+//                } catch (Exception e) {
+//                    LOG.warn("Error closing device context for device:{}",
+//                            deviceContext.getPrimaryConnectionContext().getNodeId().getValue(),  e);
+//                }
+            }
+        });
+    }
+
+    @Override
+    public void close() throws Exception {
+        for (Map.Entry<DeviceContext, RoleContext> roleContextEntry : contexts.entrySet()) {
+            roleContextEntry.getValue().close();
+        }
+        this.openflowOwnershipListener.close();
+    }
+}
index 1b8e5f76b1928febc0537f5f70112d7f97e40f8d..dbc1c5421c0dac41635270c6b288dc9123863783 100644 (file)
@@ -13,9 +13,13 @@ import org.opendaylight.openflowplugin.api.openflow.device.handlers.DeviceInitia
 import org.opendaylight.openflowplugin.api.openflow.rpc.RpcContext;
 import org.opendaylight.openflowplugin.api.openflow.rpc.RpcManager;
 import org.opendaylight.openflowplugin.impl.util.MdSalRegistratorUtils;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.role.service.rev150727.OfpRole;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 public class RpcManagerImpl implements RpcManager {
 
+    private static final Logger LOG = LoggerFactory.getLogger(RpcManagerImpl.class);
     private final RpcProviderRegistry rpcProviderRegistry;
     private DeviceInitializationPhaseHandler deviceInitPhaseHandler;
     private final int maxRequestsQuota;
@@ -33,10 +37,22 @@ public class RpcManagerImpl implements RpcManager {
 
     @Override
     public void onDeviceContextLevelUp(final DeviceContext deviceContext) {
+        LOG.debug("deviceContext.getDeviceState().getRole():"+deviceContext.getDeviceState().getRole());
+        if (deviceContext.getDeviceState().getRole() == OfpRole.BECOMESLAVE) {
+            // if slave, we dont poll for statistics and jump to rpc initialization
+            deviceInitPhaseHandler.onDeviceContextLevelUp(deviceContext);
+            return;
+        }
+
         final RpcContext rpcContext = new RpcContextImpl(deviceContext.getMessageSpy(), rpcProviderRegistry, deviceContext, maxRequestsQuota);
         deviceContext.addDeviceContextClosedHandler(rpcContext);
         MdSalRegistratorUtils.registerServices(rpcContext, deviceContext);
         // finish device initialization cycle back to DeviceManager
         deviceInitPhaseHandler.onDeviceContextLevelUp(deviceContext);
     }
+
+    @Override
+    public void close() throws Exception {
+
+    }
 }
diff --git a/openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/services/RoleService.java b/openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/services/RoleService.java
new file mode 100644 (file)
index 0000000..60637f1
--- /dev/null
@@ -0,0 +1,149 @@
+/**
+ * Copyright (c) 2015 Cisco Systems, Inc. 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.impl.services;
+
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.SettableFuture;
+import java.math.BigInteger;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import org.opendaylight.openflowplugin.api.openflow.device.DeviceContext;
+import org.opendaylight.openflowplugin.api.openflow.device.RequestContextStack;
+import org.opendaylight.openflowplugin.api.openflow.device.Xid;
+import org.opendaylight.openflowplugin.impl.role.RoleChangeException;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.transaction.rev150304.TransactionId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.common.types.rev130731.ControllerRole;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.OfHeader;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.RoleRequestInputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.RoleRequestOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.role.service.rev150727.OfpRole;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.role.service.rev150727.SetRoleOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.role.service.rev150727.SetRoleOutputBuilder;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Created by kramesha on 8/24/15.
+ */
+public class RoleService extends AbstractSimpleService<RoleRequestInputBuilder, RoleRequestOutput> {
+    private static final Logger LOG = LoggerFactory.getLogger(RoleService.class);
+
+    private final DeviceContext deviceContext;
+
+    protected RoleService(final RequestContextStack requestContextStack, final DeviceContext deviceContext, final Class<RoleRequestOutput> clazz) {
+        super(requestContextStack, deviceContext, clazz);
+        this.deviceContext = deviceContext;
+    }
+
+    @Override
+    protected OfHeader buildRequest(Xid xid, RoleRequestInputBuilder input) {
+        input.setXid(xid.getValue());
+        return input.build();
+    }
+
+    public Future<BigInteger> getGenerationIdFromDevice(Short version) throws RoleChangeException {
+        final NodeId nodeId = deviceContext.getPrimaryConnectionContext().getNodeId();
+        LOG.info("getGenerationIdFromDevice called for device:{}", nodeId.getValue());
+
+        // send a dummy no-change role request to get the generation-id of the switch
+        final RoleRequestInputBuilder roleRequestInputBuilder = new RoleRequestInputBuilder();
+        roleRequestInputBuilder.setRole(toOFJavaRole(OfpRole.NOCHANGE));
+        roleRequestInputBuilder.setVersion(version);
+        roleRequestInputBuilder.setGenerationId(BigInteger.ZERO);
+
+        final SettableFuture<BigInteger> finalFuture = SettableFuture.create();
+        ListenableFuture<RpcResult<RoleRequestOutput>> genIdListenableFuture = handleServiceCall(roleRequestInputBuilder);
+        Futures.addCallback(genIdListenableFuture, new FutureCallback<RpcResult<RoleRequestOutput>>() {
+            @Override
+            public void onSuccess(RpcResult<RoleRequestOutput> roleRequestOutputRpcResult) {
+                if (roleRequestOutputRpcResult.isSuccessful()) {
+                    RoleRequestOutput roleRequestOutput = roleRequestOutputRpcResult.getResult();
+                    if (roleRequestOutput != null) {
+                        LOG.debug("roleRequestOutput.getGenerationId()={}", roleRequestOutput.getGenerationId());
+                        finalFuture.set(roleRequestOutput.getGenerationId());
+                    } else {
+                        LOG.info("roleRequestOutput is null in getGenerationIdFromDevice");
+                        finalFuture.setException(new RoleChangeException("Exception in getting generationId for device:" + nodeId.getValue()));
+                    }
+
+                } else {
+                    LOG.error("getGenerationIdFromDevice RPC error " +
+                            roleRequestOutputRpcResult.getErrors().iterator().next().getInfo());
+
+                }
+
+            }
+
+            @Override
+            public void onFailure(Throwable throwable) {
+                LOG.info("onFailure - getGenerationIdFromDevice RPC error {}", throwable);
+                finalFuture.setException(new ExecutionException(throwable));
+            }
+        });
+        return finalFuture;
+    }
+
+
+    public Future<SetRoleOutput> submitRoleChange(final OfpRole ofpRole, final Short version, final BigInteger generationId) {
+        LOG.info("submitRoleChange called for device:{}, role:{}",
+                deviceContext.getPrimaryConnectionContext().getNodeId(), ofpRole);
+        RoleRequestInputBuilder roleRequestInputBuilder = new RoleRequestInputBuilder();
+        roleRequestInputBuilder.setRole(toOFJavaRole(ofpRole));
+        roleRequestInputBuilder.setVersion(version);
+        roleRequestInputBuilder.setGenerationId(generationId);
+
+        ListenableFuture<RpcResult<RoleRequestOutput>> roleListenableFuture = handleServiceCall(roleRequestInputBuilder);
+
+        final SettableFuture<SetRoleOutput> finalFuture = SettableFuture.create();
+        Futures.addCallback(roleListenableFuture, new FutureCallback<RpcResult<RoleRequestOutput>>() {
+            @Override
+            public void onSuccess(RpcResult<RoleRequestOutput> roleRequestOutputRpcResult) {
+                LOG.info("submitRoleChange onSuccess for device:{}, role:{}",
+                        deviceContext.getPrimaryConnectionContext().getNodeId(), ofpRole);
+                RoleRequestOutput roleRequestOutput = roleRequestOutputRpcResult.getResult();
+                SetRoleOutputBuilder setRoleOutputBuilder = new SetRoleOutputBuilder();
+                setRoleOutputBuilder.setTransactionId(new TransactionId(BigInteger.valueOf(roleRequestOutput.getXid())));
+               finalFuture.set(setRoleOutputBuilder.build());
+            }
+
+            @Override
+            public void onFailure(Throwable throwable) {
+                LOG.error("submitRoleChange onFailure for device:{}, role:{}",
+                        deviceContext.getPrimaryConnectionContext().getNodeId(), ofpRole, throwable);
+                finalFuture.set(null);
+            }
+        });
+        return finalFuture;
+    }
+
+    private static ControllerRole toOFJavaRole(OfpRole role) {
+        ControllerRole ofJavaRole = null;
+        switch (role) {
+            case BECOMEMASTER:
+                ofJavaRole = ControllerRole.OFPCRROLEMASTER;
+                break;
+            case BECOMESLAVE:
+                ofJavaRole = ControllerRole.OFPCRROLESLAVE;
+                break;
+            case NOCHANGE:
+                ofJavaRole = ControllerRole.OFPCRROLENOCHANGE;
+                break;
+            default:
+                // no intention
+                LOG.warn("given role is not supported by protocol roles: {}", role);
+                break;
+        }
+        return ofJavaRole;
+    }
+
+
+}
index c135576c4ce6543a0dc80e1225bc316f1a44894c..2d3c40dbac8f72151f6d76875991fd9c43719b2f 100644 (file)
  */
 package org.opendaylight.openflowplugin.impl.services;
 
+import com.google.common.base.Function;
+import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.common.util.concurrent.MoreExecutors;
+import com.google.common.util.concurrent.SettableFuture;
+import java.math.BigInteger;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executors;
 import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicReference;
+import org.opendaylight.openflowplugin.api.openflow.connection.ConnectionContext;
 import org.opendaylight.openflowplugin.api.openflow.device.DeviceContext;
 import org.opendaylight.openflowplugin.api.openflow.device.RequestContextStack;
 import org.opendaylight.openflowplugin.api.openflow.device.Xid;
+import org.opendaylight.openflowplugin.impl.role.RoleChangeException;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.OfHeader;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.RoleRequestOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.role.service.rev150727.OfpRole;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.role.service.rev150727.SalRoleService;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.role.service.rev150727.SetRoleInput;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.role.service.rev150727.SetRoleInputBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.role.service.rev150727.SetRoleOutput;
 import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 
-public class SalRoleServiceImpl extends AbstractSimpleService<SetRoleInputBuilder, SetRoleOutput> implements SalRoleService  {
+public class SalRoleServiceImpl extends AbstractSimpleService<SetRoleInput, SetRoleOutput> implements SalRoleService  {
+
+    private static final Logger LOG = LoggerFactory.getLogger(SalRoleServiceImpl.class);
+
+    private static final BigInteger MAX_GENERATION_ID = new BigInteger("ffffffffffffffff", 16);
+
+    private static final int MAX_RETRIES = 42;
+
+    private final DeviceContext deviceContext;
+    private final RoleService roleService;
+    private final AtomicReference<OfpRole> lastKnownRoleRef = new AtomicReference<>(OfpRole.NOCHANGE);
+    private final ListeningExecutorService listeningExecutorService;
+    private final NodeId nodeId;
+    private final Short version;
 
     public SalRoleServiceImpl(final RequestContextStack requestContextStack, final DeviceContext deviceContext) {
         super(requestContextStack, deviceContext, SetRoleOutput.class);
+        this.deviceContext = deviceContext;
+        this.roleService =  new RoleService(requestContextStack, deviceContext, RoleRequestOutput.class);
+        nodeId = deviceContext.getPrimaryConnectionContext().getNodeId();
+        version = deviceContext.getPrimaryConnectionContext().getFeatures().getVersion();
+        listeningExecutorService = MoreExecutors.listeningDecorator(Executors.newSingleThreadExecutor());
+
     }
 
     @Override
-    protected OfHeader buildRequest(Xid xid, SetRoleInputBuilder input) {
+    protected OfHeader buildRequest(Xid xid, SetRoleInput input) {
         return null;
     }
 
+    public static BigInteger getNextGenerationId(BigInteger generationId) {
+        BigInteger nextGenerationId = null;
+        if (generationId.compareTo(MAX_GENERATION_ID) < 0) {
+            nextGenerationId = generationId.add(BigInteger.ONE);
+        } else {
+            nextGenerationId = BigInteger.ZERO;
+        }
+
+        return nextGenerationId;
+    }
+
+
     @Override
-    public Future<RpcResult<SetRoleOutput>> setRole(SetRoleInput input) {
+    public Future<RpcResult<SetRoleOutput>> setRole(final SetRoleInput input) {
+        LOG.info("SetRole called with input:{}", input);
+        OfpRole lastKnownRole = lastKnownRoleRef.get();
+
+        // compare with last known role and set if different. If they are same, then return.
+        if (lastKnownRoleRef.compareAndSet(input.getControllerRole(), input.getControllerRole())) {
+            LOG.info("Role to be set is same as the last known role for the device:{}. Hence ignoring.", input.getControllerRole());
+            SettableFuture<RpcResult<SetRoleOutput>> resultFuture = SettableFuture.create();
+            resultFuture.set(RpcResultBuilder.<SetRoleOutput>success().build());
+            return resultFuture;
+        }
+
+        final SettableFuture<RpcResult<SetRoleOutput>> resultFuture = SettableFuture.create();
+
+        RoleChangeTask roleChangeTask = new RoleChangeTask(nodeId, input.getControllerRole(), version, roleService);
+
+        do {
+            ListenableFuture<RpcResult<SetRoleOutput>> deviceCheck = deviceConnectionCheck();
+            if (deviceCheck != null) {
+                LOG.info("Device {} is disconnected or state is not valid. Giving up on role change", input.getNode());
+                return deviceCheck;
+            }
+
+            ListenableFuture<SetRoleOutput> taskFuture = listeningExecutorService.submit(roleChangeTask);
+            LOG.info("RoleChangeTask submitted for execution");
+            CheckedFuture<SetRoleOutput, RoleChangeException> taskFutureChecked = makeCheckedFuture(taskFuture);
+            try {
+                SetRoleOutput setRoleOutput = taskFutureChecked.checkedGet(10, TimeUnit.SECONDS);
+                LOG.info("setRoleOutput received after roleChangeTask execution:{}", setRoleOutput);
+                resultFuture.set(RpcResultBuilder.<SetRoleOutput>success().withResult(setRoleOutput).build());
+                lastKnownRoleRef.set(input.getControllerRole());
+                return resultFuture;
+
+            } catch (TimeoutException | RoleChangeException e) {
+                roleChangeTask.incrementRetryCounter();
+                LOG.info("Exception in setRole(), will retry:" + (MAX_RETRIES - roleChangeTask.getRetryCounter()) + " times.", e);
+            }
+
+        } while (roleChangeTask.getRetryCounter() < MAX_RETRIES);
+
+        resultFuture.setException(new RoleChangeException("Set Role failed after " + MAX_RETRIES + "tries on device " + input.getNode().getValue()));
+
+        return resultFuture;
+    }
+
+    private ListenableFuture<RpcResult<SetRoleOutput>> deviceConnectionCheck() {
+        if (!ConnectionContext.CONNECTION_STATE.WORKING.equals(deviceContext.getPrimaryConnectionContext().getConnectionState())) {
+            ListenableFuture<RpcResult<SetRoleOutput>> resultingFuture = SettableFuture.create();
+            switch (deviceContext.getPrimaryConnectionContext().getConnectionState()) {
+                case RIP:
+                    final String errMsg = String.format("Device connection doesn't exist anymore. Primary connection status : %s",
+                            deviceContext.getPrimaryConnectionContext().getConnectionState());
+                    resultingFuture = Futures.immediateFailedFuture(new Throwable(errMsg));
+                    break;
+                default:
+                    resultingFuture = Futures.immediateCheckedFuture(RpcResultBuilder.<SetRoleOutput>failed().build());
+                    break;
+            }
+            return resultingFuture;
+        }
         return null;
     }
+
+    class RoleChangeTask implements Callable<SetRoleOutput> {
+
+        private final NodeId nodeId;
+        private final OfpRole ofpRole;
+        private final Short version;
+        private final RoleService roleService;
+        private int retryCounter = 0;
+
+        public RoleChangeTask(NodeId nodeId, OfpRole ofpRole, Short version, RoleService roleService) {
+            this.nodeId = nodeId;
+            this.ofpRole = ofpRole;
+            this.version = version;
+            this.roleService = roleService;
+        }
+
+        @Override
+        public SetRoleOutput call() throws RoleChangeException {
+            LOG.info("RoleChangeTask called on device:{} OFPRole:{}", this.nodeId.getValue(), ofpRole);
+
+            // we cannot move ahead without having the generation id, so block the thread till we get it.
+            BigInteger generationId = null;
+            SetRoleOutput setRoleOutput = null;
+
+            try {
+                generationId = this.roleService.getGenerationIdFromDevice(version).get(10, TimeUnit.SECONDS);
+                LOG.info("RoleChangeTask, GenerationIdFromDevice from device is {}", generationId);
+
+            } catch (Exception e ) {
+                LOG.info("Exception in getting generationId for device:{}. Ex:{}" + this.nodeId.getValue(), e);
+                throw new RoleChangeException("Exception in getting generationId for device:"+ this.nodeId.getValue(), e);
+            }
+
+
+            LOG.info("GenerationId received from device:{} is {}", nodeId.getValue(), generationId);
+
+            final BigInteger nextGenerationId = getNextGenerationId(generationId);
+
+            LOG.info("nextGenerationId received from device:{} is {}", nodeId.getValue(), nextGenerationId);
+
+            try {
+                setRoleOutput = roleService.submitRoleChange(ofpRole, version, nextGenerationId).get(10 , TimeUnit.SECONDS);
+                LOG.info("setRoleOutput after submitRoleChange:{}", setRoleOutput);
+
+            }  catch (InterruptedException | ExecutionException |  TimeoutException e) {
+                LOG.error("Exception in making role change for device", e);
+                throw new RoleChangeException("Exception in making role change for device:" + nodeId.getValue());
+            }
+
+            return setRoleOutput;
+
+        }
+
+        public void incrementRetryCounter() {
+            this.retryCounter = retryCounter + 1;
+        }
+
+        public int getRetryCounter() {
+            return retryCounter;
+        }
+    }
+
+    public static CheckedFuture<SetRoleOutput, RoleChangeException> makeCheckedFuture(ListenableFuture<SetRoleOutput> rolePushResult) {
+        return Futures.makeChecked(rolePushResult,
+                new Function<Exception, RoleChangeException>() {
+                    @Override
+                    public RoleChangeException apply(Exception input) {
+                        RoleChangeException output = null;
+                        if (input instanceof ExecutionException) {
+                            if (input.getCause() instanceof RoleChangeException) {
+                                output = (RoleChangeException) input.getCause();
+                            }
+                        }
+
+                        if (output == null) {
+                            output = new RoleChangeException(input.getMessage(), input);
+                        }
+
+                        return output;
+                    }
+                });
+    }
 }
index 5f148257ba9bc243fe7eb03bb5f30524c20ddeac..5c98f2b17de35523ef9d47f962876582b93efcda 100644 (file)
@@ -37,6 +37,7 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.openflow
 import org.opendaylight.yangtools.yang.common.RpcError;
 import org.opendaylight.yangtools.yang.common.RpcResult;
 import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.role.service.rev150727.OfpRole;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -80,6 +81,12 @@ public class StatisticsManagerImpl implements StatisticsManager, StatisticsManag
 
     @Override
     public void onDeviceContextLevelUp(final DeviceContext deviceContext) {
+        LOG.debug("deviceContext.getDeviceState().getRole():"+deviceContext.getDeviceState().getRole());
+        if (deviceContext.getDeviceState().getRole() == OfpRole.BECOMESLAVE) {
+            // if slave, we dont poll for statistics and jump to rpc initialization
+            deviceInitPhaseHandler.onDeviceContextLevelUp(deviceContext);
+            return;
+        }
 
         if (null == hashedWheelTimer) {
             LOG.trace("This is first device that delivered timer. Starting statistics polling immediately.");
index 0797245504b61a38cbc223203cf9de92b430dc80..4d3e3e8bf3bd3ccd9b506ad064ab080dca5db1ad 100644 (file)
@@ -16,7 +16,6 @@ import org.opendaylight.openflowplugin.impl.services.SalEchoServiceImpl;
 import org.opendaylight.openflowplugin.impl.services.SalFlowServiceImpl;
 import org.opendaylight.openflowplugin.impl.services.SalGroupServiceImpl;
 import org.opendaylight.openflowplugin.impl.services.SalMeterServiceImpl;
-import org.opendaylight.openflowplugin.impl.services.SalRoleServiceImpl;
 import org.opendaylight.openflowplugin.impl.services.SalTableServiceImpl;
 import org.opendaylight.openflowplugin.impl.statistics.services.OpendaylightFlowStatisticsServiceImpl;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.echo.service.rev150305.SalEchoService;
@@ -27,7 +26,6 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.Sal
 import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.service.rev130918.SalMeterService;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.module.config.rev141015.NodeConfigService;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketProcessingService;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.role.service.rev150727.SalRoleService;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.table.service.rev131026.SalTableService;
 
 public class MdSalRegistratorUtils {
@@ -36,6 +34,7 @@ public class MdSalRegistratorUtils {
         throw new IllegalStateException();
     }
 
+
     public static void registerServices(final RpcContext rpcContext, final DeviceContext deviceContext) {
         rpcContext.registerRpcServiceImplementation(SalFlowService.class, new SalFlowServiceImpl(rpcContext, deviceContext));
         rpcContext.registerRpcServiceImplementation(SalEchoService.class, new SalEchoServiceImpl(rpcContext, deviceContext));
@@ -47,6 +46,5 @@ public class MdSalRegistratorUtils {
         rpcContext.registerRpcServiceImplementation(PacketProcessingService.class, new PacketProcessingServiceImpl(rpcContext, deviceContext));
         rpcContext.registerRpcServiceImplementation(NodeConfigService.class, new NodeConfigServiceImpl(rpcContext, deviceContext));
         rpcContext.registerRpcServiceImplementation(OpendaylightFlowStatisticsService.class, new OpendaylightFlowStatisticsServiceImpl(rpcContext, deviceContext));
-        rpcContext.registerRpcServiceImplementation(SalRoleService.class, new SalRoleServiceImpl(rpcContext, deviceContext));
     }
 }
index 2a967f49a765c065f60e25da5a18a0c85c3218f5..bb09b939a39b81ba13316e4e903028951f9eff98 100644 (file)
@@ -36,6 +36,7 @@ public class OpenFlowProviderModule extends org.opendaylight.yang.gen.v1.urn.ope
         openflowPluginProvider.setNotificationPublishService(getNotificationPublishAdapterDependency());
         openflowPluginProvider.setSwitchFeaturesMandatory(getSwitchFeaturesMandatory());
         openflowPluginProvider.setIsStatisticsPollingOff(getIsStatisticsPollingOff());
+        openflowPluginProvider.setEntityOwnershipService(getEntityOwnershipServiceDependency());
         openflowPluginProvider.initialize();
 
         return openflowPluginProvider;
index ef43dc3fcf3be68b1ccc494341cb5e12f533b72b..1da6cd74de95a8e99da97d7aa7ae7dad7f9d7973 100644 (file)
@@ -10,6 +10,7 @@ module openflow-plugin-provider-impl {
     import opendaylight-md-sal-binding { prefix md-sal-binding; revision-date 2013-10-28;}
     import opendaylight-sal-binding-broker-impl { prefix sal-broker; revision-date 2013-10-28;}
     import openflowplugin-extension-registry {prefix ofp-ext-reg; revision-date 2015-04-25;}
+    import opendaylight-entity-ownership-service { prefix ownership-service; revision-date 2015-08-10;}
 
     description
         "openflow-plugin-impl";
@@ -62,6 +63,14 @@ module openflow-plugin-provider-impl {
                     }
                 }
             }
+            container entity-ownership-service {
+                uses config:service-ref {
+                    refine type {
+                        mandatory true;
+                        config:required-identity ownership-service:entity-ownership-service;
+                    }
+                }
+            }
             list openflow-switch-connection-provider {
                 uses config:service-ref {
                     refine type {
index 3c2a5125729d3a625f75c89cf310a0b804abe148..15b8633b17e62d51fefeb70ece2cb3f492bac78d 100644 (file)
@@ -21,7 +21,12 @@ import org.opendaylight.openflowplugin.api.OFConstants;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.MacAddress;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeConnector;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.common.types.rev130731.PortConfig;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.common.types.rev130731.PortConfigV10;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.common.types.rev130731.PortFeatures;
@@ -30,6 +35,7 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.common.types.rev13
 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.common.types.rev130731.PortStateV10;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.FeaturesReply;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.features.reply.PhyPort;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -300,4 +306,18 @@ public class NodeConnectorTranslatorUtilTest {
         assertEquals(apf.isPause(), npf.isPause());
         assertEquals(apf.isPauseAsym(), npf.isPauseAsym());
     }
+
+    static InstanceIdentifier<NodeConnector> createNodeConnectorId(String nodeKey, String nodeConnectorKey) {
+        return InstanceIdentifier.builder(Nodes.class)
+                .child(Node.class, new NodeKey(new NodeId(nodeKey)))
+                .child(NodeConnector.class, new NodeConnectorKey(new NodeConnectorId(nodeConnectorKey)))
+                .build();
+    }
+
+    @Test
+    public void testDummy() {
+        InstanceIdentifier<NodeConnector> id = createNodeConnectorId("openflow:1", "openflow:1:1");
+        InstanceIdentifier<Node> nodeId = id.firstIdentifierOf(Node.class);
+        System.out.println(nodeId);
+    }
 }
index 7c918a839a5702245dfd0f7b1747f9054dcdb951..98df88ee5c2544aea24c457f66f9bdb5f45b4c82 100644 (file)
@@ -373,7 +373,7 @@ public class DeviceContextImplTest {
         deviceContext.close();
         verify(connectionContext).closeConnection(eq(false));
         verify(deviceState).setValid(eq(false));
-        verify(txChainManager).close();
+        verify(txChainManager).closeWithoutCleanup();
         verify(mockedAuxiliaryConnectionContext).closeConnection(eq(false));
     }
 
@@ -483,6 +483,6 @@ public class DeviceContextImplTest {
         Assert.assertEquals(0, deviceContext.getDeviceGroupRegistry().getAllGroupIds().size());
         Assert.assertEquals(0, deviceContext.getDeviceMeterRegistry().getAllMeterIds().size());
 
-        Mockito.verify(txChainManager).close();
+        Mockito.verify(txChainManager).closeWithoutCleanup();
     }
 }
index 94393a37b2957dd21dc1eea8ef1de9696efafb9a..6d862ae943c19239e1068addd7775b76abdcf470 100644 (file)
@@ -106,6 +106,7 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731
 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.multipart.reply.multipart.reply.body.multipart.reply.table.features._case.MultipartReplyTableFeaturesBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.multipart.reply.multipart.reply.body.multipart.reply.table.features._case.multipart.reply.table.features.TableFeatures;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.multipart.reply.multipart.reply.body.multipart.reply.table.features._case.multipart.reply.table.features.TableFeaturesBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.role.service.rev150727.OfpRole;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier;
 import org.opendaylight.yangtools.yang.common.RpcError;
@@ -194,11 +195,12 @@ public class DeviceManagerImplTest {
 
     public void onDeviceContextLevelUp(boolean withException) {
         DeviceManagerImpl deviceManager = prepareDeviceManager(withException);
+        DeviceState mockedDeviceState = mock(DeviceState.class);
+        when(mockedDeviceContext.getDeviceState()).thenReturn(mockedDeviceState);
+        when(mockedDeviceState.getRole()).thenReturn(OfpRole.BECOMEMASTER);
 
         if (withException) {
             doThrow(new IllegalStateException("dummy")).when(mockedDeviceContext).initialSubmitTransaction();
-            DeviceState mockedDeviceState = mock(DeviceState.class);
-            when(mockedDeviceContext.getDeviceState()).thenReturn(mockedDeviceState);
         }
 
         deviceManager.onDeviceContextLevelUp(mockedDeviceContext);
diff --git a/openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/role/RoleContextImplTest.java b/openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/role/RoleContextImplTest.java
new file mode 100644 (file)
index 0000000..bf0e02e
--- /dev/null
@@ -0,0 +1,126 @@
+/**
+ * Copyright (c) 2015 Cisco Systems, Inc. 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.impl.role;
+
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import com.google.common.util.concurrent.SettableFuture;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentMatcher;
+import org.mockito.Matchers;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.opendaylight.controller.md.sal.common.api.clustering.EntityOwnershipService;
+import org.opendaylight.controller.sal.binding.api.RpcProviderRegistry;
+import org.opendaylight.openflowplugin.api.OFConstants;
+import org.opendaylight.openflowplugin.api.openflow.connection.ConnectionContext;
+import org.opendaylight.openflowplugin.api.openflow.device.DeviceContext;
+import org.opendaylight.openflowplugin.api.openflow.device.DeviceState;
+import org.opendaylight.openflowplugin.impl.util.DeviceStateUtil;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeRef;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.FeaturesReply;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.GetFeaturesOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.role.service.rev150727.OfpRole;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.role.service.rev150727.SalRoleService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.role.service.rev150727.SetRoleInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.role.service.rev150727.SetRoleOutput;
+import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
+
+/**
+ * Created by kramesha on 9/1/15.
+ */
+public class RoleContextImplTest {
+
+    @Mock
+    private EntityOwnershipService entityOwnershipService;
+
+    @Mock
+    private OpenflowOwnershipListener openflowOwnershipListener;
+
+    @Mock
+    private RpcProviderRegistry rpcProviderRegistry;
+
+    @Mock
+    private DeviceContext deviceContext;
+
+    @Mock
+    private ConnectionContext connectionContext;
+
+    @Mock
+    private DeviceState deviceState;
+
+    @Mock
+    private SalRoleService salRoleService;
+
+    @Mock
+    private GetFeaturesOutput getFeaturesOutput;
+
+    @Mock
+    private FeaturesReply featuresReply;
+
+    private NodeId nodeId = NodeId.getDefaultInstance("openflow:1");
+    private KeyedInstanceIdentifier<Node, NodeKey> instanceIdentifier = DeviceStateUtil.createNodeInstanceIdentifier(nodeId);
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        when(deviceContext.getPrimaryConnectionContext()).thenReturn(connectionContext);
+        when(deviceContext.getDeviceState()).thenReturn(deviceState);
+        when(connectionContext.getNodeId()).thenReturn(nodeId);
+        when(deviceState.getNodeInstanceIdentifier()).thenReturn(instanceIdentifier);
+        when(rpcProviderRegistry.getRpcService(SalRoleService.class)).thenReturn(salRoleService);
+        when(deviceState.getFeatures()).thenReturn(getFeaturesOutput);
+        when(getFeaturesOutput.getVersion()).thenReturn(OFConstants.OFP_VERSION_1_3);
+        when(deviceContext.getPrimaryConnectionContext().getFeatures()).thenReturn(featuresReply);
+    }
+
+    @Test
+    public void testOnRoleChanged() {
+        OfpRole newRole = OfpRole.BECOMEMASTER;
+
+        SettableFuture<RpcResult<SetRoleOutput>> future = SettableFuture.create();
+        future.set(RpcResultBuilder.<SetRoleOutput>success().build());
+        when(salRoleService.setRole(Matchers.argThat(new SetRoleInputMatcher(newRole, instanceIdentifier))))
+                .thenReturn(future);
+
+        RoleContextImpl roleContext = new RoleContextImpl(deviceContext, rpcProviderRegistry, entityOwnershipService, openflowOwnershipListener);
+        roleContext.setSalRoleService(salRoleService);
+
+        roleContext.onRoleChanged(OfpRole.BECOMESLAVE, newRole);
+
+        verify(deviceState).setRole(newRole);
+    }
+
+
+    private class SetRoleInputMatcher extends ArgumentMatcher<SetRoleInput> {
+
+        private OfpRole ofpRole;
+        private NodeRef nodeRef;
+        public SetRoleInputMatcher(OfpRole ofpRole, KeyedInstanceIdentifier<Node, NodeKey> instanceIdentifier) {
+            this.ofpRole = ofpRole;
+            nodeRef = new NodeRef(instanceIdentifier);
+
+        }
+
+        @Override
+        public boolean matches(Object o) {
+            SetRoleInput input = (SetRoleInput) o;
+            if (input.getControllerRole() == ofpRole &&
+                    input.getNode().equals(nodeRef)) {
+                return true;
+            }
+            return false;
+        }
+    }
+}
index e5cd598c4a5a4752c4b35c77006220c4be1f9c1f..ee20b1e649e8ed054faf52e02119320c342d8092 100644 (file)
@@ -10,13 +10,10 @@ package org.opendaylight.openflowplugin.impl.rpc;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.mockito.Mockito.when;
-
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.mockito.Matchers;
 import org.mockito.Mock;
-import org.mockito.Mockito;
 import org.mockito.runners.MockitoJUnitRunner;
 import org.opendaylight.controller.sal.binding.api.BindingAwareBroker;
 import org.opendaylight.openflowplugin.api.openflow.device.DeviceContext;
@@ -24,8 +21,6 @@ import org.opendaylight.openflowplugin.api.openflow.device.DeviceState;
 import org.opendaylight.openflowplugin.api.openflow.device.RequestContext;
 import org.opendaylight.openflowplugin.api.openflow.rpc.RpcContext;
 import org.opendaylight.openflowplugin.api.openflow.statistics.ofpspecific.MessageSpy;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.SalFlowService;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeContext;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
@@ -47,10 +42,6 @@ public class RpcContextImplTest {
     private DeviceContext deviceContext;
     @Mock
     private MessageSpy messageSpy;
-    @Mock
-    private SalFlowService salFlowServiceInstance;
-    @Mock
-    BindingAwareBroker.RoutedRpcRegistration<SalFlowService> routedRpcRegistration;
 
     private KeyedInstanceIdentifier<Node, NodeKey> nodeInstanceIdentifier;
 
@@ -61,52 +52,26 @@ public class RpcContextImplTest {
 
         when(deviceState.getNodeInstanceIdentifier()).thenReturn(nodeInstanceIdentifier);
         when(deviceContext.getDeviceState()).thenReturn(deviceState);
-        when(mockedRpcProviderRegistry.addRoutedRpcImplementation(
-                Matchers.<Class<SalFlowService>>any(), Matchers.any(SalFlowService.class)))
-                .thenReturn(routedRpcRegistration);
     }
 
     @Test
-    public void testCreateRequestContext() throws Exception {
-        try (final RpcContext rpcContext = new RpcContextImpl(messageSpy, mockedRpcProviderRegistry, deviceContext, 1)) {
-            RequestContext<?> requestContext1 = rpcContext.createRequestContext();
-            assertNotNull(requestContext1);
+    public void invokeRpcTest() {
 
-            // quota exceeded
-            RequestContext<?> requestContext2 = rpcContext.createRequestContext();
-            assertNull(requestContext2);
-
-            requestContext1.close();
-            RequestContext<?> requestContext3 = rpcContext.createRequestContext();
-            assertNotNull(requestContext3);
-        }
-    }
-
-    @Test
-    public void testRegisterRpcServiceImplementation() throws Exception {
-        try (final RpcContext rpcContext = new RpcContextImpl(messageSpy, mockedRpcProviderRegistry, deviceContext, 10)) {
-            rpcContext.registerRpcServiceImplementation(SalFlowService.class, salFlowServiceInstance);
-            Mockito.verify(routedRpcRegistration).registerPath(NodeContext.class, nodeInstanceIdentifier);
-        }
     }
 
     @Test
-    public void testClose() throws Exception {
-        try (final RpcContext rpcContext = new RpcContextImpl(messageSpy, mockedRpcProviderRegistry, deviceContext, 10)) {
-            rpcContext.registerRpcServiceImplementation(SalFlowService.class, salFlowServiceInstance);
-            rpcContext.close();
-            Mockito.verify(routedRpcRegistration).unregisterPath(NodeContext.class, nodeInstanceIdentifier);
-            Mockito.verify(routedRpcRegistration).close();
+    public void testStoreOrFail() throws Exception {
+        try (final RpcContext rpcContext = new RpcContextImpl(messageSpy, mockedRpcProviderRegistry, deviceContext, 100)) {
+            RequestContext<?> requestContext = rpcContext.createRequestContext();
+            assertNotNull(requestContext);
         }
     }
 
     @Test
-    public void testOnDeviceContextClosed() throws Exception {
-        try (final RpcContext rpcContext = new RpcContextImpl(messageSpy, mockedRpcProviderRegistry, deviceContext, 10)) {
-            rpcContext.registerRpcServiceImplementation(SalFlowService.class, salFlowServiceInstance);
-            rpcContext.onDeviceContextClosed(deviceContext);
-            Mockito.verify(routedRpcRegistration).unregisterPath(NodeContext.class, nodeInstanceIdentifier);
-            Mockito.verify(routedRpcRegistration).close();
+    public void testStoreOrFailThatFails() throws Exception {
+        try (final RpcContext rpcContext = new RpcContextImpl(messageSpy, mockedRpcProviderRegistry, deviceContext, 0)) {
+            RequestContext<?> requestContext = rpcContext.createRequestContext();
+            assertNull(requestContext);
         }
     }
 }
index d9815accb83ddd45b0c15857dea52a606ebe9fc4..ce3e4798190341b195110614642370109300e082 100644 (file)
  */
 package org.opendaylight.openflowplugin.impl.rpc;
 
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.times;
-
-import org.junit.Before;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import java.math.BigInteger;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import org.junit.Ignore;
 import org.junit.Test;
-import org.junit.runner.RunWith;
 import org.mockito.Matchers;
 import org.mockito.Mock;
-import org.mockito.Mockito;
-import org.mockito.runners.MockitoJUnitRunner;
-import org.opendaylight.controller.sal.binding.api.BindingAwareBroker;
 import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.ProviderContext;
-import org.opendaylight.openflowplugin.api.OFConstants;
+import org.opendaylight.openflowjava.protocol.api.connection.ConnectionAdapter;
 import org.opendaylight.openflowplugin.api.openflow.connection.ConnectionContext;
 import org.opendaylight.openflowplugin.api.openflow.device.DeviceContext;
-import org.opendaylight.openflowplugin.api.openflow.device.DeviceState;
-import org.opendaylight.openflowplugin.api.openflow.device.handlers.DeviceInitializationPhaseHandler;
-import org.opendaylight.openflowplugin.api.openflow.registry.ItemLifeCycleRegistry;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeContext;
+import org.opendaylight.openflowplugin.api.openflow.device.Xid;
+import org.opendaylight.openflowplugin.api.openflow.statistics.ofpspecific.MessageSpy;
+import org.opendaylight.openflowplugin.impl.services.SalFlowServiceImpl;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.AddFlowInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.AddFlowInputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.AddFlowOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.SalFlowService;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.FeaturesReply;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.GetFeaturesOutputBuilder;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier;
 import org.opendaylight.yangtools.yang.binding.RpcService;
+import org.opendaylight.yangtools.yang.common.RpcResult;
 
-@RunWith(MockitoJUnitRunner.class)
 public class RpcManagerImplTest {
 
-    private static final int AWAITED_NUM_OF_CALL_ADD_ROUTED_RPC = 10;
+    private static final int AWAITED_NUM_OF_CALL_ADD_ROUTED_RPC = 12;
 
-    private RpcManagerImpl rpcManager;
-    @Mock
-    private ProviderContext rpcProviderRegistry;
-    @Mock
-    private DeviceContext deviceContext;
-    @Mock
-    private DeviceInitializationPhaseHandler deviceINitializationPhaseHandler;
-    @Mock
-    private ConnectionContext connectionContext;
-    @Mock
-    private BindingAwareBroker.RoutedRpcRegistration<RpcService> routedRpcRegistration;
-    @Mock
-    private DeviceState deviceState;
+
+    final ProviderContext mockedProviderContext = mock(ProviderContext.class);
+    final RpcManagerImpl rpcManager = new RpcManagerImpl(mockedProviderContext, 500);
+    final DeviceContext mockedDeviceContext = mock(DeviceContext.class);
     @Mock
-    private ItemLifeCycleRegistry itemLifeCycleRegistry;
-
-    private KeyedInstanceIdentifier<Node, NodeKey> nodePath;
-
-    @Before
-    public void setUp() {
-        nodePath = KeyedInstanceIdentifier.create(Nodes.class).child(Node.class, new NodeKey(new NodeId("openflow-junit:1")));
-        rpcManager = new RpcManagerImpl(rpcProviderRegistry, 5);
-        rpcManager.setDeviceInitializationPhaseHandler(deviceINitializationPhaseHandler);
-        FeaturesReply features = new GetFeaturesOutputBuilder()
-                .setVersion(OFConstants.OFP_VERSION_1_3)
-                .build();
-        Mockito.when(connectionContext.getFeatures()).thenReturn(features);
-        Mockito.when(deviceContext.getPrimaryConnectionContext()).thenReturn(connectionContext);
-        Mockito.when(deviceContext.getDeviceState()).thenReturn(deviceState);
-        Mockito.when(deviceContext.getItemLifeCycleSourceRegistry()).thenReturn(itemLifeCycleRegistry);
-        Mockito.when(deviceState.getNodeInstanceIdentifier()).thenReturn(nodePath);
+    private MessageSpy messageSpy;
+
+    @Ignore
+    @Test
+    public void deviceConnectedTest() {
+
+        rpcManager.onDeviceContextLevelUp(mockedDeviceContext);
+
+        verify(mockedProviderContext, times(AWAITED_NUM_OF_CALL_ADD_ROUTED_RPC)).addRoutedRpcImplementation(
+                Matchers.any(Class.class), Matchers.any(RpcService.class));
     }
 
+
+    /**
+     * Tests behavior of RpcContextImpl when calling rpc from MD-SAL
+     */
+    @Ignore
     @Test
-    public void testOnDeviceContextLevelUp() {
+    public void invokeRpcTestExistsCapacityTest() throws InterruptedException, ExecutionException {
+        final ConnectionContext mockedConnectionContext = mock(ConnectionContext.class);
+        final FeaturesReply mockedFeatures = mock(FeaturesReply.class);
+        final BigInteger dummyDatapathId = BigInteger.ONE;
+        final Short dummyVersion = 1;
+        final ConnectionAdapter mockedConnectionAdapter = mock(ConnectionAdapter.class);
+
+        when(mockedFeatures.getDatapathId()).thenReturn(dummyDatapathId);
+        when(mockedFeatures.getVersion()).thenReturn(dummyVersion);
+        when(mockedConnectionContext.getFeatures()).thenReturn(mockedFeatures);
+        when(mockedConnectionContext.getConnectionAdapter()).thenReturn(mockedConnectionAdapter);
+        when(mockedDeviceContext.getPrimaryConnectionContext()).thenReturn(mockedConnectionContext);
+        final Xid mockedXid = mock(Xid.class);
+        final Long dummyXid = 1l;
+        when(mockedXid.getValue()).thenReturn(dummyXid);
+        when(mockedDeviceContext.getReservedXid()).thenReturn(dummyXid);
 
-        Mockito.when(rpcProviderRegistry.addRoutedRpcImplementation(
-                Matchers.<Class<RpcService>>any(), Matchers.any(RpcService.class)))
-                .thenReturn(routedRpcRegistration);
+        invokeRpcTestExistsCapacity(10, true);
+        invokeRpcTestExistsCapacity(0, false);
+    }
+
+    private void invokeRpcTestExistsCapacity(final int capacity, final boolean result) throws InterruptedException,
+            ExecutionException {
+        // TODO: how to invoke service remotely?
+        NodeId nodeId = new NodeId("openflow:1");
+        KeyedInstanceIdentifier<Node, NodeKey> nodeInstanceIdentifier = InstanceIdentifier.create(Nodes.class).child(Node.class, new NodeKey(nodeId));
+        final RpcContextImpl rpcContext = new RpcContextImpl(messageSpy, mockedProviderContext, mockedDeviceContext, capacity);
+        when(mockedProviderContext.getRpcService(SalFlowService.class)).thenReturn(new SalFlowServiceImpl(rpcContext, mockedDeviceContext));
+
+        final SalFlowService salFlowService = mockedProviderContext.getRpcService(SalFlowService.class);
+        final Future<RpcResult<AddFlowOutput>> addedFlow = salFlowService.addFlow(prepareTestingAddFlow());
+    }
 
-        rpcManager.onDeviceContextLevelUp(deviceContext);
+    /**
+     * @return
+     */
+    private static AddFlowInput prepareTestingAddFlow() {
+        final AddFlowInputBuilder builder = new AddFlowInputBuilder();
+        builder.setFlowName("dummy flow");
+        builder.setHardTimeout(10000);
 
-        Mockito.verify(rpcProviderRegistry, times(AWAITED_NUM_OF_CALL_ADD_ROUTED_RPC)).addRoutedRpcImplementation(
-                Matchers.<Class<RpcService>>any(), Matchers.any(RpcService.class));
-        Mockito.verify(routedRpcRegistration, times(AWAITED_NUM_OF_CALL_ADD_ROUTED_RPC)).registerPath(
-                NodeContext.class, nodePath);
-        Mockito.verify(deviceINitializationPhaseHandler).onDeviceContextLevelUp(deviceContext);
+        return builder.build();
     }
 }
diff --git a/openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/services/SalRoleServiceImplTest.java b/openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/services/SalRoleServiceImplTest.java
new file mode 100644 (file)
index 0000000..2de3711
--- /dev/null
@@ -0,0 +1,170 @@
+/**
+ * Copyright (c) 2015 Cisco Systems, Inc. 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.impl.services;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import com.google.common.util.concurrent.ListenableFuture;
+import java.math.BigInteger;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+import org.opendaylight.openflowjava.protocol.api.connection.ConnectionAdapter;
+import org.opendaylight.openflowjava.protocol.api.connection.OutboundQueue;
+import org.opendaylight.openflowplugin.api.openflow.connection.ConnectionContext;
+import org.opendaylight.openflowplugin.api.openflow.device.DeviceContext;
+import org.opendaylight.openflowplugin.api.openflow.device.RequestContext;
+import org.opendaylight.openflowplugin.api.openflow.device.RequestContextStack;
+import org.opendaylight.openflowplugin.api.openflow.device.Xid;
+import org.opendaylight.openflowplugin.api.openflow.statistics.ofpspecific.MessageSpy;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Uri;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeRef;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.FeaturesReply;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.RoleRequestOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.RoleRequestOutputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.role.service.rev150727.OfpRole;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.role.service.rev150727.SalRoleService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.role.service.rev150727.SetRoleInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.role.service.rev150727.SetRoleInputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.role.service.rev150727.SetRoleOutput;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
+
+/**
+ * Created by kramesha on 8/27/15.
+ */
+public class SalRoleServiceImplTest {
+
+    @Mock
+    private RequestContextStack mockRequestContextStack;
+
+    @Mock
+    private DeviceContext mockDeviceContext;
+
+    @Mock
+    private ConnectionAdapter mockConnectionAdapter;
+
+    @Mock
+    private FeaturesReply mockFeaturesReply;
+
+    @Mock
+    private ConnectionContext mockConnectionContext;
+
+    @Mock
+    private MessageSpy mockMessageSpy;
+
+    @Mock
+    private RequestContext<RoleRequestOutput> mockRequestContext;
+
+    @Mock
+    private OutboundQueue mockOutboundQueue;
+
+    private NodeId testNodeId = new NodeId(Uri.getDefaultInstance("openflow:1"));
+
+    private static short testVersion = 4;
+
+    private static long testXid = 100L;
+
+    private NodeRef nodeRef;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        Mockito.when(mockDeviceContext.getPrimaryConnectionContext()).thenReturn(mockConnectionContext);
+        Mockito.when(mockConnectionContext.getFeatures()).thenReturn(mockFeaturesReply);
+        Mockito.when(mockConnectionContext.getNodeId()).thenReturn(testNodeId);
+        Mockito.when(mockFeaturesReply.getVersion()).thenReturn(testVersion);
+        Mockito.when(mockDeviceContext.getMessageSpy()).thenReturn(mockMessageSpy);
+        Mockito.when(mockRequestContextStack.<RoleRequestOutput>createRequestContext()).thenReturn(mockRequestContext);
+        Mockito.when(mockRequestContext.getXid()).thenReturn(new Xid(testXid));
+        Mockito.when(mockConnectionContext.getOutboundQueueProvider()).thenReturn(mockOutboundQueue);
+        Mockito.when(mockDeviceContext.getPrimaryConnectionContext().getConnectionState()).thenReturn(ConnectionContext.CONNECTION_STATE.WORKING);
+
+        NodeKey key = new NodeKey(testNodeId);
+        InstanceIdentifier<Node> path = InstanceIdentifier.<Nodes>builder(Nodes.class)
+                .<Node, NodeKey>child(Node.class, key)
+                .build();
+        nodeRef = new NodeRef(path);
+
+    }
+
+    @Test
+    public void testSetRole() throws Exception {
+        RoleRequestOutput roleRequestOutput = (new RoleRequestOutputBuilder())
+                .setXid(testXid).setGenerationId(BigInteger.valueOf(1)).build();
+        ListenableFuture<RpcResult<RoleRequestOutput>> futureOutput =
+                RpcResultBuilder.<RoleRequestOutput>success().withResult(roleRequestOutput).buildFuture();
+
+        Mockito.when(mockRequestContext.getFuture()).thenReturn(futureOutput);
+
+
+        SalRoleService salRoleService = new SalRoleServiceImpl(mockRequestContextStack, mockDeviceContext);
+
+        SetRoleInput setRoleInput = new SetRoleInputBuilder()
+                .setControllerRole(OfpRole.BECOMESLAVE)
+                .setNode(nodeRef)
+                .build();
+
+        Future<RpcResult<SetRoleOutput>> future = salRoleService.setRole(setRoleInput);
+
+        RpcResult<SetRoleOutput> roleOutputRpcResult = future.get(5, TimeUnit.SECONDS);
+        assertNotNull("RpcResult from future cannot be null.", roleOutputRpcResult);
+        assertTrue("RpcResult from future is not successful.", roleOutputRpcResult.isSuccessful());
+
+        SetRoleOutput setRoleOutput = roleOutputRpcResult.getResult();
+        assertNotNull(setRoleOutput);
+        assertEquals(BigInteger.valueOf(testXid), setRoleOutput.getTransactionId().getValue());
+
+    }
+
+    @Test
+    public void testDuplicateRoles() throws Exception {
+        // set role to slave
+
+        RoleRequestOutput roleRequestOutput = (new RoleRequestOutputBuilder())
+                .setXid(testXid).setGenerationId(BigInteger.valueOf(1)).build();
+        ListenableFuture<RpcResult<RoleRequestOutput>> futureOutput =
+                RpcResultBuilder.<RoleRequestOutput>success().withResult(roleRequestOutput).buildFuture();
+
+        Mockito.when(mockRequestContext.getFuture()).thenReturn(futureOutput);
+
+        SalRoleService salRoleService = new SalRoleServiceImpl(mockRequestContextStack, mockDeviceContext);
+
+        SetRoleInput setRoleInput = new SetRoleInputBuilder()
+                .setControllerRole(OfpRole.BECOMESLAVE)
+                .setNode(nodeRef)
+                .build();
+
+        Future<RpcResult<SetRoleOutput>> future = salRoleService.setRole(setRoleInput);
+
+        RpcResult<SetRoleOutput> roleOutputRpcResult = future.get(5, TimeUnit.SECONDS);
+        assertNotNull("RpcResult from future cannot be null.", roleOutputRpcResult);
+        assertTrue("RpcResult from future is not successful.", roleOutputRpcResult.isSuccessful());
+
+        SetRoleOutput setRoleOutput = roleOutputRpcResult.getResult();
+        assertNotNull(setRoleOutput);
+        assertEquals(BigInteger.valueOf(testXid), setRoleOutput.getTransactionId().getValue());
+
+        // make another role change with the same role - slave
+        Future<RpcResult<SetRoleOutput>> future2 = salRoleService.setRole(setRoleInput);
+        RpcResult<SetRoleOutput> roleOutputRpcResult2 = future2.get(5, TimeUnit.SECONDS);
+        assertNotNull("RpcResult from future cannot be null.", roleOutputRpcResult2);
+        assertTrue("RpcResult from future for duplicate role is not successful.", roleOutputRpcResult2.isSuccessful());
+
+    }
+}
index 776a82fee3de417561596c819ebe6beb89283a04..4056951f66f0d4fbb49b495d944d840f5fa515a4 100644 (file)
 package org.opendaylight.openflowplugin.impl.util;
 
 
-import static org.mockito.Mockito.*;
-import static org.mockito.Matchers.*;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
 import java.math.BigInteger;
 import org.junit.Test;
-
 import org.opendaylight.openflowplugin.api.openflow.connection.ConnectionContext;
 import org.opendaylight.openflowplugin.api.openflow.device.DeviceContext;
 import org.opendaylight.openflowplugin.api.openflow.rpc.RpcContext;
@@ -30,7 +32,7 @@ public class MdSalRegistratorUtilsTest {
      * Number of currently registrated services (can be changed) in {@link MdSalRegistratorUtils#registerServices
      * (RpcContext, DeviceContext)}
      */
-    private static final int NUMBER_OF_RPC_SERVICE_REGISTRATION = 10;
+    private static final int NUMBER_OF_RPC_SERVICE_REGISTRATION = 9;
 
     @Test
     public void registerServiceTest() {