Merge "BUG-4283 experimenter msg support - registration part"
authorAnil Vishnoi <vishnoianil@gmail.com>
Fri, 13 Nov 2015 17:04:12 +0000 (17:04 +0000)
committerGerrit Code Review <gerrit@opendaylight.org>
Fri, 13 Nov 2015 17:04:12 +0000 (17:04 +0000)
40 files changed:
features-li/src/main/features/features.xml
model/model-flow-base/src/main/yang/opendaylight-action-types.yang
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/RpcContext.java
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/RpcContextImpl.java
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/DeviceManagerImplTest.java
openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/device/DeviceTransactionChainManagerProviderTest.java
openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/device/TransactionChainManagerTest.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
openflowplugin/src/main/java/org/opendaylight/openflowplugin/openflow/md/core/session/SessionManagerOFImpl.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 e23648dcdeb369586bf16f37c512cc4e8f7fb2f0..6f2aa63d65e7224c46145a84ab345dc7b07dfd65 100644 (file)
@@ -225,14 +225,14 @@ module opendaylight-action-types {
 
             case copy-ttl-out-case {
                 container copy-ttl-out {
-                    presence "no content action"
+                    presence "no content action";
                 }
              }
 
 
             case copy-ttl-in-case {
                 container copy-ttl-in {
-                    presence "no content action"
+                    presence "no content action";
                 }
              }
 
@@ -349,13 +349,13 @@ module opendaylight-action-types {
 
            case strip-vlan-action-case {
                 container strip-vlan-action {
-                    presence "no content action"
+                    presence "no content action";
                 }
              }
 
             case sw-path-action-case {
                 container sw-path-action {
-                    presence "no content action"
+                    presence "no content action";
                 }
              }
          }
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 ec8c7e7bcf65713755fef35d998f48cda21c688a..30458e01e6009bd121f0eee7fbd4ec3667d369d4 100644 (file)
@@ -18,6 +18,6 @@ import org.opendaylight.yangtools.yang.binding.RpcService;
  * <p>
  * Created by Martin Bobak &lt;mbobak@cisco.com&gt; on 25.2.2015.
  */
-public interface RpcContext extends RequestContextStack, AutoCloseable, DeviceContextClosedHandler {
+public interface RpcContext extends RequestContextStack, AutoCloseable {
     <S extends RpcService> void registerRpcServiceImplementation(Class<S> serviceClass, S serviceInstance);
 }
index 1ff35e17f6c9df5420dec1644797a5329d1c52af..928fd716d8b4b69955fa450c86ac61b45bcc3344 100644 (file)
@@ -8,6 +8,7 @@
 
 package org.opendaylight.openflowplugin.api.openflow.rpc;
 
+import org.opendaylight.openflowplugin.api.openflow.device.handlers.DeviceContextClosedHandler;
 import org.opendaylight.openflowplugin.api.openflow.device.handlers.DeviceInitializationPhaseHandler;
 import org.opendaylight.openflowplugin.api.openflow.device.handlers.DeviceInitializator;
 
@@ -18,6 +19,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, DeviceContextClosedHandler {
 
 }
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..5731c93bfde19943525c6e1c2331760336618247 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,9 +411,16 @@ public class DeviceContextImpl implements DeviceContext {
             deviceContextClosedHandler.onDeviceContextClosed(this);
         }
 
+        LOG.info("Closing transaction chain manager without cleaning inventory operational");
         transactionChainManager.close();
     }
 
+    @Override
+    public void onDeviceDisconnectedFromCluster() {
+        LOG.info("Removing device from operational and closing transaction Manager for device:{}", getDeviceState().getNodeId());
+        transactionChainManager.cleanupPostClosure();
+    }
+
     @Override
     public void onDeviceDisconnected(final ConnectionContext connectionContext) {
         if (getPrimaryConnectionContext().equals(connectionContext)) {
@@ -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..4ae3d1eed9b66dc1d18f50f0de02d2b97c50f154 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,22 @@ 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 {
+                //if role = slave
+                try {
+                    ((DeviceContextImpl) deviceContext).cancelTransaction();
+                } catch (Exception e) {
+                    //TODO: how can we avoid it. pingpong does not have cancel
+                    LOG.debug("Expected Exception: Cancel Txn exception thrown for slaves", e);
+                }
+
+            }
+
         } 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 +206,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 +337,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..06228701495bec0be2cbedd9934f90ac2ae76226 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) {
@@ -176,16 +183,39 @@ class TransactionChainManager implements TransactionChainListener, AutoCloseable
         submitIsEnabled = true;
     }
 
-    @Override
-    public void close() {
+    /**
+     * When a device disconnects from a node of the cluster, the device context gets closed. With that the txChainMgr
+     * status is set to SHUTTING_DOWN and is closed.
+     * When the EntityOwnershipService notifies and is derived that this was indeed the last node from which the device
+     * had disconnected, then we clean the inventory.
+     * Called from DeviceContext
+     */
+    public void cleanupPostClosure() {
         LOG.debug("Removing node {} from operational DS.", nodeII);
         synchronized (txLock) {
-            final WriteTransaction writeTx = getTransactionSafely();
+            final WriteTransaction writeTx;
+
+            //TODO(Kamal): Fix this. This might cause two txChain Manager working on the same node.
+            if (txChainFactory == null) {
+                LOG.info("Creating new Txn Chain Factory for cleanup purposes - Race Condition Hazard, " +
+                        "Concurrent Modification Hazard, node:{}", nodeII);
+                createTxChain(dataBroker);
+            }
+
+            if (TransactionChainManagerStatus.SHUTTING_DOWN.equals(transactionChainManagerStatus)) {
+                // status is already shutdown. so get the tx directly
+                writeTx = txChainFactory.newWriteOnlyTransaction();
+            } else {
+                writeTx = getTransactionSafely();
+            }
+
             this.transactionChainManagerStatus = TransactionChainManagerStatus.SHUTTING_DOWN;
             writeTx.delete(LogicalDatastoreType.OPERATIONAL, nodeII);
             LOG.debug("Delete node {} from operational DS put to write transaction.", nodeII);
+
             CheckedFuture<Void, TransactionCommitFailedException> submitsFuture = writeTx.submit();
             LOG.debug("Delete node {} from operational DS write transaction submitted.", nodeII);
+
             Futures.addCallback(submitsFuture, new FutureCallback<Void>() {
                 @Override
                 public void onSuccess(final Void aVoid) {
@@ -204,6 +234,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.");
@@ -217,9 +251,20 @@ class TransactionChainManager implements TransactionChainListener, AutoCloseable
             }
         }
         txChainFactory.close();
+        txChainFactory = null;
         LOG.debug("Transaction chain factory closed.");
     }
 
+    @Override
+    public void close() {
+        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..814cc91
--- /dev/null
@@ -0,0 +1,111 @@
+/**
+ * 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 com.google.common.base.Optional;
+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.controller.md.sal.common.api.clustering.EntityOwnershipState;
+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 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());
+
+        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);
+                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;
+
+        Optional<EntityOwnershipState> entityOwnershipStateOptional = entityOwnershipService.getOwnershipState(entity);
+
+        if (entityOwnershipStateOptional != null && entityOwnershipStateOptional.isPresent()) {
+            final EntityOwnershipState entityOwnershipState = entityOwnershipStateOptional.get();
+            if (entityOwnershipState.hasOwner()) {
+                LOG.debug("An owner exist for entity {}", entity);
+                roleChangeExecutor.submit(new Callable<Object>() {
+                    @Override
+                    public Object call() throws Exception {
+                        if (entityOwnershipState.isOwner()) {
+                            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();
+        }
+    }
+}
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..06305e1
--- /dev/null
@@ -0,0 +1,183 @@
+/**
+ * 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) {
+
+        if (!isDeviceConnected()) {
+            // this can happen as after the disconnect, we still get a last messsage from EntityOwnershipService.
+            LOG.info("Device {} is disconnected from this node. Hence not attempting a role change.",
+                    deviceContext.getPrimaryConnectionContext().getNodeId());
+            return;
+        }
+
+        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);
+            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..78cb8c7
--- /dev/null
@@ -0,0 +1,97 @@
+/**
+ * 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);
+            return;
+        }
+
+        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 502ac137241296d82d4b68fa8ad3db3464b0aece..b373bf93db35c3b868cef95d7faf3a802e8fa82d 100644 (file)
@@ -68,6 +68,8 @@ public class RpcContextImpl implements RpcContext {
         for (final RoutedRpcRegistration<?> rpcRegistration : rpcRegistrations) {
             rpcRegistration.unregisterPath(NodeContext.class, deviceContext.getDeviceState().getNodeInstanceIdentifier());
             rpcRegistration.close();
+            LOG.debug("Closing RPC Registration of service {} for device {}.", rpcRegistration.getServiceType(),
+                    deviceContext.getDeviceState().getNodeInstanceIdentifier());
         }
     }
 
@@ -87,9 +89,4 @@ public class RpcContextImpl implements RpcContext {
             }
         };
     }
-
-    @Override
-    public void onDeviceContextClosed(DeviceContext deviceContext) {
-        close();
-    }
 }
index 1b8e5f76b1928febc0537f5f70112d7f97e40f8d..3031214d09f4b18503a50ae1030df7f418cab870 100644 (file)
@@ -12,13 +12,22 @@ import org.opendaylight.openflowplugin.api.openflow.device.DeviceContext;
 import org.opendaylight.openflowplugin.api.openflow.device.handlers.DeviceInitializationPhaseHandler;
 import org.opendaylight.openflowplugin.api.openflow.rpc.RpcContext;
 import org.opendaylight.openflowplugin.api.openflow.rpc.RpcManager;
+import org.opendaylight.openflowplugin.api.openflow.statistics.StatisticsContext;
 import org.opendaylight.openflowplugin.impl.util.MdSalRegistratorUtils;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.role.service.rev150727.OfpRole;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.concurrent.ConcurrentHashMap;
 
 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;
+    private final ConcurrentHashMap<DeviceContext, RpcContext> contexts = new ConcurrentHashMap<>();
 
     public RpcManagerImpl(final RpcProviderRegistry rpcProviderRegistry,
                           final int quotaValue) {
@@ -33,10 +42,56 @@ public class RpcManagerImpl implements RpcManager {
 
     @Override
     public void onDeviceContextLevelUp(final DeviceContext deviceContext) {
-        final RpcContext rpcContext = new RpcContextImpl(deviceContext.getMessageSpy(), rpcProviderRegistry, deviceContext, maxRequestsQuota);
-        deviceContext.addDeviceContextClosedHandler(rpcContext);
-        MdSalRegistratorUtils.registerServices(rpcContext, deviceContext);
+        NodeId nodeId = deviceContext.getDeviceState().getNodeId();
+        OfpRole ofpRole = deviceContext.getDeviceState().getRole();
+
+        LOG.debug("Node:{}, deviceContext.getDeviceState().getRole():{}", nodeId, ofpRole);
+
+        RpcContext rpcContext = contexts.get(deviceContext);
+        if (rpcContext == null) {
+            rpcContext = new RpcContextImpl(deviceContext.getMessageSpy(), rpcProviderRegistry, deviceContext, maxRequestsQuota);
+            contexts.put(deviceContext, rpcContext);
+        }
+
+
+        if (ofpRole == OfpRole.BECOMESLAVE) {
+            // if slave, we need to de-register rpcs if any have been registered, in case of master to slave
+            LOG.info("Unregistering RPC registration (if any) for slave role for node:{}", deviceContext.getDeviceState().getNodeId());
+            try {
+                MdSalRegistratorUtils.unregisterServices(rpcContext);
+            } catch (Exception e) {
+                LOG.error("Exception while unregistering rpcs for slave role for node:{}. But continuing.", nodeId, e);
+            }
+
+        } else {
+            LOG.info("Registering Openflow RPCs for node:{}, role:{}", nodeId, ofpRole);
+            MdSalRegistratorUtils.registerServices(rpcContext, deviceContext);
+        }
+
+        deviceContext.addDeviceContextClosedHandler(this);
+
         // finish device initialization cycle back to DeviceManager
         deviceInitPhaseHandler.onDeviceContextLevelUp(deviceContext);
     }
+
+    @Override
+    public void close() throws Exception {
+
+    }
+
+
+    @Override
+    public void onDeviceContextClosed(DeviceContext deviceContext) {
+        RpcContext removedContext = contexts.remove(deviceContext);
+        if (removedContext != null) {
+            try {
+                LOG.info("Unregistering rpcs for device context closure");
+                removedContext.close();
+            } catch (Exception e) {
+                LOG.error("Exception while unregistering rpcs onDeviceContextClosed handler for node:{}. But continuing.",
+                        deviceContext.getDeviceState().getNodeId(), e);
+            }
+        }
+
+    }
 }
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..cfb430d8f71cc073ff457d3b67eb9e8029339199 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,12 +81,22 @@ public class StatisticsManagerImpl implements StatisticsManager, StatisticsManag
 
     @Override
     public void onDeviceContextLevelUp(final DeviceContext deviceContext) {
+        LOG.debug("Node:{}, deviceContext.getDeviceState().getRole():{}", deviceContext.getDeviceState().getNodeId(),
+                deviceContext.getDeviceState().getRole());
+        if (deviceContext.getDeviceState().getRole() == OfpRole.BECOMESLAVE) {
+            // if slave, we dont poll for statistics and jump to rpc initialization
+            LOG.info("Skipping Statistics for slave role for node:{}", deviceContext.getDeviceState().getNodeId());
+            deviceInitPhaseHandler.onDeviceContextLevelUp(deviceContext);
+            return;
+        }
 
         if (null == hashedWheelTimer) {
             LOG.trace("This is first device that delivered timer. Starting statistics polling immediately.");
             hashedWheelTimer = deviceContext.getTimer();
         }
 
+        LOG.info("Starting Statistics for master role for node:{}", deviceContext.getDeviceState().getNodeId());
+
         final StatisticsContext statisticsContext = new StatisticsContextImpl(deviceContext);
         deviceContext.addDeviceContextClosedHandler(this);
         final ListenableFuture<Boolean> weHaveDynamicData = statisticsContext.gatherDynamicData();
index 0797245504b61a38cbc223203cf9de92b430dc80..7373c44dab2a9f164128a49f3f5ea128d9fbb55f 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,9 @@ 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));
+    }
+
+    public static void unregisterServices(final RpcContext rpcContext) throws Exception {
+        rpcContext.close();
     }
 }
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 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);
index 6b759be020636e3ceb5219048c05569471900879..e4691fdd4793af94919bfa60ea1b359ebde1f313 100644 (file)
@@ -154,7 +154,7 @@ public class DeviceTransactionChainManagerProviderTest {
                     }
                 });
         Mockito.when(writeTx.submit()).thenReturn(checkedSubmitCleanFuture);
-        txChainManager.close();
+        txChainManager.cleanupPostClosure();
         Assert.assertEquals(TransactionChainManager.TransactionChainManagerStatus.SHUTTING_DOWN,
                 txChainManagerRegistration_1.getTransactionChainManager().getTransactionChainManagerStatus());
         txChainManager.attemptToRegisterHandler(readyForNewTransactionChainHandler);
index 9026c60f810c9f34f9a7b9300eb457336e6d380b..7909a3637a80bab91b7c81d7c9b686fd7156033a 100644 (file)
@@ -166,7 +166,7 @@ public class TransactionChainManagerTest {
     public void testAttemptToRegisterHandler2() throws Exception {
         final InOrder inOrder = Mockito.inOrder(writeTx, txChain);
 
-        txChainManager.close();
+        txChainManager.cleanupPostClosure();
         Assert.assertEquals(TransactionChainManager.TransactionChainManagerStatus.SHUTTING_DOWN, txChainManager.getTransactionChainManagerStatus());
 
         boolean attemptResult = txChainManager.attemptToRegisterHandler(readyForNewTransactionChainHandler);
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..0278049
--- /dev/null
@@ -0,0 +1,127 @@
+/**
+ * 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);
+        when(deviceContext.getPrimaryConnectionContext().getConnectionState()).thenReturn(ConnectionContext.CONNECTION_STATE.WORKING);
+    }
+
+    @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() {
index 15c191b2750644161e14732ba7ec5ae02165340a..baf0f21c238603c356ad28d2dc3a8f1b476c2e03 100644 (file)
@@ -96,7 +96,7 @@ public class SessionManagerOFImpl implements ConjunctSessionManager {
     public void invalidateSessionContext(SwitchSessionKeyOF sessionKey) {
         SessionContext context = getSessionContext(sessionKey);
         if (context == null) {
-            LOG.warn("context for invalidation not found");
+            LOG.info("context for invalidation not found");
         } else {
             synchronized (context) {
                 for (Entry<SwitchConnectionDistinguisher, ConnectionConductor> auxEntry : context.getAuxiliaryConductors()) {
@@ -112,7 +112,7 @@ public class SessionManagerOFImpl implements ConjunctSessionManager {
 
     private void invalidateDeadSessionContext(SessionContext sessionContext) {
         if (sessionContext == null) {
-            LOG.warn("context for invalidation not found");
+            LOG.info("context for invalidation not found");
         } else {
             synchronized (sessionContext) {
                 for (Entry<SwitchConnectionDistinguisher, ConnectionConductor> auxEntry : sessionContext
@@ -158,7 +158,7 @@ public class SessionManagerOFImpl implements ConjunctSessionManager {
     private static void invalidateAuxiliary(SessionContext context, SwitchConnectionDistinguisher connectionCookie,
                                             boolean disconnect) {
         if (context == null) {
-            LOG.warn("context for invalidation not found");
+            LOG.info("context for invalidation not found");
         } else {
             ConnectionConductor auxiliaryConductor = context.removeAuxiliaryConductor(connectionCookie);
             if (auxiliaryConductor == null) {