--- /dev/null
+<?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>
--- /dev/null
+/*
+ * 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;
+ }
+}
--- /dev/null
+
+/*
+ * 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);
+}
--- /dev/null
+<?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>
--- /dev/null
+
+/*
+ * 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));
+ }
+ }
+}
--- /dev/null
+
+/*
+ * 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();
+ }
+}
--- /dev/null
+
+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 + "]";
+ }
+}
--- /dev/null
+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;
+ }
+}
--- /dev/null
+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();
+ }
+ }
+}
--- /dev/null
+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()));
+ }
+}
--- /dev/null
+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 + "]";
+ }
+}
--- /dev/null
+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;
+ }
+
+}
--- /dev/null
+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;
+ }
+
+}
--- /dev/null
+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;
+ }
+}
--- /dev/null
+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;
+ }
+}
<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>
--- /dev/null
+<?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
--- /dev/null
+
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+<?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
--- /dev/null
+/*
+ * 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));
+ }
+ }
+}
--- /dev/null
+/*
+ * 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
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;
}
+ /**
+ * 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