Connection Manager infrastructure supporting the Clustering Active-Active requirement. 48/748/1
authorMadhu Venugopal <vmadhu@cisco.com>
Tue, 30 Jul 2013 18:05:43 +0000 (11:05 -0700)
committerMadhu Venugopal <vmadhu@cisco.com>
Tue, 30 Jul 2013 18:05:43 +0000 (11:05 -0700)
This commit brings in the following changes :
1. Connection Manager as a functional module that runs above SAL providing common services to various south-bound plugins.
   The initial version of Connection Manager takes in Node as the only argument and let the Connection Management Scheme to determine the fate of the connection.
   Primary use-case that is addressed here is the HA/Clustering's Active-Active mode with the ability for Nodes to connect to different controller in a cluster.

2. SAL Connection service is implemented as a seperate bundle in order to show case the possibility of SAL being extensible and not a monolith piece of infra.

3. The only API that is exposed now is the isLocal functionality which can be used by Apps and Protocol plugins alike to take care of their individual funcationalities.

The corresponding Protocol-plugin changes will be pushed as a different commit.

Change-Id: I0ffad694b1a21ff4ec7e2139bb81a398412b9de0
Signed-off-by: Madhu Venugopal <vmadhu@cisco.com>
26 files changed:
opendaylight/connectionmanager/api/pom.xml [new file with mode: 0644]
opendaylight/connectionmanager/api/src/main/java/org/opendaylight/controller/connectionmanager/ConnectionMgmtScheme.java [new file with mode: 0644]
opendaylight/connectionmanager/api/src/main/java/org/opendaylight/controller/connectionmanager/IConnectionManager.java [new file with mode: 0644]
opendaylight/connectionmanager/implementation/pom.xml [new file with mode: 0644]
opendaylight/connectionmanager/implementation/src/main/java/org/opendaylight/controller/connectionmanager/internal/Activator.java [new file with mode: 0644]
opendaylight/connectionmanager/implementation/src/main/java/org/opendaylight/controller/connectionmanager/internal/ConnectionManager.java [new file with mode: 0644]
opendaylight/connectionmanager/implementation/src/main/java/org/opendaylight/controller/connectionmanager/internal/ConnectionMgmtEvent.java [new file with mode: 0644]
opendaylight/connectionmanager/implementation/src/main/java/org/opendaylight/controller/connectionmanager/internal/ConnectionMgmtEventType.java [new file with mode: 0644]
opendaylight/connectionmanager/implementation/src/main/java/org/opendaylight/controller/connectionmanager/scheme/AbstractScheme.java [new file with mode: 0644]
opendaylight/connectionmanager/implementation/src/main/java/org/opendaylight/controller/connectionmanager/scheme/AnyControllerScheme.java [new file with mode: 0644]
opendaylight/connectionmanager/implementation/src/main/java/org/opendaylight/controller/connectionmanager/scheme/ControllerConfig.java [new file with mode: 0644]
opendaylight/connectionmanager/implementation/src/main/java/org/opendaylight/controller/connectionmanager/scheme/LoadBalancedScheme.java [new file with mode: 0644]
opendaylight/connectionmanager/implementation/src/main/java/org/opendaylight/controller/connectionmanager/scheme/RoundRobinScheme.java [new file with mode: 0644]
opendaylight/connectionmanager/implementation/src/main/java/org/opendaylight/controller/connectionmanager/scheme/SchemeFactory.java [new file with mode: 0644]
opendaylight/connectionmanager/implementation/src/main/java/org/opendaylight/controller/connectionmanager/scheme/SingleControllerScheme.java [new file with mode: 0644]
opendaylight/distribution/opendaylight/pom.xml
opendaylight/sal/connection/api/pom.xml [new file with mode: 0644]
opendaylight/sal/connection/api/src/main/java/org/opendaylight/controller/sal/connection/ConnectionConstants.java [new file with mode: 0644]
opendaylight/sal/connection/api/src/main/java/org/opendaylight/controller/sal/connection/IConnectionListener.java [new file with mode: 0644]
opendaylight/sal/connection/api/src/main/java/org/opendaylight/controller/sal/connection/IConnectionService.java [new file with mode: 0644]
opendaylight/sal/connection/api/src/main/java/org/opendaylight/controller/sal/connection/IPluginInConnectionService.java [new file with mode: 0644]
opendaylight/sal/connection/api/src/main/java/org/opendaylight/controller/sal/connection/IPluginOutConnectionService.java [new file with mode: 0644]
opendaylight/sal/connection/implementation/pom.xml [new file with mode: 0644]
opendaylight/sal/connection/implementation/src/main/java/org/opendaylight/controller/sal/connection/implementation/internal/Activator.java [new file with mode: 0644]
opendaylight/sal/connection/implementation/src/main/java/org/opendaylight/controller/sal/connection/implementation/internal/ConnectionService.java [new file with mode: 0644]
opendaylight/sal/implementation/src/main/java/org/opendaylight/controller/sal/implementation/internal/Activator.java

