Adding query API in connection-mgr to retrieve node controllers 48/2048/7
authorMuthukumaran Kothandaraman <mkothand@in.ibm.com>
Mon, 21 Oct 2013 13:05:40 +0000 (18:35 +0530)
committerGerrit Code Review <gerrit@opendaylight.org>
Thu, 24 Oct 2013 15:31:01 +0000 (15:31 +0000)
Change-Id: Id76d8577cfc08a96f533c62f2072c1abd040105e
Signed-off-by: Muthukumaran Kothandaraman <mkothand@in.ibm.com>
opendaylight/connectionmanager/api/src/main/java/org/opendaylight/controller/connectionmanager/IConnectionManager.java
opendaylight/connectionmanager/api/src/main/java/org/opendaylight/controller/connectionmanager/NodeAccessPermission.java [new file with mode: 0644]
opendaylight/connectionmanager/implementation/src/main/java/org/opendaylight/controller/connectionmanager/internal/ConnectionManager.java
opendaylight/connectionmanager/implementation/src/main/java/org/opendaylight/controller/connectionmanager/scheme/AbstractScheme.java

index 106d14dc39abd69a718c7634a9639a2ec9374519..c7e7fa4770a171052544bd7f1da3522b3c0af4a8 100644 (file)
@@ -1,4 +1,3 @@
-
 /*
  * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
  *
@@ -19,36 +18,40 @@ import org.opendaylight.controller.sal.core.Node;
 import org.opendaylight.controller.sal.utils.Status;
 
 /**
- * Connection Manager provides south-bound connectivity services.
- * The APIs are currently focused towards Active-Active Clustering support
- * wherein the node can connect to any of the Active Controller in the Cluster.
- * This component can also host the necessary logic for south-bound connectivity
- * when partial cluster is identified during Partition scenarios.
+ * Connection Manager provides south-bound connectivity services. The APIs are
+ * currently focused towards Active-Active Clustering support wherein the node
+ * can connect to any of the Active Controller in the Cluster. This component
+ * can also host the necessary logic for south-bound connectivity when partial
+ * cluster is identified during Partition scenarios.
  *
- * This (and its corresponding implementation) component can also be enhanced further
- * for more fancy algorithms/criteria for connection acceptance.
+ * This (and its corresponding implementation) component can also be enhanced
+ * further for more fancy algorithms/criteria for connection acceptance.
  */
 
 public interface IConnectionManager {
     /**
-     * This method returns Connectivity Algorithm (Scheme) that is currently being used.
+     * This method returns Connectivity Algorithm (Scheme) that is currently
+     * being used.
      *
      * @return ConnectionMgmtScheme Enum that represents the active scheme.
      */
     public ConnectionMgmtScheme getActiveScheme();
 
     /**
-     * Method that will retrieve and return a Set of Nodes that is currently connected to the given controller.
+     * Method that will retrieve and return a Set of Nodes that is currently
+     * connected to the given controller.
      *
-     * @param controller InetAddress of the Controller that is currently connected to a set of Nodes.
+     * @param controller
+     *            InetAddress of the Controller that is currently connected to a
+     *            set of Nodes.
      *
      * @return Set<Node> Set of Nodes connected to a controller.
      */
     public Set<Node> getNodes(InetAddress controller);
 
     /**
-     * Method that will retrieve and return a Set of Nodes that is currently connected to
-     * the controller on which this method is executed.
+     * Method that will retrieve and return a Set of Nodes that is currently
+     * connected to the controller on which this method is executed.
      *
      * @return Set<Node> Set of Nodes connected to this controller.
      */
@@ -57,21 +60,24 @@ public interface IConnectionManager {
     /**
      * @deprecated Use getLocalityStatus(Node node) instead.
      *
-     * Method to test if a node is local to a controller.
+     *             Method to test if a node is local to a controller.
      *
-     * @param node The node for which the locality is being tested
+     * @param node
+     *            The node for which the locality is being tested
      * @return true if node is local to this controller.<br>
-     *         false if either node is not connected to this controller or
-     *         not connected to any other controllers in the cluster.
+     *         false if either node is not connected to this controller or not
+     *         connected to any other controllers in the cluster.
      */
     public boolean isLocal(Node node);
 
     /**
-     * getLocalityStatus provides the tri-state connectivity status as opposed to the
-     * binary status returned by isLocal.
-     * ConnectionLocality enum that is returned by this method also includes the case of
-     * a Node not connected to any of the controllers in the cluster.
-     * @param node The node for which the locality is being verified
+     * getLocalityStatus provides the tri-state connectivity status as opposed
+     * to the binary status returned by isLocal. ConnectionLocality enum that is
+     * returned by this method also includes the case of a Node not connected to
+     * any of the controllers in the cluster.
+     *
+     * @param node
+     *            The node for which the locality is being verified
      * @return ConnectionLocality
      */
     public ConnectionLocality getLocalityStatus(Node node);
@@ -86,25 +92,49 @@ public interface IConnectionManager {
     /**
      * Connect to a node
      *
-     * @param connectionIdentifier identifier with which the application would refer to a given connection.
-     * @param params Connection Params in Map format. This is entirely handled by the south-bound
-     * plugins and is an opaque value for SAL or Connection Manager. Typical values keyed inside
-     * this params are Management IP-Address, Username, Password, Security Keys, etc...
+     * @param connectionIdentifier
+     *            identifier with which the application would refer to a given
+     *            connection.
+     * @param params
+     *            Connection Params in Map format. This is entirely handled by
+     *            the south-bound plugins and is an opaque value for SAL or
+     *            Connection Manager. Typical values keyed inside this params
+     *            are Management IP-Address, Username, Password, Security Keys,
+     *            etc...
      *
-     *  @return Node Node connected to.
+     * @return Node Node connected to.
      */
-    public Node connect (String connectionIdentifier, Map<ConnectionConstants, String> params);
+    public Node connect(String connectionIdentifier,
+            Map<ConnectionConstants, String> params);
 
     /**
      * Connect to a node
      *
-     * @param type Type of the node representing NodeIDType.
-     * @param connectionIdentifier identifier with which the application would refer to a given connection.
-     * @param params Connection Params in Map format. This is entirely handled by the south-bound
-     * plugins and is an opaque value for SAL or Connection Manager. Typical values keyed inside
-     * this params are Management IP-Address, Username, Password, Security Keys, etc...
+     * @param type
+     *            Type of the node representing NodeIDType.
+     * @param connectionIdentifier
+     *            identifier with which the application would refer to a given
+     *            connection.
+     * @param params
+     *            Connection Params in Map format. This is entirely handled by
+     *            the south-bound plugins and is an opaque value for SAL or
+     *            Connection Manager. Typical values keyed inside this params
+     *            are Management IP-Address, Username, Password, Security Keys,
+     *            etc...
+     *
+     * @return Status of the Connect Operation.
+     */
+    public Node connect(String type, String connectionIdentifier,
+            Map<ConnectionConstants, String> params);
+
+    /**
+     * Retrieve list of cluster-members to which Node is connected to
+     *
+     * @param node
+     *            Node for which cluster-members to be retrieved
      *
-     *  @return Status of the Connect Operation.
+     * @return Set<InetAddress> List of cluster-member addresses to which the
+     *         node is connected
      */
-    public Node connect(String type, String connectionIdentifier, Map<ConnectionConstants, String> params);
+    public Set<InetAddress> getControllers(Node node);
 }
diff --git a/opendaylight/connectionmanager/api/src/main/java/org/opendaylight/controller/connectionmanager/NodeAccessPermission.java b/opendaylight/connectionmanager/api/src/main/java/org/opendaylight/controller/connectionmanager/NodeAccessPermission.java
new file mode 100644 (file)
index 0000000..c59e297
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Copyright IBM Corporation, 2013.  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.controller.connectionmanager;
+
+/*
+ *
+ *  This enum enables the options which could be used for querying
+ *  the cluster-members based on the state-change permissions.
+ *
+ *  SB Plugin or any other means could be used to store this metadata
+ *  against each connection w.r.t. Node
+ *
+ *  TODO: When connection-manager is enhanced later to store connection
+ *  metadata with permissions, this could be used to query the connections
+ *  based on permission-level of the connection
+ *
+ */
+
+public enum NodeAccessPermission {
+
+    /*
+     * This option could be used to retrieve the cluster-members who have rights
+     * only to read the Node state but are not eligible for modifying the state
+     * of Node
+     */
+    READONLY_ACCESS,
+
+    /*
+     * This option could be used to retrieve the cluster-members who have rights
+     * to read and modify the state of Node
+     */
+    READWRITE_ACCESS
+
+}
index e5bf5258c5faa86fff78f5959692494f0049052f..773c6cac508b9b8974846d7d1ad38ad80fadc1fb 100644 (file)
@@ -1,4 +1,3 @@
-
 /*
  * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
  *
@@ -22,6 +21,7 @@ package org.opendaylight.controller.connectionmanager.internal;
 
 import java.net.InetAddress;
 import java.net.UnknownHostException;
+import java.util.Collections;
 import java.util.HashSet;
 import java.util.Map;
 import java.util.Set;
@@ -56,11 +56,11 @@ import org.osgi.framework.FrameworkUtil;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-public class ConnectionManager implements IConnectionManager, IConnectionListener,
-                                          ICoordinatorChangeAware, IListenInventoryUpdates,
-                                          ICacheUpdateAware<Node, Set<InetAddress>>,
-                                          CommandProvider {
-    private static final Logger logger = LoggerFactory.getLogger(ConnectionManager.class);
+public class ConnectionManager implements IConnectionManager,
+        IConnectionListener, ICoordinatorChangeAware, IListenInventoryUpdates,
+        ICacheUpdateAware<Node, Set<InetAddress>>, CommandProvider {
+    private static final Logger logger = LoggerFactory
+            .getLogger(ConnectionManager.class);
     private ConnectionMgmtScheme activeScheme = ConnectionMgmtScheme.ANY_CONTROLLER_ONE_MASTER;
     private IClusterGlobalServices clusterServices;
     private ConcurrentMap<ConnectionMgmtScheme, AbstractScheme> schemes;
@@ -100,7 +100,8 @@ public class ConnectionManager implements IConnectionManager, IConnectionListene
     }
 
     private void getInventories() {
-        Map<Node, Map<String, Property>> nodeProp = this.inventoryService.getNodeProps();
+        Map<Node, Map<String, Property>> nodeProp = this.inventoryService
+                .getNodeProps();
         for (Map.Entry<Node, Map<String, Property>> entry : nodeProp.entrySet()) {
             Node node = entry.getKey();
             logger.debug("getInventories for node:{}", new Object[] { node });
@@ -112,8 +113,10 @@ public class ConnectionManager implements IConnectionManager, IConnectionListene
             updateNode(node, UpdateType.ADDED, props);
         }
 
-        Map<NodeConnector, Map<String, Property>> nodeConnectorProp = this.inventoryService.getNodeConnectorProps();
-        for (Map.Entry<NodeConnector, Map<String, Property>> entry : nodeConnectorProp.entrySet()) {
+        Map<NodeConnector, Map<String, Property>> nodeConnectorProp = this.inventoryService
+                .getNodeConnectorProps();
+        for (Map.Entry<NodeConnector, Map<String, Property>> entry : nodeConnectorProp
+                .entrySet()) {
             Map<String, Property> propMap = entry.getValue();
             Set<Property> props = new HashSet<Property>();
             for (Property property : propMap.values()) {
@@ -123,18 +126,18 @@ public class ConnectionManager implements IConnectionManager, IConnectionListene
         }
     }
 
-
-   public void started() {
-       String schemeStr = System.getProperty("connection.scheme");
-       for (ConnectionMgmtScheme scheme : ConnectionMgmtScheme.values()) {
-           AbstractScheme schemeImpl = SchemeFactory.getScheme(scheme, clusterServices);
-           if (schemeImpl != null) {
-               schemes.put(scheme, schemeImpl);
-               if (scheme.name().equalsIgnoreCase(schemeStr)) {
-                   activeScheme = scheme;
-               }
-           }
-       }
+    public void started() {
+        String schemeStr = System.getProperty("connection.scheme");
+        for (ConnectionMgmtScheme scheme : ConnectionMgmtScheme.values()) {
+            AbstractScheme schemeImpl = SchemeFactory.getScheme(scheme,
+                    clusterServices);
+            if (schemeImpl != null) {
+                schemes.put(scheme, schemeImpl);
+                if (scheme.name().equalsIgnoreCase(schemeStr)) {
+                    activeScheme = scheme;
+                }
+            }
+        }
 
         connectionEventThread.start();
 
@@ -145,7 +148,8 @@ public class ConnectionManager implements IConnectionManager, IConnectionListene
     }
 
     public void init() {
-        connectionEventThread = new Thread(new EventHandler(), "ConnectionEvent Thread");
+        connectionEventThread = new Thread(new EventHandler(),
+                "ConnectionEvent Thread");
         this.connectionEvents = new LinkedBlockingQueue<ConnectionMgmtEvent>();
         schemes = new ConcurrentHashMap<ConnectionMgmtScheme, AbstractScheme>();
     }
@@ -157,7 +161,8 @@ public class ConnectionManager implements IConnectionManager, IConnectionListene
             AbstractScheme scheme = schemes.get(activeScheme);
             for (Node localNode : localNodes) {
                 connectionService.disconnect(localNode);
-                if (scheme != null) scheme.removeNode(localNode);
+                if (scheme != null)
+                    scheme.removeNode(localNode);
             }
         }
     }
@@ -170,28 +175,32 @@ public class ConnectionManager implements IConnectionManager, IConnectionListene
     @Override
     public Set<Node> getNodes(InetAddress controller) {
         AbstractScheme scheme = schemes.get(activeScheme);
-        if (scheme == null) return null;
+        if (scheme == null)
+            return null;
         return scheme.getNodes(controller);
     }
 
     @Override
     public Set<Node> getLocalNodes() {
         AbstractScheme scheme = schemes.get(activeScheme);
-        if (scheme == null) return null;
+        if (scheme == null)
+            return null;
         return scheme.getNodes();
     }
 
     @Override
     public boolean isLocal(Node node) {
         AbstractScheme scheme = schemes.get(activeScheme);
-        if (scheme == null) return false;
+        if (scheme == null)
+            return false;
         return scheme.isLocal(node);
     }
 
     @Override
     public ConnectionLocality getLocalityStatus(Node node) {
         AbstractScheme scheme = schemes.get(activeScheme);
-        if (scheme == null) return ConnectionLocality.NOT_CONNECTED;
+        if (scheme == null)
+            return ConnectionLocality.NOT_CONNECTED;
         return scheme.getLocalityStatus(node);
     }
 
@@ -199,7 +208,8 @@ public class ConnectionManager implements IConnectionManager, IConnectionListene
     public void updateNode(Node node, UpdateType type, Set<Property> props) {
         logger.debug("updateNode: {} type {} props {}", node, type, props);
         AbstractScheme scheme = schemes.get(activeScheme);
-        if (scheme == null) return;
+        if (scheme == null)
+            return;
         switch (type) {
         case ADDED:
             scheme.addNode(node);
@@ -208,22 +218,24 @@ public class ConnectionManager implements IConnectionManager, IConnectionListene
             scheme.removeNode(node);
             break;
         default:
-                break;
+            break;
         }
     }
 
     @Override
     public void updateNodeConnector(NodeConnector nodeConnector,
             UpdateType type, Set<Property> props) {
-        logger.debug("updateNodeConnector: {} type {} props {}", nodeConnector, type, props);
+        logger.debug("updateNodeConnector: {} type {} props {}", nodeConnector,
+                type, props);
         AbstractScheme scheme = schemes.get(activeScheme);
-        if (scheme == null) return;
+        if (scheme == null)
+            return;
         switch (type) {
         case ADDED:
             scheme.addNode(nodeConnector.getNode());
             break;
         default:
-                break;
+            break;
         }
     }
 
@@ -233,70 +245,87 @@ public class ConnectionManager implements IConnectionManager, IConnectionListene
     }
 
     @Override
-    public Node connect(String connectionIdentifier, Map<ConnectionConstants, String> params) {
-        if (connectionService == null) return null;
+    public Node connect(String connectionIdentifier,
+            Map<ConnectionConstants, String> params) {
+        if (connectionService == null)
+            return null;
         Node node = connectionService.connect(connectionIdentifier, params);
         AbstractScheme scheme = schemes.get(activeScheme);
-        if (scheme != null && node != null) scheme.addNode(node);
+        if (scheme != null && node != null)
+            scheme.addNode(node);
         return node;
     }
 
     @Override
-    public Node connect(String type, String connectionIdentifier, Map<ConnectionConstants, String> params) {
-        if (connectionService == null) return null;
+    public Node connect(String type, String connectionIdentifier,
+            Map<ConnectionConstants, String> params) {
+        if (connectionService == null)
+            return null;
         Node node = connectionService.connect(connectionIdentifier, params);
         AbstractScheme scheme = schemes.get(activeScheme);
-        if (scheme != null && node != null) scheme.addNode(node);
+        if (scheme != null && node != null)
+            scheme.addNode(node);
         return node;
     }
 
     @Override
-    public Status disconnect (Node node) {
-        if (node == null) return new Status(StatusCode.BADREQUEST);
-        if (connectionService == null) return new Status(StatusCode.NOSERVICE);
+    public Status disconnect(Node node) {
+        if (node == null)
+            return new Status(StatusCode.BADREQUEST);
+        if (connectionService == null)
+            return new Status(StatusCode.NOSERVICE);
         Status status = connectionService.disconnect(node);
         if (status.isSuccess()) {
             AbstractScheme scheme = schemes.get(activeScheme);
-            if (scheme != null) scheme.removeNode(node);
+            if (scheme != null)
+                scheme.removeNode(node);
         }
         return status;
     }
 
     @Override
     public void entryCreated(Node key, String cacheName, boolean originLocal) {
-        if (originLocal) return;
+        if (originLocal)
+            return;
     }
 
     /*
-     * Clustering Services' doesnt provide the existing states in the cache update callbacks.
-     * Hence, using a scratch local cache to maintain the existing state.
-     *
+     * Clustering Services' doesnt provide the existing states in the cache
+     * update callbacks. Hence, using a scratch local cache to maintain the
+     * existing state.
      */
     private ConcurrentMap<Node, Set<InetAddress>> existingConnections = new ConcurrentHashMap<Node, Set<InetAddress>>();
 
     @Override
-    public void entryUpdated(Node node, Set<InetAddress> newControllers, String cacheName, boolean originLocal) {
-        if (originLocal) return;
+    public void entryUpdated(Node node, Set<InetAddress> newControllers,
+            String cacheName, boolean originLocal) {
+        if (originLocal)
+            return;
         Set<InetAddress> existingControllers = existingConnections.get(node);
         if (existingControllers != null) {
-            logger.debug("Processing Update for : {} NewControllers : {} existingControllers : {}", node,
-                    newControllers.toString(), existingControllers.toString());
+            logger.debug(
+                    "Processing Update for : {} NewControllers : {} existingControllers : {}",
+                    node, newControllers.toString(),
+                    existingControllers.toString());
             if (newControllers.size() < existingControllers.size()) {
-                Set<InetAddress> removed = new HashSet<InetAddress>(existingControllers);
+                Set<InetAddress> removed = new HashSet<InetAddress>(
+                        existingControllers);
                 if (removed.removeAll(newControllers)) {
                     logger.debug("notifyNodeDisconnectFromMaster({})", node);
                     notifyNodeDisconnectedEvent(node);
                 }
             }
         } else {
-            logger.debug("Ignoring the Update for : {} NewControllers : {}", node, newControllers.toString());
+            logger.debug("Ignoring the Update for : {} NewControllers : {}",
+                    node, newControllers.toString());
         }
         existingConnections.put(node, newControllers);
     }
 
     @Override
     public void entryDeleted(Node key, String cacheName, boolean originLocal) {
-        if (originLocal) return;
+        if (originLocal)
+            return;
         logger.debug("Deleted : {} cache : {}", key, cacheName);
         notifyNodeDisconnectedEvent(key);
     }
@@ -307,22 +336,27 @@ public class ConnectionManager implements IConnectionManager, IConnectionListene
                 this.connectionEvents.put(event);
             }
         } catch (InterruptedException e) {
-            logger.debug("enqueueConnectionEvent caught Interrupt Exception for event {}", event);
+            logger.debug(
+                    "enqueueConnectionEvent caught Interrupt Exception for event {}",
+                    event);
         }
     }
 
     private void notifyClusterViewChanged() {
-        ConnectionMgmtEvent event = new ConnectionMgmtEvent(ConnectionMgmtEventType.CLUSTER_VIEW_CHANGED, null);
+        ConnectionMgmtEvent event = new ConnectionMgmtEvent(
+                ConnectionMgmtEventType.CLUSTER_VIEW_CHANGED, null);
         enqueueConnectionEvent(event);
     }
 
     private void notifyNodeDisconnectedEvent(Node node) {
-        ConnectionMgmtEvent event = new ConnectionMgmtEvent(ConnectionMgmtEventType.NODE_DISCONNECTED_FROM_MASTER, node);
+        ConnectionMgmtEvent event = new ConnectionMgmtEvent(
+                ConnectionMgmtEventType.NODE_DISCONNECTED_FROM_MASTER, node);
         enqueueConnectionEvent(event);
     }
 
     /*
-     * this thread monitors the connectionEvent queue for new incoming events from
+     * this thread monitors the connectionEvent queue for new incoming events
+     * from
      */
     private class EventHandler implements Runnable {
         @Override
@@ -334,17 +368,19 @@ public class ConnectionManager implements IConnectionManager, IConnectionListene
                     ConnectionMgmtEventType eType = ev.getEvent();
                     switch (eType) {
                     case NODE_DISCONNECTED_FROM_MASTER:
-                        Node node = (Node)ev.getData();
+                        Node node = (Node) ev.getData();
                         connectionService.notifyNodeDisconnectFromMaster(node);
                         break;
                     case CLUSTER_VIEW_CHANGED:
                         AbstractScheme scheme = schemes.get(activeScheme);
-                        if (scheme == null) return;
+                        if (scheme == null)
+                            return;
                         scheme.handleClusterViewChanged();
                         connectionService.notifyClusterViewChanged();
                         break;
                     default:
-                        logger.error("Unknown Connection event {}", eType.ordinal());
+                        logger.error("Unknown Connection event {}",
+                                eType.ordinal());
                     }
                 } catch (InterruptedException e) {
                     connectionEvents.clear();
@@ -361,7 +397,7 @@ public class ConnectionManager implements IConnectionManager, IConnectionListene
                 null);
     }
 
