Merge "Connection Manager infrastructure supporting the Clustering Active-Active...
authorAlessandro Boch <aboch@cisco.com>
Wed, 31 Jul 2013 00:12:42 +0000 (00:12 +0000)
committerGerrit Code Review <gerrit@opendaylight.org>
Wed, 31 Jul 2013 00:12:42 +0000 (00:12 +0000)
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 50b34faac847f8a084e44b42cee41d93c2cc3a81..e6871afa772860e05ba22db915005823af7d6243 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 85d239f4b9e1c52a265e2cb685fcb64925dac3d2..fc9b9e2df48dae7e1dda300c113c5c1470a23ebf 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