diff --git a/opendaylight/connectionmanager/api/pom.xml b/opendaylight/connectionmanager/api/pom.xml
new file mode 100644 (file)
index 0000000..b27209d
--- /dev/null
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.opendaylight.controller</groupId>
+    <artifactId>commons.opendaylight</artifactId>
+    <version>1.4.0-SNAPSHOT</version>
+    <relativePath>../../commons/opendaylight</relativePath>
+  </parent>
+
+  <artifactId>connectionmanager</artifactId>
+  <version>0.1.0-SNAPSHOT</version>
+  <packaging>bundle</packaging>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.felix</groupId>
+        <artifactId>maven-bundle-plugin</artifactId>
+        <version>2.3.6</version>
+        <extensions>true</extensions>
+        <configuration>
+          <instructions>
+            <Import-Package>
+              org.opendaylight.controller.sal.core,
+              org.opendaylight.controller.sal.utils,
+              org.opendaylight.controller.sal.connection
+            </Import-Package>
+            <Export-Package>
+              org.opendaylight.controller.connectionmanager
+            </Export-Package>
+          </instructions>
+          <manifestLocation>${project.basedir}/META-INF</manifestLocation>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+  <dependencies>
+    <dependency>
+      <groupId>org.opendaylight.controller</groupId>
+      <artifactId>sal</artifactId>
+      <version>0.5.0-SNAPSHOT</version>
+    </dependency>
+    <dependency>
+      <groupId>org.opendaylight.controller</groupId>
+      <artifactId>sal.connection</artifactId>
+      <version>0.1.0-SNAPSHOT</version>
+    </dependency>
+  </dependencies>
+</project>
diff --git a/opendaylight/connectionmanager/api/src/main/java/org/opendaylight/controller/connectionmanager/ConnectionMgmtScheme.java b/opendaylight/connectionmanager/api/src/main/java/org/opendaylight/controller/connectionmanager/ConnectionMgmtScheme.java
new file mode 100644 (file)
index 0000000..49e77b2
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2013 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.controller.connectionmanager;
+
+/**
+ * Enumeration that represents the Connectivity Scheme / Algorithm for South-Bound nodes
+ * towards an Active-Active Clustered Controllers.
+ */
+public enum ConnectionMgmtScheme {
+    /**
+     * All the nodes are connected with a Single Controller.
+     * The SingleControllerScheme algorithm will determine that one controller to which all
+     * the nodes are connected with.
+     * This is like Active-Standby model from a South-Bound perspective.
+     */
+    SINGLE_CONTROLLER("All nodes connected with a Single Controller"),
+
+    /**
+     * Any node can be connected with any controller. But with just 1 master controller.
+     */
+    ANY_CONTROLLER_ONE_MASTER("Nodes can to connect with any controller in the cluster"),
+
+    /**
+     * Simple Round Robin Scheme that will let the nodes connect with each controller in
+     * Active-Active cluster in a round robin fashion.
+     */
+    ROUND_ROBIN("Each node is connected with individual Controller in Round-Robin fashion"),
+
+    /**
+     * Complex Load Balancing scheme that will let the nodes connect with controller based
+     * on the resource usage in each of the controllers in a cluster.
+     */
+    LOAD_BALANCED("Connect nodes to controllers based on the Controller Load"),
+
+    /**
+     * Container based scheme will let the nodes connect with controller based
+     * on the container configuration.
+     */
+    CONTAINER_BASED("Connect nodes to controllers based on Container they belong to");
+
+    private ConnectionMgmtScheme(String name) {
+        this.name = name;
+    }
+
+    private String name;
+
+    public String toString() {
+        return name;
+    }
+
+    public static ConnectionMgmtScheme fromString(String pName) {
+        for(ConnectionMgmtScheme p:ConnectionMgmtScheme.values()) {
+            if (p.toString().equals(pName)) {
+                return p;
+            }
+        }
+        return null;
+    }
+}
diff --git a/opendaylight/connectionmanager/api/src/main/java/org/opendaylight/controller/connectionmanager/IConnectionManager.java b/opendaylight/connectionmanager/api/src/main/java/org/opendaylight/controller/connectionmanager/IConnectionManager.java
new file mode 100644 (file)
index 0000000..12b1969
--- /dev/null
@@ -0,0 +1,94 @@
+
+/*
+ * Copyright (c) 2013 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.controller.connectionmanager;
+
+import java.net.InetAddress;
+import java.util.Map;
+import java.util.Set;
+
+import org.opendaylight.controller.sal.connection.ConnectionConstants;
+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.
+ *
+ * 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.
+     *
+     * @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.
+     *
+     * @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.
+     *
+     * @return Set<Node> Set of Nodes connected to this controller.
+     */
+    public Set<Node> getLocalNodes();
+
+    /**
+     * Method to test if a node is local to a controller.
+     *
+     * @return true if node is local to this controller. false otherwise.
+     */
+    public boolean isLocal(Node node);
+
+    /**
+     * Disconnect a Node from the controller.
+     *
+     * @return Status of the disconnect Operation.
+     */
+    public Status disconnect(Node node);
+
+    /**
+     * 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...
+     *
+     *  @return Node Node connected to.
+     */
+    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...
+     *
+     *  @return Status of the Connect Operation.
+     */
+    public Node connect(String type, String connectionIdentifier, Map<ConnectionConstants, String> params);
+}
diff --git a/opendaylight/connectionmanager/implementation/pom.xml b/opendaylight/connectionmanager/implementation/pom.xml
new file mode 100644 (file)
index 0000000..2c92cf2
--- /dev/null
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.opendaylight.controller</groupId>
+    <artifactId>commons.opendaylight</artifactId>
+    <version>1.4.0-SNAPSHOT</version>
+    <relativePath>../../commons/opendaylight</relativePath>
+  </parent>
+
+  <artifactId>connectionmanager.implementation</artifactId>
+  <version>0.1.0-SNAPSHOT</version>
+  <packaging>bundle</packaging>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.felix</groupId>
+        <artifactId>maven-bundle-plugin</artifactId>
+        <version>2.3.6</version>
+        <extensions>true</extensions>
+        <configuration>
+          <instructions>
+            <Import-Package>
+              org.opendaylight.controller.connectionmanager,
+              org.opendaylight.controller.clustering.services,
+              org.opendaylight.controller.sal.utils,
+              org.opendaylight.controller.sal.core,
+              org.opendaylight.controller.sal.connection,
+              org.opendaylight.controller.sal.inventory,
+              org.eclipse.osgi.framework.console,
+              org.osgi.framework,
+              org.slf4j,
+              org.apache.felix.dm
+            </Import-Package>
+            <Export-Package>
+            </Export-Package>
+            <Bundle-Activator>
+              org.opendaylight.controller.connectionmanager.internal.Activator
+            </Bundle-Activator>
+          </instructions>
+          <manifestLocation>${project.basedir}/META-INF</manifestLocation>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+  <dependencies>
+    <dependency>
+      <groupId>org.opendaylight.controller</groupId>
+      <artifactId>clustering.services</artifactId>
+      <version>0.4.0-SNAPSHOT</version>
+    </dependency>
+    <dependency>
+      <groupId>org.opendaylight.controller</groupId>
+      <artifactId>connectionmanager</artifactId>
+      <version>0.1.0-SNAPSHOT</version>
+    </dependency>
+    <dependency>
+      <groupId>org.opendaylight.controller</groupId>
+      <artifactId>sal</artifactId>
+      <version>0.5.0-SNAPSHOT</version>
+    </dependency>
+    <dependency>
+      <groupId>org.opendaylight.controller</groupId>
+      <artifactId>sal.connection</artifactId>
+      <version>0.1.0-SNAPSHOT</version>
+    </dependency>
+  </dependencies>
+</project>
diff --git a/opendaylight/connectionmanager/implementation/src/main/java/org/opendaylight/controller/connectionmanager/internal/Activator.java b/opendaylight/connectionmanager/implementation/src/main/java/org/opendaylight/controller/connectionmanager/internal/Activator.java
new file mode 100644 (file)
index 0000000..5ebbfe2
--- /dev/null
@@ -0,0 +1,102 @@
+
+/*
+ * Copyright (c) 2013 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.controller.connectionmanager.internal;
+
+import java.util.Dictionary;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.Set;
+
+import org.opendaylight.controller.clustering.services.ICacheUpdateAware;
+import org.opendaylight.controller.clustering.services.IClusterGlobalServices;
+import org.opendaylight.controller.clustering.services.ICoordinatorChangeAware;
+import org.opendaylight.controller.connectionmanager.IConnectionManager;
+import org.opendaylight.controller.sal.connection.IConnectionListener;
+import org.opendaylight.controller.sal.connection.IConnectionService;
+import org.apache.felix.dm.Component;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.opendaylight.controller.sal.core.ComponentActivatorAbstractBase;
+import org.opendaylight.controller.sal.inventory.IListenInventoryUpdates;
+
+public class Activator extends ComponentActivatorAbstractBase {
+    protected static final Logger logger = LoggerFactory
+            .getLogger(Activator.class);
+
+    /**
+     * Function called when the activator starts just after some
+     * initializations are done by the
+     * ComponentActivatorAbstractBase.
+     *
+     */
+    public void init() {
+    }
+
+    /**
+     * Function called when the activator stops just before the
+     * cleanup done by ComponentActivatorAbstractBase
+     *
+     */
+    public void destroy() {
+    }
+
+    /**
+     * Method which tells how many Global implementations are
+     * supported by the bundle. This way we can tune the number of
+     * components created. This components will be created ONLY at the
+     * time of bundle startup and will be destroyed only at time of
+     * bundle destruction, this is the major difference with the
+     * implementation retrieved via getImplementations where all of
+     * them are assumed to be in a container!
+     *
+     *
+     * @return The list of implementations the bundle will support,
+     * in Global version
+     */
+    protected Object[] getGlobalImplementations() {
+        Object[] res = { ConnectionManager.class };
+        return res;
+    }
+
+    /**
+     * Configure the dependency for a given instance Global
+     *
+     * @param c Component assigned for this instance, this will be
+     * what will be used for configuration
+     * @param imp implementation to be configured
+     * @param containerName container on which the configuration happens
+     */
+    protected void configureGlobalInstance(Component c, Object imp) {
+        if (imp.equals(ConnectionManager.class)) {
+            Dictionary<String, Object> props = new Hashtable<String, Object>();
+            Set<String> propSet = new HashSet<String>();
+            propSet.add("connectionmanager.nodeconnections");
+            props.put("cachenames", propSet);
+            props.put("scope", "Global");
+
+            // export the service
+            c.setInterface(new String[] { IConnectionManager.class.getName(),
+                                          IConnectionListener.class.getName(),
+                                          ICoordinatorChangeAware.class.getName(),
+                                          IListenInventoryUpdates.class.getName(),
+                                          ICacheUpdateAware.class.getName()},
+                                          props);
+
+            c.add(createServiceDependency()
+                    .setService(IClusterGlobalServices.class)
+                    .setCallbacks("setClusterServices", "unsetClusterServices")
+                    .setRequired(true));
+
+            c.add(createServiceDependency().setService(IConnectionService.class)
+                    .setCallbacks("setConnectionService", "unsetConnectionService")
+                    .setRequired(true));
+        }
+    }
+}
diff --git a/opendaylight/connectionmanager/implementation/src/main/java/org/opendaylight/controller/connectionmanager/internal/ConnectionManager.java b/opendaylight/connectionmanager/implementation/src/main/java/org/opendaylight/controller/connectionmanager/internal/ConnectionManager.java
new file mode 100644 (file)
index 0000000..c4b7d4f
--- /dev/null
@@ -0,0 +1,342 @@
+
+/*
+ * Copyright (c) 2013 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
+ */
+
+/**
+ * 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.
+ *
+ * But this (and its corresponding implementation) component can also be used for
+ * basic connectivity mechansims for various south-bound plugins.
+ */
+
+package org.opendaylight.controller.connectionmanager.internal;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.LinkedBlockingQueue;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.eclipse.osgi.framework.console.CommandInterpreter;
+import org.eclipse.osgi.framework.console.CommandProvider;
+import org.opendaylight.controller.clustering.services.ICacheUpdateAware;
+import org.opendaylight.controller.clustering.services.IClusterGlobalServices;
+import org.opendaylight.controller.clustering.services.ICoordinatorChangeAware;
+import org.opendaylight.controller.connectionmanager.ConnectionMgmtScheme;
+import org.opendaylight.controller.connectionmanager.IConnectionManager;
+import org.opendaylight.controller.connectionmanager.scheme.AbstractScheme;
+import org.opendaylight.controller.connectionmanager.scheme.SchemeFactory;
+import org.opendaylight.controller.sal.connection.ConnectionConstants;
+import org.opendaylight.controller.sal.connection.IConnectionListener;
+import org.opendaylight.controller.sal.connection.IConnectionService;
+import org.opendaylight.controller.sal.core.Node;
+import org.opendaylight.controller.sal.core.NodeConnector;
+import org.opendaylight.controller.sal.core.Property;
+import org.opendaylight.controller.sal.core.UpdateType;
+import org.opendaylight.controller.sal.inventory.IListenInventoryUpdates;
+import org.opendaylight.controller.sal.utils.Status;
+import org.opendaylight.controller.sal.utils.StatusCode;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.FrameworkUtil;
+
+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;
+    private IConnectionService connectionService;
+    private Thread connectionEventThread;
+    private BlockingQueue<ConnectionMgmtEvent> connectionEvents;
+
+    public void setClusterServices(IClusterGlobalServices i) {
+        this.clusterServices = i;
+    }
+
+    public void unsetClusterServices(IClusterGlobalServices i) {
+        if (this.clusterServices == i) {
+            this.clusterServices = null;
+        }
+    }
+
+    public void setConnectionService(IConnectionService i) {
+        this.connectionService = i;
+    }
+
+    public void unsetConnectionService(IConnectionService i) {
+        if (this.connectionService == i) {
+            this.connectionService = null;
+        }
+    }
+
+    public void started() {
+        connectionEventThread = new Thread(new EventHandler(), "ConnectionEvent Thread");
+        connectionEventThread.start();
+
+        registerWithOSGIConsole();
+        notifyClusterViewChanged();
+    }
+
+    public void init() {
+        this.connectionEvents = new LinkedBlockingQueue<ConnectionMgmtEvent>();
+        schemes = new ConcurrentHashMap<ConnectionMgmtScheme, AbstractScheme>();
+        for (ConnectionMgmtScheme scheme : ConnectionMgmtScheme.values()) {
+            AbstractScheme schemeImpl = SchemeFactory.getScheme(scheme, clusterServices);
+            if (schemeImpl != null) schemes.put(scheme, schemeImpl);
+        }
+    }
+
+    public void stop() {
+        connectionEventThread.interrupt();
+        Set<Node> localNodes = getLocalNodes();
+        if (localNodes != null) {
+            AbstractScheme scheme = schemes.get(activeScheme);
+            for (Node localNode : localNodes) {
+                connectionService.disconnect(localNode);
+                if (scheme != null) scheme.removeNode(localNode);
+            }
+        }
+    }
+
+    @Override
+    public ConnectionMgmtScheme getActiveScheme() {
+        return activeScheme;
+    }
+
+    @Override
+    public Set<Node> getNodes(InetAddress controller) {
+        AbstractScheme scheme = schemes.get(activeScheme);
+        if (scheme == null) return null;
+        return scheme.getNodes(controller);
+    }
+
+    @Override
+    public Set<Node> getLocalNodes() {
+        AbstractScheme scheme = schemes.get(activeScheme);
+        if (scheme == null) return null;
+        return scheme.getNodes();
+    }
+
+    @Override
+    public boolean isLocal(Node node) {
+        AbstractScheme scheme = schemes.get(activeScheme);
+        if (scheme == null) return false;
+        return scheme.isLocal(node);
+    }
+
+    @Override
+    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;
+        switch (type) {
+        case ADDED:
+            scheme.addNode(node);
+            break;
+        case REMOVED:
+            scheme.removeNode(node);
+            break;
+        default:
+                break;
+        }
+    }
+
+    @Override
+    public void updateNodeConnector(NodeConnector nodeConnector,
+            UpdateType type, Set<Property> props) {
+        logger.debug("updateNodeConnector: {} type {} props {}", nodeConnector, type, props);
+        AbstractScheme scheme = schemes.get(activeScheme);
+        if (scheme == null) return;
+        switch (type) {
+        case ADDED:
+            scheme.addNode(nodeConnector.getNode());
+            break;
+        default:
+                break;
+        }
+    }
+
+    @Override
+    public void coordinatorChanged() {
+        AbstractScheme scheme = schemes.get(activeScheme);
+        if (scheme == null) return;
+        scheme.handleClusterViewChanged();
+        notifyClusterViewChanged();
+    }
+
+    @Override
+    public Node connect(String connectionIdentifier, Map<ConnectionConstants, String> params) {
+        if (connectionService == null) return null;
+        return connectionService.connect(connectionIdentifier, params);
+    }
+
+    @Override
+    public Node connect(String type, String connectionIdentifier, Map<ConnectionConstants, String> params) {
+        if (connectionService == null) return null;
+        return connectionService.connect(type, connectionIdentifier, params);
+    }
+
+    @Override
+    public Status disconnect (Node node) {
+        if (connectionService == null) return new Status(StatusCode.NOSERVICE);
+        return connectionService.disconnect(node);
+    }
+
+    @Override
+    public void entryCreated(Node key, String cacheName, boolean originLocal) {
+        AbstractScheme scheme = schemes.get(activeScheme);
+        logger.debug("Created : {} cache : {} existingValue : {}", key, cacheName, scheme.getNodeConnections().get(key));
+    }
+
+    /*
+     * 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;
+        Set<InetAddress> existingControllers = existingConnections.get(node);
+        if (existingControllers != null) {
+            logger.debug("Processing Update for : {} NewControllers : {} existingControllers : {}", node,
+                    newControllers.toString(), existingControllers.toString());
+            if (newControllers.size() < existingControllers.size()) {
+                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());
+        }
+        existingConnections.put(node, newControllers);
+    }
+
+    @Override
+    public void entryDeleted(Node key, String cacheName, boolean originLocal) {
+        if (originLocal) return;
+        logger.debug("Deleted : {} cache : {}", key, cacheName);
+        notifyNodeDisconnectedEvent(key);
+    }
+
+    private void enqueueConnectionEvent(ConnectionMgmtEvent event) {
+        try {
+            if (!connectionEvents.contains(event)) {
+                this.connectionEvents.put(event);
+            }
+        } catch (InterruptedException e) {
+            logger.debug("enqueueConnectionEvent caught Interrupt Exception for event {}", event);
+        }
+    }
+
+    private void notifyClusterViewChanged() {
+        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);
+        enqueueConnectionEvent(event);
+    }
+
+    /*
+     * this thread monitors the connectionEvent queue for new incoming events from
+     */
+    private class EventHandler implements Runnable {
+        @Override
+        public void run() {
+
+            while (true) {
+                try {
+                    ConnectionMgmtEvent ev = connectionEvents.take();
+                    ConnectionMgmtEventType eType = ev.getEvent();
+                    switch (eType) {
+                    case NODE_DISCONNECTED_FROM_MASTER:
+                        Node node = (Node)ev.getData();
+                        connectionService.notifyNodeDisconnectFromMaster(node);
+                        break;
+                    case CLUSTER_VIEW_CHANGED:
+                        connectionService.notifyClusterViewChanged();
+                        break;
+                    default:
+                        logger.error("Unknown Connection event {}", eType.ordinal());
+                    }
+                } catch (InterruptedException e) {
+                    connectionEvents.clear();
+                    return;
+                }
+            }
+        }
+    }
+
+    private void registerWithOSGIConsole() {
+        BundleContext bundleContext = FrameworkUtil.getBundle(this.getClass())
+                .getBundleContext();
+        bundleContext.registerService(CommandProvider.class.getName(), this,
+                null);
+    }
+
+    public void _scheme (CommandInterpreter ci) {
+        String schemeStr = ci.nextArgument();
+        if (schemeStr == null) {
+            ci.println("Please enter valid Scheme name");
+            ci.println("Current Scheme : " + activeScheme.name());
+            return;
+        }
+        ConnectionMgmtScheme scheme = ConnectionMgmtScheme.valueOf(schemeStr);
+        if (scheme == null) {
+            ci.println("Please enter a valid Scheme name");
+            return;
+        }
+        activeScheme = scheme;
+    }
+
+    public void _printNodes (CommandInterpreter ci) {
+        String controller = ci.nextArgument();
+        if (controller == null) {
+            ci.println("Nodes connected to this controller : ");
+            if (this.getLocalNodes() == null) ci.println("None");
+            else ci.println(this.getLocalNodes().toString());
+            return;
+        }
+        try {
+            InetAddress address = InetAddress.getByName(controller);
+            ci.println("Nodes connected to controller "+controller);
+            if (this.getNodes(address) == null) ci.println("None");
+            else ci.println(this.getNodes(address).toString());
+            return;
+        } catch (UnknownHostException e) {
+            e.printStackTrace();
+        }
+    }
+
+    @Override
+    public String getHelp() {
+        StringBuffer help = new StringBuffer();
+        help.append("---Connection Manager---\n");
+        help.append("\t scheme [<name>]                      - Print / Set scheme\n");
+        help.append("\t printNodes [<controller>]            - Print connected nodes\n");
+        return help.toString();
+    }
+}
diff --git a/opendaylight/connectionmanager/implementation/src/main/java/org/opendaylight/controller/connectionmanager/internal/ConnectionMgmtEvent.java b/opendaylight/connectionmanager/implementation/src/main/java/org/opendaylight/controller/connectionmanager/internal/ConnectionMgmtEvent.java
new file mode 100644 (file)
index 0000000..f07672d
--- /dev/null
@@ -0,0 +1,47 @@
+
+package org.opendaylight.controller.connectionmanager.internal;
+
+public class ConnectionMgmtEvent {
+    ConnectionMgmtEventType event;
+    Object data;
+    public ConnectionMgmtEvent(ConnectionMgmtEventType event, Object data) {
+        this.event = event;
+        this.data = data;
+    }
+    public ConnectionMgmtEventType getEvent() {
+        return event;
+    }
+    public Object getData() {
+        return data;
+    }
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + ((data == null) ? 0 : data.hashCode());
+        result = prime * result + ((event == null) ? 0 : event.hashCode());
+        return result;
+    }
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj)
+            return true;
+        if (obj == null)
+            return false;
+        if (getClass() != obj.getClass())
+            return false;
+        ConnectionMgmtEvent other = (ConnectionMgmtEvent) obj;
+        if (data == null) {
+            if (other.data != null)
+                return false;
+        } else if (!data.equals(other.data))
+            return false;
+        if (event != other.event)
+            return false;
+        return true;
+    }
+    @Override
+    public String toString() {
+        return "ConnectionMgmtEvent [event=" + event + ", data=" + data + "]";
+    }
+}
diff --git a/opendaylight/connectionmanager/implementation/src/main/java/org/opendaylight/controller/connectionmanager/internal/ConnectionMgmtEventType.java b/opendaylight/connectionmanager/implementation/src/main/java/org/opendaylight/controller/connectionmanager/internal/ConnectionMgmtEventType.java
new file mode 100644 (file)
index 0000000..98399b0
--- /dev/null
@@ -0,0 +1,25 @@
+package org.opendaylight.controller.connectionmanager.internal;
+
+public enum ConnectionMgmtEventType {
+    NODE_DISCONNECTED_FROM_MASTER("Node is disconnected from master"),
+    CLUSTER_VIEW_CHANGED("Cluster Composition changed");
+
+    private ConnectionMgmtEventType(String name) {
+        this.name = name;
+    }
+
+    private String name;
+
+    public String toString() {
+        return name;
+    }
+
+    public static ConnectionMgmtEventType fromString(String pName) {
+        for(ConnectionMgmtEventType p:ConnectionMgmtEventType.values()) {
+            if (p.toString().equals(pName)) {
+                return p;
+            }
+        }
+        return null;
+    }
+}
diff --git a/opendaylight/connectionmanager/implementation/src/main/java/org/opendaylight/controller/connectionmanager/scheme/AbstractScheme.java b/opendaylight/connectionmanager/implementation/src/main/java/org/opendaylight/controller/connectionmanager/scheme/AbstractScheme.java
new file mode 100644 (file)
index 0000000..a490916
--- /dev/null
@@ -0,0 +1,289 @@
+package org.opendaylight.controller.connectionmanager.scheme;
+
+import java.net.InetAddress;
+import java.util.ArrayList;
+import java.util.EnumSet;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import org.opendaylight.controller.clustering.services.CacheConfigException;
+import org.opendaylight.controller.clustering.services.CacheExistException;
+import org.opendaylight.controller.clustering.services.IClusterGlobalServices;
+import org.opendaylight.controller.clustering.services.IClusterServices;
+import org.opendaylight.controller.connectionmanager.ConnectionMgmtScheme;
+import org.opendaylight.controller.sal.core.Node;
+import org.opendaylight.controller.sal.utils.Status;
+import org.opendaylight.controller.sal.utils.StatusCode;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public abstract class AbstractScheme {
+    private static final Logger log = LoggerFactory.getLogger(AbstractScheme.class);
+    protected IClusterGlobalServices clusterServices = null;
+    /*
+     * A more natural Map data-structure is to have a Key=Controller IP-address with value = a set of Nodes.
+     * But, such a data-structure results in some complex event processing during the Cluster operations
+     * to sync up the Connection states.
+     *
+     * A data-structure with Node as the key and set of controllers provides a good balance
+     * between the needed functionality and simpler clustering implementation for Connection Manager.
+     */
+    protected ConcurrentMap <Node, Set<InetAddress>> nodeConnections;
+    protected abstract boolean isConnectionAllowedInternal(Node node);
+    private String name;
+
+    protected AbstractScheme(IClusterGlobalServices clusterServices, ConnectionMgmtScheme type) {
+        this.clusterServices = clusterServices;
+        if (type != null) name = type.name();
+        else name = "UNKNOWN";
+        if (clusterServices != null) {
+            allocateCaches();
+            retrieveCaches();
+        }
+    }
+
+    protected ConcurrentMap <InetAddress, Set<Node>> getControllerToNodesMap() {
+        ConcurrentMap <InetAddress, Set<Node>> controllerNodesMap = new ConcurrentHashMap <InetAddress, Set<Node>>();
+        for (Node node : nodeConnections.keySet()) {
+            Set<InetAddress> controllers = nodeConnections.get(node);
+            if (controllers == null) continue;
+            for (InetAddress controller : controllers) {
+                Set<Node> nodes = controllerNodesMap.get(controller);
+                if (nodes == null) {
+                    nodes = new HashSet<Node>();
+                }
+                nodes.add(node);
+                controllerNodesMap.put(controller, nodes);
+            }
+        }
+        return controllerNodesMap;
+    }
+
+    public boolean isConnectionAllowed (Node node) {
+        if (clusterServices == null || nodeConnections == null) {
+            return false;
+        }
+
+        return isConnectionAllowedInternal(node);
+    }
+
+    @SuppressWarnings("deprecation")
+    public void handleClusterViewChanged() {
+        List<InetAddress> controllers = clusterServices.getClusteredControllers();
+        ConcurrentMap <InetAddress, Set<Node>> controllerNodesMap = getControllerToNodesMap();
+        List<InetAddress> toRemove = new ArrayList<InetAddress>();
+        for (InetAddress c : controllerNodesMap.keySet()) {
+            if (!controllers.contains(c)) {
+                toRemove.add(c);
+            }
+        }
+
+        for (InetAddress c : toRemove) {
+            log.debug("Removing Controller : {} from the Connections table", c);
+            for (Iterator<Node> nodeIterator = nodeConnections.keySet().iterator();nodeIterator.hasNext();) {
+                Node node = nodeIterator.next();
+                Set <InetAddress> oldControllers = nodeConnections.get(node);
+                Set <InetAddress> newControllers = new HashSet<InetAddress>(oldControllers);
+                if (newControllers.remove(c)) {
+                    boolean replaced = false;
+                    try {
+                        replaced = nodeConnections.replace(node, oldControllers, newControllers);
+                    } catch (Exception e) {
+                        log.debug("Replace exception : ", e);
+                        replaced = false;
+                    }
+                    if (!replaced) {
+                        try {
+                            Thread.sleep(10);
+                        } catch (InterruptedException e) {
+                        }
+                        handleClusterViewChanged();
+                    }
+                }
+            }
+        }
+    }
+
+    public Set<Node> getNodes(InetAddress controller) {
+        if (nodeConnections == null) return null;
+        ConcurrentMap <InetAddress, Set<Node>> controllerNodesMap = getControllerToNodesMap();
+        return controllerNodesMap.get(controller);
+    }
+
+    @SuppressWarnings("deprecation")
+    public Set<Node> getNodes() {
+        return getNodes(clusterServices.getMyAddress());
+    }
+
+    public Set<InetAddress> getControllers(Node node) {
+        if (nodeConnections != null) return nodeConnections.get(node);
+        return null;
+    }
+
+    public ConcurrentMap<Node, Set<InetAddress>> getNodeConnections() {
+        return nodeConnections;
+    }
+
+    @SuppressWarnings("deprecation")
+    public boolean isLocal(Node node) {
+        if (nodeConnections == null) return false;
+        InetAddress myController = clusterServices.getMyAddress();
+        Set<InetAddress> controllers = nodeConnections.get(node);
+        return (controllers != null && controllers.contains(myController));
+    }
+
+    @SuppressWarnings("deprecation")
+    public Status removeNode (Node node) {
+        return removeNodeFromController(node, clusterServices.getMyAddress());
+    }
+
+    protected Status removeNodeFromController (Node node, InetAddress controller) {
+        if (node == null || controller == null) {
+            return new Status(StatusCode.BADREQUEST);
+        }
+
+        if (clusterServices == null || nodeConnections == null) {
+            return new Status(StatusCode.SUCCESS);
+        }
+
+        Set<InetAddress> oldControllers = nodeConnections.get(node);
+
+        if (oldControllers != null && oldControllers.contains(controller)) {
+            Set<InetAddress> newControllers = new HashSet<InetAddress>(oldControllers);
+            if (newControllers.remove(controller)) {
+                if (newControllers.size() > 0) {
+                    boolean replaced = false;
+                    try {
+                    replaced = nodeConnections.replace(node, oldControllers, newControllers);
+                    } catch (Exception e) {
+                        log.debug("Replace exception : ", e);
+                        replaced = false;
+                    }
+                    if (!replaced) {
+                        try {
+                            Thread.sleep(10);
+                        } catch (InterruptedException e) {
+                        }
+                        return removeNodeFromController(node, controller);
+                    }
+                } else {
+                    nodeConnections.remove(node);
+                }
+            }
+        }
+        return new Status(StatusCode.SUCCESS);
+
+    }
+
+    /*
+     * A few race-conditions were seen with the Clustered caches in putIfAbsent and replace
+     * functions. Leaving a few debug logs behind to assist in debugging if strange things happen.
+     */
+    private Status putNodeToController (Node node, InetAddress controller) {
+        if (clusterServices == null || nodeConnections == null) {
+            return new Status(StatusCode.SUCCESS);
+        }
+        log.debug("Trying to Put {} to {}", controller.getHostAddress(), node.toString());
+
+        Set <InetAddress> oldControllers = nodeConnections.get(node);
+        Set <InetAddress> newControllers = null;
+        if (oldControllers == null) {
+            newControllers = new HashSet<InetAddress>();
+        } else {
+            if (oldControllers.size() > 0 && !isConnectionAllowed(node)) {
+                /*
+                 * In certain race conditions, the putIfAbsent fails to be atomic.
+                 * This check is added to identify such cases and report an warning
+                 * for debugging.
+                 */
+                log.warn("States Exists for {} : {}", node, oldControllers.toString());
+            }
+            newControllers = new HashSet<InetAddress>(oldControllers);
+        }
+        newControllers.add(controller);
+
+        if (nodeConnections.putIfAbsent(node, newControllers) != null) {
+            log.debug("PutIfAbsent failed {} to {}", controller.getHostAddress(), node.toString());
+            /*
+             * This check is needed again to take care of the case where some schemes
+             * would not allow nodes to be connected to multiple controllers.
+             * Hence, if putIfAbsent fails, that means, some other controller is competing
+             * with this controller to take hold of a Node.
+             */
+            if (isConnectionAllowed(node)) {
+                log.debug("Trying to replace old={} with new={} for {} to {}", oldControllers.toString(), newControllers.toString(),
+                        controller.getHostAddress(), node.toString());
+                if (!nodeConnections.replace(node, oldControllers, newControllers)) {
+                    try {
+                        Thread.sleep(10);
+                    } catch (InterruptedException e) {
+                    }
+                    log.debug("Replace failed... old={} with new={} for {} to {}", oldControllers.toString(), newControllers.toString(),
+                            controller.getHostAddress(), node.toString());
+                    return putNodeToController(node, controller);
+                } else {
+                    log.debug("Replace successful old={} with new={} for {} to {}", oldControllers.toString(), newControllers.toString(),
+                            controller.getHostAddress(), node.toString());
+                }
+            } else {
+                return new Status(StatusCode.CONFLICT);
+            }
+        } else {
+            log.debug("Added {} to {}", controller.getHostAddress(), node.toString());
+        }
+        return new Status(StatusCode.SUCCESS);
+    }
+
+    public Status addNode (Node node, InetAddress controller) {
+        if (node == null || controller == null) {
+            return new Status(StatusCode.BADREQUEST);
+        }
+        if (isLocal(node)) return new Status(StatusCode.SUCCESS);
+        if (isConnectionAllowed(node)) {
+            return putNodeToController(node, controller);
+        } else {
+            return new Status(StatusCode.NOTALLOWED);
+        }
+    }
+
+    @SuppressWarnings("deprecation")
+    public Status addNode (Node node) {
+        return addNode(node, clusterServices.getMyAddress());
+    }
+
+    @SuppressWarnings({ "unchecked", "deprecation" })
+    private void retrieveCaches() {
+        if (this.clusterServices == null) {
+            log.error("un-initialized clusterServices, can't retrieve cache");
+            return;
+        }
+
+        nodeConnections = (ConcurrentMap<Node, Set<InetAddress>>) clusterServices.getCache("connectionmanager."+name+".nodeconnections");
+
+        if (nodeConnections == null) {
+            log.error("\nFailed to get caches");
+        }
+    }
+
+    @SuppressWarnings("deprecation")
+    private void allocateCaches() {
+        if (this.clusterServices == null) {
+            log.error("un-initialized clusterServices, can't create cache");
+            return;
+        }
+
+        try {
+            clusterServices.createCache("connectionmanager."+name+".nodeconnections", EnumSet.of(IClusterServices.cacheMode.NON_TRANSACTIONAL));
+        } catch (CacheExistException cee) {
+            log.error("\nCache already exists - destroy and recreate if needed");
+        } catch (CacheConfigException cce) {
+            log.error("\nCache configuration invalid - check cache mode");
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+}
diff --git a/opendaylight/connectionmanager/implementation/src/main/java/org/opendaylight/controller/connectionmanager/scheme/AnyControllerScheme.java b/opendaylight/connectionmanager/implementation/src/main/java/org/opendaylight/controller/connectionmanager/scheme/AnyControllerScheme.java
new file mode 100644 (file)
index 0000000..02282c3
--- /dev/null
@@ -0,0 +1,34 @@
+package org.opendaylight.controller.connectionmanager.scheme;
+
+import java.net.InetAddress;
+import java.util.Set;
+import org.opendaylight.controller.clustering.services.IClusterGlobalServices;
+import org.opendaylight.controller.connectionmanager.ConnectionMgmtScheme;
+import org.opendaylight.controller.sal.core.Node;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+class AnyControllerScheme extends AbstractScheme {
+    private static final Logger logger = LoggerFactory.getLogger(AnyControllerScheme.class);
+    private static AbstractScheme myScheme= null;
+
+    protected AnyControllerScheme(IClusterGlobalServices clusterServices) {
+        super(clusterServices, ConnectionMgmtScheme.ANY_CONTROLLER_ONE_MASTER);
+    }
+
+    public static AbstractScheme getScheme(IClusterGlobalServices clusterServices) {
+        if (myScheme == null) {
+            myScheme = new AnyControllerScheme(clusterServices);
+        }
+        return myScheme;
+    }
+
+    @SuppressWarnings("deprecation")
+    @Override
+    public boolean isConnectionAllowedInternal(Node node) {
+        if (nodeConnections == null) return true;
+        Set <InetAddress> controllers = nodeConnections.get(node);
+        if (controllers == null || controllers.size() == 0) return true;
+        return (controllers.size() == 1 && controllers.contains(clusterServices.getMyAddress()));
+    }
+}
diff --git a/opendaylight/connectionmanager/implementation/src/main/java/org/opendaylight/controller/connectionmanager/scheme/ControllerConfig.java b/opendaylight/connectionmanager/implementation/src/main/java/org/opendaylight/controller/connectionmanager/scheme/ControllerConfig.java
new file mode 100644 (file)
index 0000000..4db80a5
--- /dev/null
@@ -0,0 +1,62 @@
+package org.opendaylight.controller.connectionmanager.scheme;
+
+import java.net.InetAddress;
+
+/**
+ * Configuration object that can be used to prioritize or add weight to a given controller.
+ * This can be potentially used by the Connection management scheme algorithms.
+ *
+ * This is currently not used.
+ *
+ */
+public class ControllerConfig {
+    private InetAddress controllerId;
+    private int priority;
+    private int weight;
+
+    public ControllerConfig(InetAddress controllerId, int priority, int weight) {
+        this.controllerId = controllerId;
+        this.priority = priority;
+        this.weight = weight;
+    }
+
+    public InetAddress getControllerId() {
+        return controllerId;
+    }
+    public int getPriority() {
+        return priority;
+    }
+    public int getWeight() {
+        return weight;
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result
+                + ((controllerId == null) ? 0 : controllerId.hashCode());
+        return result;
+    }
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj)
+            return true;
+        if (obj == null)
+            return false;
+        if (getClass() != obj.getClass())
+            return false;
+        ControllerConfig other = (ControllerConfig) obj;
+        if (controllerId == null) {
+            if (other.controllerId != null)
+                return false;
+        } else if (!controllerId.equals(other.controllerId))
+            return false;
+        return true;
+    }
+    @Override
+    public String toString() {
+        return "ControllerConfig [controllerId=" + controllerId + ", priority="
+                + priority + ", weight=" + weight + "]";
+    }
+}
diff --git a/opendaylight/connectionmanager/implementation/src/main/java/org/opendaylight/controller/connectionmanager/scheme/LoadBalancedScheme.java b/opendaylight/connectionmanager/implementation/src/main/java/org/opendaylight/controller/connectionmanager/scheme/LoadBalancedScheme.java
new file mode 100644 (file)
index 0000000..a80494b
--- /dev/null
@@ -0,0 +1,30 @@
+package org.opendaylight.controller.connectionmanager.scheme;
+
+
+import org.opendaylight.controller.clustering.services.IClusterGlobalServices;
+import org.opendaylight.controller.connectionmanager.ConnectionMgmtScheme;
+import org.opendaylight.controller.sal.core.Node;
+
+/**
+ * Load Balancing scheme will let the nodes connect with controller based
+ * on the resource usage in each of the controllers in a cluster.
+ *
+ * Incomplete and Currently not used.
+ */
+
+class LoadBalancedScheme extends AbstractScheme {
+
+    protected LoadBalancedScheme(IClusterGlobalServices clusterServices) {
+        super(clusterServices, ConnectionMgmtScheme.LOAD_BALANCED);
+    }
+
+    public static AbstractScheme getScheme(IClusterGlobalServices clusterServices) {
+        return null;
+    }
+
+    @Override
+    public boolean isConnectionAllowedInternal(Node node) {
+        return false;
+    }
+
+}
diff --git a/opendaylight/connectionmanager/implementation/src/main/java/org/opendaylight/controller/connectionmanager/scheme/RoundRobinScheme.java b/opendaylight/connectionmanager/implementation/src/main/java/org/opendaylight/controller/connectionmanager/scheme/RoundRobinScheme.java
new file mode 100644 (file)
index 0000000..4a498dc
--- /dev/null
@@ -0,0 +1,23 @@
+package org.opendaylight.controller.connectionmanager.scheme;
+
+import org.opendaylight.controller.clustering.services.IClusterGlobalServices;
+import org.opendaylight.controller.connectionmanager.ConnectionMgmtScheme;
+import org.opendaylight.controller.sal.core.Node;
+
+class RoundRobinScheme extends AbstractScheme {
+    protected RoundRobinScheme(IClusterGlobalServices clusterServices) {
+        super(clusterServices, ConnectionMgmtScheme.ROUND_ROBIN);
+        // TODO Auto-generated constructor stub
+    }
+
+    public static AbstractScheme getScheme(IClusterGlobalServices clusterServices) {
+        return null;
+    }
+
+    @Override
+    public boolean isConnectionAllowedInternal(Node node) {
+        // TODO Auto-generated method stub
+        return false;
+    }
+
+}
diff --git a/opendaylight/connectionmanager/implementation/src/main/java/org/opendaylight/controller/connectionmanager/scheme/SchemeFactory.java b/opendaylight/connectionmanager/implementation/src/main/java/org/opendaylight/controller/connectionmanager/scheme/SchemeFactory.java
new file mode 100644 (file)
index 0000000..7e13745
--- /dev/null
@@ -0,0 +1,19 @@
+package org.opendaylight.controller.connectionmanager.scheme;
+
+import org.opendaylight.controller.clustering.services.IClusterGlobalServices;
+import org.opendaylight.controller.connectionmanager.ConnectionMgmtScheme;
+
+public class SchemeFactory {
+    public static AbstractScheme getScheme(ConnectionMgmtScheme scheme, IClusterGlobalServices clusterServices) {
+        if (scheme == ConnectionMgmtScheme.SINGLE_CONTROLLER) {
+            return SingleControllerScheme.getScheme(clusterServices);
+        } else if (scheme == ConnectionMgmtScheme.ROUND_ROBIN) {
+            return RoundRobinScheme.getScheme(clusterServices);
+        } else if (scheme == ConnectionMgmtScheme.LOAD_BALANCED) {
+            return LoadBalancedScheme.getScheme(clusterServices);
+        } else if (scheme == ConnectionMgmtScheme.ANY_CONTROLLER_ONE_MASTER) {
+            return AnyControllerScheme.getScheme(clusterServices);
+        }
+        return null;
+    }
+}
diff --git a/opendaylight/connectionmanager/implementation/src/main/java/org/opendaylight/controller/connectionmanager/scheme/SingleControllerScheme.java b/opendaylight/connectionmanager/implementation/src/main/java/org/opendaylight/controller/connectionmanager/scheme/SingleControllerScheme.java
new file mode 100644 (file)
index 0000000..d80911a
--- /dev/null
@@ -0,0 +1,35 @@
+package org.opendaylight.controller.connectionmanager.scheme;
+
+import java.net.InetAddress;
+import java.util.Set;
+import org.opendaylight.controller.clustering.services.IClusterGlobalServices;
+import org.opendaylight.controller.connectionmanager.ConnectionMgmtScheme;
+import org.opendaylight.controller.sal.core.Node;
+
+class SingleControllerScheme extends AbstractScheme {
+
+    private static AbstractScheme myScheme= null;
+
+    protected SingleControllerScheme(IClusterGlobalServices clusterServices) {
+        super(clusterServices, ConnectionMgmtScheme.SINGLE_CONTROLLER);
+    }
+
+    public static AbstractScheme getScheme(IClusterGlobalServices clusterServices) {
+        if (myScheme == null) {
+            myScheme = new SingleControllerScheme(clusterServices);
+        }
+        return myScheme;
+    }
+
+    @SuppressWarnings("deprecation")
+    @Override
+    public boolean isConnectionAllowedInternal(Node node) {
+        if (nodeConnections == null) return true;
+        for (Node existingNode : nodeConnections.keySet()) {
+            Set<InetAddress> controllers = nodeConnections.get(existingNode);
+            if (controllers == null || controllers.size() == 0) continue;
+            if (!controllers.contains(clusterServices.getMyAddress())) return false;
+        }
+        return true;
+    }
+}
index 50b34fa..e6871af 100644 (file)
     <module>../../topologymanager</module>
     <module>../../usermanager/api</module>
     <module>../../usermanager/implementation</module>