-    public void _scheme (CommandInterpreter ci) {
+    public void _scheme(CommandInterpreter ci) {
         String schemeStr = ci.nextArgument();
         if (schemeStr == null) {
             ci.println("Please enter valid Scheme name");
@@ -376,7 +412,7 @@ public class ConnectionManager implements IConnectionManager, IConnectionListene
         activeScheme = scheme;
     }
 
-    public void _printNodes (CommandInterpreter ci) {
+    public void _printNodes(CommandInterpreter ci) {
         String controller = ci.nextArgument();
         if (controller == null) {
             ci.println("Nodes connected to this controller : ");
@@ -389,14 +425,14 @@ public class ConnectionManager implements IConnectionManager, IConnectionListene
         }
         try {
             InetAddress address = InetAddress.getByName(controller);
-            ci.println("Nodes connected to controller "+controller);
+            ci.println("Nodes connected to controller " + controller);
             if (this.getNodes(address) == null) {
                 ci.println("None");
             } else {
                 ci.println(this.getNodes(address).toString());
             }
         } catch (UnknownHostException e) {
-           logger.error("An error occured",e);
+            logger.error("An error occured", e);
         }
     }
 
@@ -408,4 +444,12 @@ public class ConnectionManager implements IConnectionManager, IConnectionListene
         help.append("\t printNodes [<controller>]            - Print connected nodes\n");
         return help.toString();
     }
+
+    @Override
+    public Set<InetAddress> getControllers(Node node) {
+        AbstractScheme scheme = schemes.get(activeScheme);
+        if (scheme == null)
+            return Collections.emptySet();
+        return scheme.getControllers(node);
+    }
 }
index d7b1968429c7de4fb0053b76b8dc135984fc69e3..5ba156ea7cb4690f2147915f1f29163d1b46fec4 100644 (file)
@@ -2,6 +2,7 @@ package org.opendaylight.controller.connectionmanager.scheme;
 
 import java.net.InetAddress;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.EnumSet;
 import java.util.HashSet;
 import java.util.Iterator;
@@ -134,7 +135,7 @@ public abstract class AbstractScheme {
 
     public Set<InetAddress> getControllers(Node node) {
         if (nodeConnections != null) return nodeConnections.get(node);
-        return null;
+        return Collections.emptySet();
     }
 
     public ConcurrentMap<Node, Set<InetAddress>> getNodeConnections() {