+    <module>../../connectionmanager/api</module>
+    <module>../../connectionmanager/implementation</module>
     <module>../../security</module>
 
-
     <module>../../../third-party/openflowj</module>
     <module>../../../third-party/net.sf.jung2</module>
     <module>../../../third-party/jersey-servlet</module>
     <module>../../sal/api</module>
     <module>../../sal/implementation</module>
 
+    <!-- SAL Extension bundles -->
+    <module>../../sal/connection/api</module>
+    <module>../../sal/connection/implementation</module>
+
     <!--  Web bundles -->
     <module>../../web/root</module>
     <module>../../web/flows</module>
diff --git a/opendaylight/sal/connection/api/pom.xml b/opendaylight/sal/connection/api/pom.xml
new file mode 100644 (file)
index 0000000..45425c9
--- /dev/null
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="UTF-8"?>\r
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">\r
+  <modelVersion>4.0.0</modelVersion>\r
+  <parent>\r
+    <groupId>org.opendaylight.controller</groupId>\r
+    <artifactId>commons.opendaylight</artifactId>\r
+    <version>1.4.0-SNAPSHOT</version>\r
+    <relativePath>../../../commons/opendaylight</relativePath>\r
+  </parent>\r
+\r
+  <artifactId>sal.connection</artifactId>\r
+  <version>0.1.0-SNAPSHOT</version>\r
+  <packaging>bundle</packaging>\r
+\r
+  <build>\r
+    <plugins>\r
+      <plugin>\r
+        <groupId>org.apache.felix</groupId>\r
+        <artifactId>maven-bundle-plugin</artifactId>\r
+        <version>2.3.6</version>\r
+        <extensions>true</extensions>\r
+        <configuration>\r
+          <instructions>\r
+            <Import-Package>\r
+              org.slf4j,\r
+              org.osgi.framework,\r
+              org.apache.felix.dm,\r
+              org.apache.commons.lang3.tuple,\r
+              javax.xml.bind.annotation,\r
+              javax.xml.bind.annotation.adapters,\r
+              org.opendaylight.controller.sal.core,\r
+              org.opendaylight.controller.sal.utils\r
+            </Import-Package>\r
+            <Export-Package>\r
+              org.opendaylight.controller.sal.connection\r
+             </Export-Package>\r
+          </instructions>\r
+          <manifestLocation>${project.basedir}/META-INF</manifestLocation>\r
+        </configuration>\r
+      </plugin>\r
+    </plugins>\r
+  </build>\r
+  <dependencies>\r
+    <dependency>\r
+      <groupId>org.opendaylight.controller</groupId>\r
+      <artifactId>sal</artifactId>\r
+      <version>0.5.0-SNAPSHOT</version>\r
+    </dependency>\r
+    <dependency>\r
+      <groupId>junit</groupId>\r
+      <artifactId>junit</artifactId>\r
+      <version>4.8.1</version>\r
+      <scope>test</scope>\r
+    </dependency>\r
+  </dependencies>\r
+</project>\r
diff --git a/opendaylight/sal/connection/api/src/main/java/org/opendaylight/controller/sal/connection/ConnectionConstants.java b/opendaylight/sal/connection/api/src/main/java/org/opendaylight/controller/sal/connection/ConnectionConstants.java
new file mode 100644 (file)
index 0000000..aaa883b
--- /dev/null
@@ -0,0 +1,33 @@
+
+/*
+ * Copyright (c) 2013 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.controller.sal.connection;
+
+/**
+ * ConnectionConstants
+ * Expand this enum as and when needed to support other connection parameters that
+ * might be needed for certain protocol plugins.
+ */
+public enum ConnectionConstants {
+    ADDRESS("address"),
+    PORT("port"),
+    PROTOCOL("protocol"),
+    USERNAME("username"),
+    PASSWORD("password");
+
+    private ConnectionConstants(String name) {
+        this.name = name;
+    }
+
+    private String name;
+
+    public String toString() {
+        return name;
+    }
+}
\ No newline at end of file
diff --git a/opendaylight/sal/connection/api/src/main/java/org/opendaylight/controller/sal/connection/IConnectionListener.java b/opendaylight/sal/connection/api/src/main/java/org/opendaylight/controller/sal/connection/IConnectionListener.java
new file mode 100644 (file)
index 0000000..7ab3bb2
--- /dev/null
@@ -0,0 +1,15 @@
+/*
+ * Copyright (c) 2013 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.controller.sal.connection;
+/**
+ * This interface defines the methods the SAL service which relay the Connection
+ * Notification events to the functional modules
+ */
+public interface IConnectionListener extends IPluginOutConnectionService {
+}
\ No newline at end of file
diff --git a/opendaylight/sal/connection/api/src/main/java/org/opendaylight/controller/sal/connection/IConnectionService.java b/opendaylight/sal/connection/api/src/main/java/org/opendaylight/controller/sal/connection/IConnectionService.java
new file mode 100644 (file)
index 0000000..685738c
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2013 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.controller.sal.connection;
+
+import java.util.Map;
+
+import org.opendaylight.controller.sal.core.Node;
+import org.opendaylight.controller.sal.utils.Status;
+
+/**
+ * Interface that defines the methods available to the functional modules that operate
+ * above SAL for disconnecting or connecting to a particular node.
+ */
+public interface IConnectionService {
+    /**
+     * Connect to a node with a specified node type.
+     *
+     * @param type Type of the node representing NodeIDType.
+     * @param connectionIdentifier Convenient identifier for the applications to make use of
+     * @param params Connection Params in Map format. This is entirely handled by the south-bound
+     * plugins and is an opaque value for SAL. Typical values keyed inside this params are
+     * Management IP-Address, Username, Password, Security Keys, etc...
+     *
+     *  @return Node
+     */
+    public Node connect (String type, String connectionIdentifier, Map<ConnectionConstants, String> params);
+
+
+    /**
+     * Discover the node type and Connect to the first plugin that is able to connect with the specified parameters.
+     *
+     * @param type Type of the node representing NodeIDType.
+     * @param connectionIdentifier Convenient identifier for the applications to make use of
+     * @param params Connection Params in Map format. This is entirely handled by the south-bound
+     * plugins and is an opaque value for SAL. Typical values keyed inside this params are
+     * Management IP-Address, Username, Password, Security Keys, etc...
+     *
+     *  @return Node
+     */
+    public Node connect (String connectionIdentifier, Map<ConnectionConstants, String> params);
+
+    /**
+     * Disconnect a Node that is connected to this Controller.
+     *
+     * @param node
+     * @param flow
+     */
+    public Status disconnect(Node node);
+
+    /**
+     * View Change notification
+     */
+    public void notifyNodeDisconnectFromMaster(Node node);
+
+    /**
+     * View Change notification
+     */
+    public void notifyClusterViewChanged();
+}
\ No newline at end of file
diff --git a/opendaylight/sal/connection/api/src/main/java/org/opendaylight/controller/sal/connection/IPluginInConnectionService.java b/opendaylight/sal/connection/api/src/main/java/org/opendaylight/controller/sal/connection/IPluginInConnectionService.java
new file mode 100644 (file)
index 0000000..9bae123
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2013 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.controller.sal.connection;
+
+import java.util.Map;
+
+import org.opendaylight.controller.sal.core.Node;
+import org.opendaylight.controller.sal.utils.Status;
+
+/**
+ * @file IPluginInConnectionService.java
+ *
+ * @brief Connection interface to be implemented by protocol plugins
+ */
+public interface IPluginInConnectionService {
+    /**
+     * Disconnect a Node that is connected to this Controller.
+     *
+     * @param node
+     * @param flow
+     */
+    public Status disconnect(Node node);
+
+    /**
+     * Connect to a node
+     *
+     * @param connectionIdentifier Convenient identifier for the applications to make use of
+     * @param params Connection Params in Map format. This is entirely handled by the south-bound
+     * plugins and is an opaque value for SAL. Typical values keyed inside this params are
+     * Management IP-Address, Username, Password, Security Keys, etc...
+     *
+     * @return Node
+     */
+    public Node connect (String connectionIdentifier, Map<ConnectionConstants, String> params);
+
+    /**
+     * View Change notification
+     */
+    public void notifyClusterViewChanged();
+
+    /**
+     * Node Disconnected from the node's master controller.
+     */
+    public void notifyNodeDisconnectFromMaster(Node node);
+
+}
\ No newline at end of file
diff --git a/opendaylight/sal/connection/api/src/main/java/org/opendaylight/controller/sal/connection/IPluginOutConnectionService.java b/opendaylight/sal/connection/api/src/main/java/org/opendaylight/controller/sal/connection/IPluginOutConnectionService.java
new file mode 100644 (file)
index 0000000..b602d61
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2013 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.controller.sal.connection;
+
+import org.opendaylight.controller.sal.core.Node;
+
+public interface IPluginOutConnectionService {
+    /**
+     * Method to test if a node is local to a controller.
+     *
+     * @return true if node is local to this controller. false otherwise.
+     */
+    public boolean isLocal(Node node);
+}
\ No newline at end of file
diff --git a/opendaylight/sal/connection/implementation/pom.xml b/opendaylight/sal/connection/implementation/pom.xml
new file mode 100644 (file)
index 0000000..2feed0b
--- /dev/null
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="UTF-8"?>\r
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">\r
+  <modelVersion>4.0.0</modelVersion>\r
+  <parent>\r
+    <groupId>org.opendaylight.controller</groupId>\r
+    <artifactId>commons.opendaylight</artifactId>\r
+    <version>1.4.0-SNAPSHOT</version>\r
+    <relativePath>../../../commons/opendaylight</relativePath>\r
+  </parent>\r
+\r
+  <artifactId>sal.connection.implementation</artifactId>\r
+  <version>0.1.0-SNAPSHOT</version>\r
+  <packaging>bundle</packaging>\r
+\r
+  <build>\r
+    <plugins>\r
+      <plugin>\r
+        <groupId>org.apache.felix</groupId>\r
+        <artifactId>maven-bundle-plugin</artifactId>\r
+        <version>2.3.6</version>\r
+        <extensions>true</extensions>\r
+        <configuration>\r
+          <instructions>\r
+            <Import-Package>\r
+              org.slf4j,\r
+              org.opendaylight.controller.sal.core,\r
+              org.opendaylight.controller.sal.packet,\r
+              org.opendaylight.controller.sal.inventory,\r
+              org.opendaylight.controller.sal.flowprogrammer,\r
+              org.opendaylight.controller.sal.reader,\r
+              org.opendaylight.controller.sal.topology,\r
+              org.opendaylight.controller.sal.action,\r
+              org.opendaylight.controller.sal.match,\r
+              org.opendaylight.controller.sal.utils,\r
+              org.opendaylight.controller.sal.connection,\r
+              org.apache.felix.dm,\r
+              org.eclipse.osgi.framework.console,\r
+              org.osgi.framework\r
+            </Import-Package>\r
+            <Export-Package>\r
+            </Export-Package>\r
+            <Bundle-Activator>\r
+              org.opendaylight.controller.sal.connection.implementation.internal.Activator\r
+            </Bundle-Activator>\r
+          </instructions>\r
+          <manifestLocation>${project.basedir}/META-INF</manifestLocation>\r
+        </configuration>\r
+      </plugin>\r
+    </plugins>\r
+  </build>\r
+  <dependencies>\r
+    <dependency>\r
+      <groupId>org.opendaylight.controller</groupId>\r
+      <artifactId>sal</artifactId>\r
+      <version>0.5.0-SNAPSHOT</version>\r
+    </dependency>\r
+    <dependency>\r
+      <groupId>org.opendaylight.controller</groupId>\r
+      <artifactId>sal.connection</artifactId>\r
+      <version>0.1.0-SNAPSHOT</version>\r
+    </dependency>\r
+  </dependencies>\r
+</project>\r
diff --git a/opendaylight/sal/connection/implementation/src/main/java/org/opendaylight/controller/sal/connection/implementation/internal/Activator.java b/opendaylight/sal/connection/implementation/src/main/java/org/opendaylight/controller/sal/connection/implementation/internal/Activator.java
new file mode 100644 (file)
index 0000000..68cd10d
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2013 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.controller.sal.connection.implementation.internal;
+
+import org.apache.felix.dm.Component;
+import org.opendaylight.controller.sal.connection.IConnectionListener;
+import org.opendaylight.controller.sal.connection.IConnectionService;
+import org.opendaylight.controller.sal.connection.IPluginInConnectionService;
+import org.opendaylight.controller.sal.connection.IPluginOutConnectionService;
+import org.opendaylight.controller.sal.core.ComponentActivatorAbstractBase;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class Activator extends ComponentActivatorAbstractBase {
+    protected static final Logger logger = LoggerFactory
+            .getLogger(Activator.class);
+
+    /**
+     * Function called when the activator starts just after some initializations
+     * are done by the ComponentActivatorAbstractBase.
+     *
+     */
+    @Override
+    public void init() {
+
+    }
+
+    /**
+     * Function called when the activator stops just before the cleanup done by
+     * ComponentActivatorAbstractBase
+     *
+     */
+    @Override
+    public void destroy() {
+
+    }
+
+    /**
+     * Function that is used to communicate to dependency manager the list of
+     * known Global implementations
+     *
+     *
+     * @return An array containing all the CLASS objects that will be
+     *         instantiated in order to get an fully working implementation
+     *         Object
+     */
+    public Object[] getGlobalImplementations() {
+        Object[] res = { ConnectionService.class};
+        return res;
+    }
+
+    /**
+     * Function that is called when configuration of the dependencies is required.
+     *
+     * @param c
+     *            dependency manager Component object, used for configuring the
+     *            dependencies exported and imported
+     * @param imp
+     *            Implementation class that is being configured, needed as long
+     *            as the same routine can configure multiple implementations
+     */
+    public void configureGlobalInstance(Component c, Object imp) {
+        if (imp.equals(ConnectionService.class)) {
+            c.setInterface(
+                    new String[] { IConnectionService.class.getName(),
+                                   IPluginOutConnectionService.class.getName() },
+                                   null);
+
+            c.add(createServiceDependency()
+                    .setService(IPluginInConnectionService.class)
+                    .setCallbacks("setPluginService", "unsetPluginService")
+                    .setRequired(false));
+            c.add(createServiceDependency()
+                    .setService(IConnectionListener.class)
+                    .setCallbacks("setListener", "unsetListener")
+                    .setRequired(false));
+        }
+    }
+}
diff --git a/opendaylight/sal/connection/implementation/src/main/java/org/opendaylight/controller/sal/connection/implementation/internal/ConnectionService.java b/opendaylight/sal/connection/implementation/src/main/java/org/opendaylight/controller/sal/connection/implementation/internal/ConnectionService.java
new file mode 100644 (file)
index 0000000..ad4a5fb
--- /dev/null
@@ -0,0 +1,154 @@
+/*
+ * Copyright (c) 2013 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.controller.sal.connection.implementation.internal;
+
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.Collections;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import org.opendaylight.controller.sal.connection.ConnectionConstants;
+import org.opendaylight.controller.sal.connection.IConnectionListener;
+import org.opendaylight.controller.sal.connection.IConnectionService;
+import org.opendaylight.controller.sal.connection.IPluginInConnectionService;
+import org.opendaylight.controller.sal.connection.IPluginOutConnectionService;
+import org.opendaylight.controller.sal.core.Node;
+import org.opendaylight.controller.sal.utils.GlobalConstants;
+import org.opendaylight.controller.sal.utils.Status;
+import org.opendaylight.controller.sal.utils.StatusCode;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ConnectionService implements IPluginOutConnectionService, IConnectionService {
+    protected static final Logger logger = LoggerFactory
+            .getLogger(ConnectionService.class);
+    private IConnectionListener connectionListener;
+    private ConcurrentMap<String, IPluginInConnectionService> pluginService =
+            new ConcurrentHashMap<String, IPluginInConnectionService>();
+
+    void setPluginService (Map props, IPluginInConnectionService s) {
+        String type = null;
+        Object value = props.get(GlobalConstants.PROTOCOLPLUGINTYPE.toString());
+        if (value instanceof String) {
+            type = (String) value;
+        }
+        if (type == null) {
+            logger.error("Received a PluginInConnectionService without any "
+                    + "protocolPluginType provided");
+        } else {
+            this.pluginService.put(type, s);
+        }
+    }
+
+    void unsetPluginService(Map props, IPluginInConnectionService s) {
+        String type = null;
+
+        Object value = props.get(GlobalConstants.PROTOCOLPLUGINTYPE.toString());
+        if (value instanceof String) {
+            type = (String) value;
+        }
+        if (type == null) {
+            logger.error("Received a PluginInConnectionService without any "
+                    + "protocolPluginType provided");
+        } else if (this.pluginService.get(type).equals(s)) {
+            this.pluginService.remove(type);
+        }
+    }
+
+    void setListener(IConnectionListener s) {
+        this.connectionListener = s;
+    }
+
+    void unsetListener(IConnectionListener s) {
+        if (this.connectionListener == s) {
+            this.connectionListener = null;
+        }
+    }
+
+    /**
+     * Function called by the dependency manager when all the required
+     * dependencies are satisfied
+     *
+     */
+    void init() {
+    }
+
+    /**
+     * Function called by the dependency manager when at least one dependency
+     * become unsatisfied or when the component is shutting down because for
+     * example bundle is being stopped.
+     *
+     */
+    void destroy() {
+        connectionListener = null;
+        if (this.pluginService != null) {
+            this.pluginService.clear();
+        }
+    }
+
+    /**
+     * Method to test if a node is local to a controller.
+     *
+     * @return true if node is local to this controller. false otherwise.
+     */
+    public boolean isLocal(Node node) {
+        if (this.connectionListener == null) return true;
+        return connectionListener.isLocal(node);
+    }
+
+    @Override
+    public Node connect (String type, String connectionIdentifier, Map<ConnectionConstants, String> params) {
+        IPluginInConnectionService s = pluginService.get(type);
+        if (s != null) return s.connect(connectionIdentifier, params);
+        return null;
+    }
+
+    @Override
+    public Node connect (String connectionIdentifier, Map<ConnectionConstants, String> params) {
+        synchronized (this.pluginService) {
+            for (String pluginType : this.pluginService.keySet()) {
+                IPluginInConnectionService s = pluginService.get(pluginType);
+                Node node = s.connect(connectionIdentifier, params);
+                if (node != null) return node;
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public Status disconnect(Node node) {
+        IPluginInConnectionService s = pluginService.get(node.getType());
+        if (s != null) return s.disconnect(node);
+        return new Status(StatusCode.NOTFOUND);
+    }
+
+    /**
+     * View Change notification
+     */
+    @Override
+    public void notifyClusterViewChanged() {
+        for (String pluginType : this.pluginService.keySet()) {
+            IPluginInConnectionService s = pluginService.get(pluginType);
+            s.notifyClusterViewChanged();
+        }
+    }
+
+    /**
+     * Node Disconnected from the node's master controller.
+     */
+    @Override
+    public void notifyNodeDisconnectFromMaster(Node node) {
+        for (String pluginType : this.pluginService.keySet()) {
+            IPluginInConnectionService s = pluginService.get(pluginType);
+            s.notifyNodeDisconnectFromMaster(node);
+        }
+    }
+}
\ No newline at end of file
index 85d239f..fc9b9e2 100644 (file)
@@ -8,6 +8,9 @@
 
 package org.opendaylight.controller.sal.implementation.internal;
 
+import java.util.Dictionary;
+import java.util.Hashtable;
+
 import org.apache.felix.dm.Component;
 import org.opendaylight.controller.sal.core.ComponentActivatorAbstractBase;
 import org.opendaylight.controller.sal.flowprogrammer.IFlowProgrammerListener;
@@ -57,6 +60,52 @@ public class Activator extends ComponentActivatorAbstractBase {
 
     }
 
+    /**
+     * Function that is used to communicate to dependency manager the list of
+     * known Global implementations
+     *
+     *
+     * @return An array containing all the CLASS objects that will be
+     *         instantiated in order to get an fully working implementation
+     *         Object
+     */
+    public Object[] getGlobalImplementations() {
+        Object[] res = { Inventory.class };
+        return res;
+    }
+
+    /**
+     * Function that is called when configuration of the dependencies is required.
+     *
+     * @param c
+     *            dependency manager Component object, used for configuring the
+     *            dependencies exported and imported
+     * @param imp
+     *            Implementation class that is being configured, needed as long
+     *            as the same routine can configure multiple implementations
+     */
+    public void configureGlobalInstance(Component c, Object imp) {
+        if (imp.equals(Inventory.class)) {
+            Dictionary<String, Object> props = new Hashtable<String, Object>();
+            props.put("scope", "Global");
+            // export the service
+            c.setInterface(
+                    new String[] { IPluginOutInventoryService.class.getName(),
+                            IInventoryService.class.getName() }, props);
+
+            // Now lets add a service dependency to make sure the
+            // provider of service exists
+            c.add(createServiceDependency()
+                    .setService(IListenInventoryUpdates.class, "(scope=Global)")
+                    .setCallbacks("setUpdateService", "unsetUpdateService")
+                    .setRequired(false));
+            c.add(createServiceDependency()
+                    .setService(IPluginInInventoryService.class, "(scope=Global)")
+                    .setCallbacks("setPluginService", "unsetPluginService")
+                    .setRequired(true));
+        }
+    }
+
     /**
      * Function that is used to communicate to dependency manager the list of
      * known implementations for services inside a container