tom

Further simplified the store & connection manager relationship.

Showing 16 changed files with 277 additions and 345 deletions
1 -package org.onlab.onos.ccc;
2 -
3 -import com.fasterxml.jackson.core.JsonEncoding;
4 -import com.fasterxml.jackson.core.JsonFactory;
5 -import com.fasterxml.jackson.databind.JsonNode;
6 -import com.fasterxml.jackson.databind.ObjectMapper;
7 -import com.fasterxml.jackson.databind.node.ArrayNode;
8 -import com.fasterxml.jackson.databind.node.ObjectNode;
9 -import org.onlab.onos.cluster.DefaultControllerNode;
10 -import org.onlab.onos.cluster.NodeId;
11 -import org.onlab.packet.IpPrefix;
12 -
13 -import java.io.File;
14 -import java.io.IOException;
15 -import java.util.HashSet;
16 -import java.util.Iterator;
17 -import java.util.Set;
18 -
19 -/**
20 - * Allows for reading and writing cluster definition as a JSON file.
21 - */
22 -public class ClusterDefinitionStore {
23 -
24 - private final File file;
25 -
26 - /**
27 - * Creates a reader/writer of the cluster definition file.
28 - *
29 - * @param filePath location of the definition file
30 - */
31 - public ClusterDefinitionStore(String filePath) {
32 - file = new File(filePath);
33 - }
34 -
35 - /**
36 - * Returns set of the controller nodes, including self.
37 - *
38 - * @return set of controller nodes
39 - */
40 - public Set<DefaultControllerNode> read() throws IOException {
41 - Set<DefaultControllerNode> nodes = new HashSet<>();
42 - ObjectMapper mapper = new ObjectMapper();
43 - ObjectNode clusterNodeDef = (ObjectNode) mapper.readTree(file);
44 - Iterator<JsonNode> it = ((ArrayNode) clusterNodeDef.get("nodes")).elements();
45 - while (it.hasNext()) {
46 - ObjectNode nodeDef = (ObjectNode) it.next();
47 - nodes.add(new DefaultControllerNode(new NodeId(nodeDef.get("id").asText()),
48 - IpPrefix.valueOf(nodeDef.get("ip").asText()),
49 - nodeDef.get("tcpPort").asInt(9876)));
50 - }
51 - return nodes;
52 - }
53 -
54 - /**
55 - * Writes the given set of the controller nodes.
56 - *
57 - * @param nodes set of controller nodes
58 - */
59 - public void write(Set<DefaultControllerNode> nodes) throws IOException {
60 - ObjectMapper mapper = new ObjectMapper();
61 - ObjectNode clusterNodeDef = mapper.createObjectNode();
62 - ArrayNode nodeDefs = mapper.createArrayNode();
63 - clusterNodeDef.set("nodes", nodeDefs);
64 - for (DefaultControllerNode node : nodes) {
65 - ObjectNode nodeDef = mapper.createObjectNode();
66 - nodeDef.put("id", node.id().toString())
67 - .put("ip", node.ip().toString())
68 - .put("tcpPort", node.tcpPort());
69 - nodeDefs.add(nodeDef);
70 - }
71 - mapper.writeTree(new JsonFactory().createGenerator(file, JsonEncoding.UTF8),
72 - clusterNodeDef);
73 - }
74 -
75 -}
1 +package org.onlab.onos.store.cluster.impl;
2 +
3 +import org.onlab.onos.cluster.DefaultControllerNode;
4 +
5 +/**
6 + * Service for administering communications manager.
7 + */
8 +public interface ClusterCommunicationAdminService {
9 +
10 + /**
11 + * Adds the node to the list of monitored nodes.
12 + *
13 + * @param node node to be added
14 + */
15 + void addNode(DefaultControllerNode node);
16 +
17 + /**
18 + * Removes the node from the list of monitored nodes.
19 + *
20 + * @param node node to be removed
21 + */
22 + void removeNode(DefaultControllerNode node);
23 +
24 + /**
25 + * Starts-up the communications engine.
26 + *
27 + * @param localNode local controller node
28 + * @param delegate nodes delegate
29 + */
30 + void startUp(DefaultControllerNode localNode, ClusterNodesDelegate delegate);
31 +
32 +}
1 package org.onlab.onos.store.cluster.impl; 1 package org.onlab.onos.store.cluster.impl;
2 2
3 +import com.google.common.collect.HashMultimap;
4 +import com.google.common.collect.ImmutableSet;
5 +import com.google.common.collect.Multimap;
6 +import org.apache.felix.scr.annotations.Activate;
7 +import org.apache.felix.scr.annotations.Component;
8 +import org.apache.felix.scr.annotations.Deactivate;
9 +import org.apache.felix.scr.annotations.Reference;
10 +import org.apache.felix.scr.annotations.ReferenceCardinality;
11 +import org.apache.felix.scr.annotations.Service;
3 import org.onlab.onos.cluster.DefaultControllerNode; 12 import org.onlab.onos.cluster.DefaultControllerNode;
4 import org.onlab.onos.cluster.NodeId; 13 import org.onlab.onos.cluster.NodeId;
14 +import org.onlab.onos.store.cluster.messaging.ClusterCommunicationService;
5 import org.onlab.onos.store.cluster.messaging.ClusterMessage; 15 import org.onlab.onos.store.cluster.messaging.ClusterMessage;
6 import org.onlab.onos.store.cluster.messaging.ClusterMessageStream; 16 import org.onlab.onos.store.cluster.messaging.ClusterMessageStream;
7 import org.onlab.onos.store.cluster.messaging.HelloMessage; 17 import org.onlab.onos.store.cluster.messaging.HelloMessage;
18 +import org.onlab.onos.store.cluster.messaging.MessageSubject;
19 +import org.onlab.onos.store.cluster.messaging.MessageSubscriber;
8 import org.onlab.onos.store.cluster.messaging.SerializationService; 20 import org.onlab.onos.store.cluster.messaging.SerializationService;
21 +import org.onlab.packet.IpPrefix;
9 import org.slf4j.Logger; 22 import org.slf4j.Logger;
10 import org.slf4j.LoggerFactory; 23 import org.slf4j.LoggerFactory;
11 24
...@@ -28,9 +41,12 @@ import static java.net.InetAddress.getByAddress; ...@@ -28,9 +41,12 @@ import static java.net.InetAddress.getByAddress;
28 import static org.onlab.util.Tools.namedThreads; 41 import static org.onlab.util.Tools.namedThreads;
29 42
30 /** 43 /**
31 - * Manages connections to other controller cluster nodes. 44 + * Implements the cluster communication services to use by other stores.
32 */ 45 */
33 -public class ConnectionManager implements MessageSender { 46 +@Component(immediate = true)
47 +@Service
48 +public class ClusterCommunicationManager
49 + implements ClusterCommunicationService, ClusterCommunicationAdminService {
34 50
35 private final Logger log = LoggerFactory.getLogger(getClass()); 51 private final Logger log = LoggerFactory.getLogger(getClass());
36 52
...@@ -43,10 +59,11 @@ public class ConnectionManager implements MessageSender { ...@@ -43,10 +59,11 @@ public class ConnectionManager implements MessageSender {
43 private ClusterConnectionListener connectionListener; 59 private ClusterConnectionListener connectionListener;
44 private List<ClusterIOWorker> workers = new ArrayList<>(WORKERS); 60 private List<ClusterIOWorker> workers = new ArrayList<>(WORKERS);
45 61
46 - private final DefaultControllerNode localNode; 62 + private DefaultControllerNode localNode;
47 - private final ClusterNodesDelegate nodesDelegate; 63 + private ClusterNodesDelegate nodesDelegate;
48 - private final CommunicationsDelegate commsDelegate; 64 +
49 - private final SerializationService serializationService; 65 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
66 + protected SerializationService serializationService;
50 67
51 // Nodes to be monitored to make sure they have a connection. 68 // Nodes to be monitored to make sure they have a connection.
52 private final Set<DefaultControllerNode> nodes = new HashSet<>(); 69 private final Set<DefaultControllerNode> nodes = new HashSet<>();
...@@ -54,6 +71,9 @@ public class ConnectionManager implements MessageSender { ...@@ -54,6 +71,9 @@ public class ConnectionManager implements MessageSender {
54 // Means to track message streams to other nodes. 71 // Means to track message streams to other nodes.
55 private final Map<NodeId, ClusterMessageStream> streams = new ConcurrentHashMap<>(); 72 private final Map<NodeId, ClusterMessageStream> streams = new ConcurrentHashMap<>();
56 73
74 + // TODO: use something different that won't require synchronization
75 + private Multimap<MessageSubject, MessageSubscriber> subscribers = HashMultimap.create();
76 +
57 // Executor pools for listening and managing connections to other nodes. 77 // Executor pools for listening and managing connections to other nodes.
58 private final ExecutorService listenExecutor = 78 private final ExecutorService listenExecutor =
59 Executors.newSingleThreadExecutor(namedThreads("onos-comm-listen")); 79 Executors.newSingleThreadExecutor(namedThreads("onos-comm-listen"));
...@@ -65,59 +85,110 @@ public class ConnectionManager implements MessageSender { ...@@ -65,59 +85,110 @@ public class ConnectionManager implements MessageSender {
65 private final Timer timer = new Timer("onos-comm-initiator"); 85 private final Timer timer = new Timer("onos-comm-initiator");
66 private final TimerTask connectionCustodian = new ConnectionCustodian(); 86 private final TimerTask connectionCustodian = new ConnectionCustodian();
67 87
68 - private final WorkerFinder workerFinder = new LeastUtilitiedWorkerFinder(); 88 + @Activate
89 + public void activate() {
90 + log.info("Activated but waiting for delegate");
91 + }
69 92
93 + @Deactivate
94 + public void deactivate() {
95 + connectionCustodian.cancel();
96 + if (connectionListener != null) {
97 + connectionListener.shutdown();
98 + for (ClusterIOWorker worker : workers) {
99 + worker.shutdown();
100 + }
101 + }
102 + log.info("Stopped");
103 + }
70 104
71 - /** 105 + @Override
72 - * Creates a new connection manager. 106 + public boolean send(ClusterMessage message, NodeId toNodeId) {
73 - */ 107 + ClusterMessageStream stream = streams.get(toNodeId);
74 - ConnectionManager(DefaultControllerNode localNode, 108 + if (stream != null) {
75 - ClusterNodesDelegate nodesDelegate, 109 + try {
76 - CommunicationsDelegate commsDelegate, 110 + stream.write(message);
77 - SerializationService serializationService) { 111 + return true;
112 + } catch (IOException e) {
113 + log.warn("Unable to send message {} to node {}",
114 + message.subject(), toNodeId);
115 + }
116 + }
117 + return false;
118 + }
119 +
120 + @Override
121 + public synchronized void addSubscriber(MessageSubject subject,
122 + MessageSubscriber subscriber) {
123 + subscribers.put(subject, subscriber);
124 + }
125 +
126 + @Override
127 + public synchronized void removeSubscriber(MessageSubject subject,
128 + MessageSubscriber subscriber) {
129 + subscribers.remove(subject, subscriber);
130 + }
131 +
132 + @Override
133 + public Set<MessageSubscriber> getSubscribers(MessageSubject subject) {
134 + return ImmutableSet.copyOf(subscribers.get(subject));
135 + }
136 +
137 + @Override
138 + public void addNode(DefaultControllerNode node) {
139 + nodes.add(node);
140 + }
141 +
142 + @Override
143 + public void removeNode(DefaultControllerNode node) {
144 + nodes.remove(node);
145 + ClusterMessageStream stream = streams.remove(node.id());
146 + if (stream != null) {
147 + stream.close();
148 + }
149 + }
150 +
151 + @Override
152 + public void startUp(DefaultControllerNode localNode,
153 + ClusterNodesDelegate delegate) {
78 this.localNode = localNode; 154 this.localNode = localNode;
79 - this.nodesDelegate = nodesDelegate; 155 + this.nodesDelegate = delegate;
80 - this.commsDelegate = commsDelegate;
81 - this.serializationService = serializationService;
82 156
83 - commsDelegate.setSender(this);
84 startCommunications(); 157 startCommunications();
85 startListening(); 158 startListening();
86 - startInitiating(); 159 + startInitiatingConnections();
87 log.info("Started"); 160 log.info("Started");
88 } 161 }
89 162
90 /** 163 /**
91 - * Shuts down the connection manager. 164 + * Dispatches the specified message to all subscribers to its subject.
92 - */
93 - void shutdown() {
94 - connectionListener.shutdown();
95 - for (ClusterIOWorker worker : workers) {
96 - worker.shutdown();
97 - }
98 - log.info("Stopped");
99 - }
100 -
101 - /**
102 - * Adds the node to the list of monitored nodes.
103 * 165 *
104 - * @param node node to be added 166 + * @param message message to dispatch
167 + * @param fromNodeId node from which the message was received
105 */ 168 */
106 - void addNode(DefaultControllerNode node) { 169 + void dispatch(ClusterMessage message, NodeId fromNodeId) {
107 - nodes.add(node); 170 + Set<MessageSubscriber> set = getSubscribers(message.subject());
171 + if (set != null) {
172 + for (MessageSubscriber subscriber : set) {
173 + subscriber.receive(message, fromNodeId);
174 + }
175 + }
108 } 176 }
109 177
110 /** 178 /**
111 - * Removes the node from the list of monitored nodes. 179 + * Removes the stream associated with the specified node.
112 * 180 *
113 - * @param node node to be removed 181 + * @param nodeId newly detected cluster node id
182 + * @param ip node IP listen address
183 + * @param tcpPort node TCP listen port
184 + * @return controller node bound to the stream
114 */ 185 */
115 - void removeNode(DefaultControllerNode node) { 186 + DefaultControllerNode addNodeStream(NodeId nodeId, IpPrefix ip, int tcpPort,
116 - nodes.remove(node); 187 + ClusterMessageStream stream) {
117 - ClusterMessageStream stream = streams.remove(node.id()); 188 + DefaultControllerNode node = nodesDelegate.nodeDetected(nodeId, ip, tcpPort);
118 - if (stream != null) { 189 + stream.setNode(node);
119 - stream.close(); 190 + streams.put(node.id(), stream);
120 - } 191 + return node;
121 } 192 }
122 193
123 /** 194 /**
...@@ -126,23 +197,30 @@ public class ConnectionManager implements MessageSender { ...@@ -126,23 +197,30 @@ public class ConnectionManager implements MessageSender {
126 * @param node node whose stream to remove 197 * @param node node whose stream to remove
127 */ 198 */
128 void removeNodeStream(DefaultControllerNode node) { 199 void removeNodeStream(DefaultControllerNode node) {
129 - nodesDelegate.nodeVanished(node); 200 + nodesDelegate.nodeVanished(node.id());
130 streams.remove(node.id()); 201 streams.remove(node.id());
131 } 202 }
132 203
133 - @Override 204 + /**
134 - public boolean send(NodeId nodeId, ClusterMessage message) { 205 + * Finds the least utilized IO worker.
135 - ClusterMessageStream stream = streams.get(nodeId); 206 + *
136 - if (stream != null) { 207 + * @return IO worker
137 - try { 208 + */
138 - stream.write(message); 209 + ClusterIOWorker findWorker() {
139 - return true; 210 + ClusterIOWorker leastUtilized = null;
140 - } catch (IOException e) { 211 + int minCount = Integer.MAX_VALUE;
141 - log.warn("Unable to send a message about {} to node {}", 212 + for (ClusterIOWorker worker : workers) {
142 - message.subject(), nodeId); 213 + int count = worker.streamCount();
214 + if (count == 0) {
215 + return worker;
216 + }
217 +
218 + if (count < minCount) {
219 + leastUtilized = worker;
220 + minCount = count;
143 } 221 }
144 } 222 }
145 - return false; 223 + return leastUtilized;
146 } 224 }
147 225
148 /** 226 /**
...@@ -154,8 +232,7 @@ public class ConnectionManager implements MessageSender { ...@@ -154,8 +232,7 @@ public class ConnectionManager implements MessageSender {
154 for (int i = 0; i < WORKERS; i++) { 232 for (int i = 0; i < WORKERS; i++) {
155 try { 233 try {
156 ClusterIOWorker worker = 234 ClusterIOWorker worker =
157 - new ClusterIOWorker(this, commsDelegate, 235 + new ClusterIOWorker(this, serializationService, hello);
158 - serializationService, hello);
159 workers.add(worker); 236 workers.add(worker);
160 commExecutors.execute(worker); 237 commExecutors.execute(worker);
161 } catch (IOException e) { 238 } catch (IOException e) {
...@@ -177,8 +254,7 @@ public class ConnectionManager implements MessageSender { ...@@ -177,8 +254,7 @@ public class ConnectionManager implements MessageSender {
177 private void startListening() { 254 private void startListening() {
178 try { 255 try {
179 connectionListener = 256 connectionListener =
180 - new ClusterConnectionListener(localNode.ip(), localNode.tcpPort(), 257 + new ClusterConnectionListener(this, localNode.ip(), localNode.tcpPort());
181 - workerFinder);
182 listenExecutor.execute(connectionListener); 258 listenExecutor.execute(connectionListener);
183 if (!connectionListener.awaitStart(START_TIMEOUT)) { 259 if (!connectionListener.awaitStart(START_TIMEOUT)) {
184 log.warn("Listener did not start on-time; moving on..."); 260 log.warn("Listener did not start on-time; moving on...");
...@@ -189,28 +265,27 @@ public class ConnectionManager implements MessageSender { ...@@ -189,28 +265,27 @@ public class ConnectionManager implements MessageSender {
189 } 265 }
190 266
191 /** 267 /**
268 + * Attempts to connect to any nodes that do not have an associated connection.
269 + */
270 + private void startInitiatingConnections() {
271 + timer.schedule(connectionCustodian, CONNECTION_CUSTODIAN_DELAY,
272 + CONNECTION_CUSTODIAN_FREQUENCY);
273 + }
274 +
275 + /**
192 * Initiates open connection request and registers the pending socket 276 * Initiates open connection request and registers the pending socket
193 - * channel with the given IO loop. 277 + * channel with the given IO worker.
194 * 278 *
195 - * @param loop loop with which the channel should be registered 279 + * @param worker loop with which the channel should be registered
196 * @throws java.io.IOException if the socket could not be open or connected 280 * @throws java.io.IOException if the socket could not be open or connected
197 */ 281 */
198 private void initiateConnection(DefaultControllerNode node, 282 private void initiateConnection(DefaultControllerNode node,
199 - ClusterIOWorker loop) throws IOException { 283 + ClusterIOWorker worker) throws IOException {
200 SocketAddress sa = new InetSocketAddress(getByAddress(node.ip().toOctets()), node.tcpPort()); 284 SocketAddress sa = new InetSocketAddress(getByAddress(node.ip().toOctets()), node.tcpPort());
201 SocketChannel ch = SocketChannel.open(); 285 SocketChannel ch = SocketChannel.open();
202 ch.configureBlocking(false); 286 ch.configureBlocking(false);
203 ch.connect(sa); 287 ch.connect(sa);
204 - loop.connectStream(ch); 288 + worker.connectStream(ch);
205 - }
206 -
207 -
208 - /**
209 - * Attempts to connect to any nodes that do not have an associated connection.
210 - */
211 - private void startInitiating() {
212 - timer.schedule(connectionCustodian, CONNECTION_CUSTODIAN_DELAY,
213 - CONNECTION_CUSTODIAN_FREQUENCY);
214 } 289 }
215 290
216 // Sweeps through all controller nodes and attempts to open connection to 291 // Sweeps through all controller nodes and attempts to open connection to
...@@ -219,9 +294,9 @@ public class ConnectionManager implements MessageSender { ...@@ -219,9 +294,9 @@ public class ConnectionManager implements MessageSender {
219 @Override 294 @Override
220 public void run() { 295 public void run() {
221 for (DefaultControllerNode node : nodes) { 296 for (DefaultControllerNode node : nodes) {
222 - if (node != localNode && !streams.containsKey(node.id())) { 297 + if (!node.id().equals(localNode.id()) && !streams.containsKey(node.id())) {
223 try { 298 try {
224 - initiateConnection(node, workerFinder.findWorker()); 299 + initiateConnection(node, findWorker());
225 } catch (IOException e) { 300 } catch (IOException e) {
226 log.debug("Unable to connect", e); 301 log.debug("Unable to connect", e);
227 } 302 }
...@@ -230,26 +305,4 @@ public class ConnectionManager implements MessageSender { ...@@ -230,26 +305,4 @@ public class ConnectionManager implements MessageSender {
230 } 305 }
231 } 306 }
232 307
233 - // Finds the least utilitied IO loop.
234 - private class LeastUtilitiedWorkerFinder implements WorkerFinder {
235 -
236 - @Override
237 - public ClusterIOWorker findWorker() {
238 - ClusterIOWorker leastUtilized = null;
239 - int minCount = Integer.MAX_VALUE;
240 - for (ClusterIOWorker worker : workers) {
241 - int count = worker.streamCount();
242 - if (count == 0) {
243 - return worker;
244 - }
245 -
246 - if (count < minCount) {
247 - leastUtilized = worker;
248 - minCount = count;
249 - }
250 - }
251 - return leastUtilized;
252 - }
253 - }
254 -
255 } 308 }
......
...@@ -23,12 +23,12 @@ public class ClusterConnectionListener extends AcceptorLoop { ...@@ -23,12 +23,12 @@ public class ClusterConnectionListener extends AcceptorLoop {
23 private static final int SO_SEND_BUFFER_SIZE = COMM_BUFFER_SIZE; 23 private static final int SO_SEND_BUFFER_SIZE = COMM_BUFFER_SIZE;
24 private static final int SO_RCV_BUFFER_SIZE = COMM_BUFFER_SIZE; 24 private static final int SO_RCV_BUFFER_SIZE = COMM_BUFFER_SIZE;
25 25
26 - private final WorkerFinder workerFinder; 26 + private final ClusterCommunicationManager manager;
27 27
28 - ClusterConnectionListener(IpPrefix ip, int tcpPort, 28 + ClusterConnectionListener(ClusterCommunicationManager manager,
29 - WorkerFinder workerFinder) throws IOException { 29 + IpPrefix ip, int tcpPort) throws IOException {
30 super(SELECT_TIMEOUT, new InetSocketAddress(getByAddress(ip.toOctets()), tcpPort)); 30 super(SELECT_TIMEOUT, new InetSocketAddress(getByAddress(ip.toOctets()), tcpPort));
31 - this.workerFinder = workerFinder; 31 + this.manager = manager;
32 } 32 }
33 33
34 @Override 34 @Override
...@@ -41,7 +41,7 @@ public class ClusterConnectionListener extends AcceptorLoop { ...@@ -41,7 +41,7 @@ public class ClusterConnectionListener extends AcceptorLoop {
41 so.setReceiveBufferSize(SO_RCV_BUFFER_SIZE); 41 so.setReceiveBufferSize(SO_RCV_BUFFER_SIZE);
42 so.setSendBufferSize(SO_SEND_BUFFER_SIZE); 42 so.setSendBufferSize(SO_SEND_BUFFER_SIZE);
43 43
44 - workerFinder.findWorker().acceptStream(sc); 44 + manager.findWorker().acceptStream(sc);
45 } 45 }
46 46
47 } 47 }
......
...@@ -3,8 +3,10 @@ package org.onlab.onos.store.cluster.impl; ...@@ -3,8 +3,10 @@ package org.onlab.onos.store.cluster.impl;
3 import org.onlab.nio.IOLoop; 3 import org.onlab.nio.IOLoop;
4 import org.onlab.nio.MessageStream; 4 import org.onlab.nio.MessageStream;
5 import org.onlab.onos.cluster.DefaultControllerNode; 5 import org.onlab.onos.cluster.DefaultControllerNode;
6 +import org.onlab.onos.cluster.NodeId;
6 import org.onlab.onos.store.cluster.messaging.ClusterMessage; 7 import org.onlab.onos.store.cluster.messaging.ClusterMessage;
7 import org.onlab.onos.store.cluster.messaging.ClusterMessageStream; 8 import org.onlab.onos.store.cluster.messaging.ClusterMessageStream;
9 +import org.onlab.onos.store.cluster.messaging.HelloMessage;
8 import org.onlab.onos.store.cluster.messaging.SerializationService; 10 import org.onlab.onos.store.cluster.messaging.SerializationService;
9 import org.slf4j.Logger; 11 import org.slf4j.Logger;
10 import org.slf4j.LoggerFactory; 12 import org.slf4j.LoggerFactory;
...@@ -29,27 +31,23 @@ public class ClusterIOWorker extends ...@@ -29,27 +31,23 @@ public class ClusterIOWorker extends
29 31
30 private static final long SELECT_TIMEOUT = 50; 32 private static final long SELECT_TIMEOUT = 50;
31 33
32 - private final ConnectionManager connectionManager; 34 + private final ClusterCommunicationManager manager;
33 - private final CommunicationsDelegate commsDelegate;
34 private final SerializationService serializationService; 35 private final SerializationService serializationService;
35 private final ClusterMessage helloMessage; 36 private final ClusterMessage helloMessage;
36 37
37 /** 38 /**
38 * Creates a new cluster IO worker. 39 * Creates a new cluster IO worker.
39 * 40 *
40 - * @param connectionManager parent connection manager 41 + * @param manager parent comms manager
41 - * @param commsDelegate communications delegate for dispatching
42 * @param serializationService serialization service for encode/decode 42 * @param serializationService serialization service for encode/decode
43 * @param helloMessage hello message for greeting peers 43 * @param helloMessage hello message for greeting peers
44 * @throws IOException if errors occur during IO loop ignition 44 * @throws IOException if errors occur during IO loop ignition
45 */ 45 */
46 - ClusterIOWorker(ConnectionManager connectionManager, 46 + ClusterIOWorker(ClusterCommunicationManager manager,
47 - CommunicationsDelegate commsDelegate,
48 SerializationService serializationService, 47 SerializationService serializationService,
49 ClusterMessage helloMessage) throws IOException { 48 ClusterMessage helloMessage) throws IOException {
50 super(SELECT_TIMEOUT); 49 super(SELECT_TIMEOUT);
51 - this.connectionManager = connectionManager; 50 + this.manager = manager;
52 - this.commsDelegate = commsDelegate;
53 this.serializationService = serializationService; 51 this.serializationService = serializationService;
54 this.helloMessage = helloMessage; 52 this.helloMessage = helloMessage;
55 } 53 }
...@@ -61,11 +59,27 @@ public class ClusterIOWorker extends ...@@ -61,11 +59,27 @@ public class ClusterIOWorker extends
61 59
62 @Override 60 @Override
63 protected void processMessages(List<ClusterMessage> messages, MessageStream<ClusterMessage> stream) { 61 protected void processMessages(List<ClusterMessage> messages, MessageStream<ClusterMessage> stream) {
62 + NodeId nodeId = getNodeId(messages, (ClusterMessageStream) stream);
64 for (ClusterMessage message : messages) { 63 for (ClusterMessage message : messages) {
65 - commsDelegate.dispatch(message); 64 + manager.dispatch(message, nodeId);
66 } 65 }
67 } 66 }
68 67
68 + // Retrieves the node from the stream. If one is not bound, it attempts
69 + // to bind it using the knowledge that the first message must be a hello.
70 + private NodeId getNodeId(List<ClusterMessage> messages, ClusterMessageStream stream) {
71 + DefaultControllerNode node = stream.node();
72 + if (node == null && !messages.isEmpty()) {
73 + ClusterMessage firstMessage = messages.get(0);
74 + if (firstMessage instanceof HelloMessage) {
75 + HelloMessage hello = (HelloMessage) firstMessage;
76 + node = manager.addNodeStream(hello.nodeId(), hello.ipAddress(),
77 + hello.tcpPort(), stream);
78 + }
79 + }
80 + return node != null ? node.id() : null;
81 + }
82 +
69 @Override 83 @Override
70 public ClusterMessageStream acceptStream(SocketChannel channel) { 84 public ClusterMessageStream acceptStream(SocketChannel channel) {
71 ClusterMessageStream stream = super.acceptStream(channel); 85 ClusterMessageStream stream = super.acceptStream(channel);
...@@ -99,7 +113,7 @@ public class ClusterIOWorker extends ...@@ -99,7 +113,7 @@ public class ClusterIOWorker extends
99 DefaultControllerNode node = ((ClusterMessageStream) stream).node(); 113 DefaultControllerNode node = ((ClusterMessageStream) stream).node();
100 if (node != null) { 114 if (node != null) {
101 log.info("Closed connection to node {}", node.id()); 115 log.info("Closed connection to node {}", node.id());
102 - connectionManager.removeNodeStream(node); 116 + manager.removeNodeStream(node);
103 } 117 }
104 super.removeStream(stream); 118 super.removeStream(stream);
105 } 119 }
......
1 package org.onlab.onos.store.cluster.impl; 1 package org.onlab.onos.store.cluster.impl;
2 2
3 import org.onlab.onos.cluster.DefaultControllerNode; 3 import org.onlab.onos.cluster.DefaultControllerNode;
4 +import org.onlab.onos.cluster.NodeId;
5 +import org.onlab.packet.IpPrefix;
4 6
5 /** 7 /**
6 * Simple back interface through which connection manager can interact with 8 * Simple back interface through which connection manager can interact with
...@@ -9,17 +11,20 @@ import org.onlab.onos.cluster.DefaultControllerNode; ...@@ -9,17 +11,20 @@ import org.onlab.onos.cluster.DefaultControllerNode;
9 public interface ClusterNodesDelegate { 11 public interface ClusterNodesDelegate {
10 12
11 /** 13 /**
12 - * Notifies about a new cluster node being detected. 14 + * Notifies about cluster node coming online.
13 * 15 *
14 - * @param node newly detected cluster node 16 + * @param nodeId newly detected cluster node id
17 + * @param ip node IP listen address
18 + * @param tcpPort node TCP listen port
19 + * @return the controller node
15 */ 20 */
16 - void nodeDetected(DefaultControllerNode node); 21 + DefaultControllerNode nodeDetected(NodeId nodeId, IpPrefix ip, int tcpPort);
17 22
18 /** 23 /**
19 * Notifies about cluster node going offline. 24 * Notifies about cluster node going offline.
20 * 25 *
21 - * @param node cluster node that vanished 26 + * @param nodeId identifier of the cluster node that vanished
22 */ 27 */
23 - void nodeVanished(DefaultControllerNode node); 28 + void nodeVanished(NodeId nodeId);
24 29
25 } 30 }
......
1 -package org.onlab.onos.store.cluster.impl;
2 -
3 -import org.onlab.onos.store.cluster.messaging.ClusterMessage;
4 -
5 -/**
6 - * Simple back interface for interacting with the communications service.
7 - */
8 -public interface CommunicationsDelegate {
9 -
10 - /**
11 - * Dispatches the specified message to all registered subscribers.
12 - *
13 - * @param message message to be dispatched
14 - */
15 - void dispatch(ClusterMessage message);
16 -
17 - /**
18 - * Sets the sender.
19 - *
20 - * @param messageSender message sender
21 - */
22 - void setSender(MessageSender messageSender);
23 -
24 -}
...@@ -14,7 +14,6 @@ import org.onlab.onos.cluster.ControllerNode; ...@@ -14,7 +14,6 @@ import org.onlab.onos.cluster.ControllerNode;
14 import org.onlab.onos.cluster.DefaultControllerNode; 14 import org.onlab.onos.cluster.DefaultControllerNode;
15 import org.onlab.onos.cluster.NodeId; 15 import org.onlab.onos.cluster.NodeId;
16 import org.onlab.onos.store.AbstractStore; 16 import org.onlab.onos.store.AbstractStore;
17 -import org.onlab.onos.store.cluster.messaging.SerializationService;
18 import org.onlab.packet.IpPrefix; 17 import org.onlab.packet.IpPrefix;
19 import org.slf4j.Logger; 18 import org.slf4j.Logger;
20 import org.slf4j.LoggerFactory; 19 import org.slf4j.LoggerFactory;
...@@ -43,20 +42,20 @@ public class DistributedClusterStore ...@@ -43,20 +42,20 @@ public class DistributedClusterStore
43 private final Map<NodeId, State> states = new ConcurrentHashMap<>(); 42 private final Map<NodeId, State> states = new ConcurrentHashMap<>();
44 43
45 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) 44 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
46 - private CommunicationsDelegate commsDelegate; 45 + private ClusterCommunicationAdminService communicationAdminService;
47 -
48 - @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
49 - private SerializationService serializationService;
50 46
51 private final ClusterNodesDelegate nodesDelegate = new InnerNodesDelegate(); 47 private final ClusterNodesDelegate nodesDelegate = new InnerNodesDelegate();
52 - private ConnectionManager connectionManager;
53 48
54 @Activate 49 @Activate
55 public void activate() { 50 public void activate() {
56 loadClusterDefinition(); 51 loadClusterDefinition();
57 establishSelfIdentity(); 52 establishSelfIdentity();
58 - connectionManager = new ConnectionManager(localNode, nodesDelegate, 53 +
59 - commsDelegate, serializationService); 54 + // Start-up the comm service and prime it with the loaded nodes.
55 + communicationAdminService.startUp(localNode, nodesDelegate);
56 + for (DefaultControllerNode node : nodes.values()) {
57 + communicationAdminService.addNode(node);
58 + }
60 log.info("Started"); 59 log.info("Started");
61 } 60 }
62 61
...@@ -92,8 +91,8 @@ public class DistributedClusterStore ...@@ -92,8 +91,8 @@ public class DistributedClusterStore
92 if (localNode == null) { 91 if (localNode == null) {
93 localNode = new DefaultControllerNode(new NodeId(ip.toString()), ip); 92 localNode = new DefaultControllerNode(new NodeId(ip.toString()), ip);
94 nodes.put(localNode.id(), localNode); 93 nodes.put(localNode.id(), localNode);
95 - states.put(localNode.id(), State.ACTIVE);
96 } 94 }
95 + states.put(localNode.id(), State.ACTIVE);
97 } 96 }
98 97
99 @Override 98 @Override
...@@ -122,7 +121,7 @@ public class DistributedClusterStore ...@@ -122,7 +121,7 @@ public class DistributedClusterStore
122 public ControllerNode addNode(NodeId nodeId, IpPrefix ip, int tcpPort) { 121 public ControllerNode addNode(NodeId nodeId, IpPrefix ip, int tcpPort) {
123 DefaultControllerNode node = new DefaultControllerNode(nodeId, ip, tcpPort); 122 DefaultControllerNode node = new DefaultControllerNode(nodeId, ip, tcpPort);
124 nodes.put(nodeId, node); 123 nodes.put(nodeId, node);
125 - connectionManager.addNode(node); 124 + communicationAdminService.addNode(node);
126 return node; 125 return node;
127 } 126 }
128 127
...@@ -130,21 +129,25 @@ public class DistributedClusterStore ...@@ -130,21 +129,25 @@ public class DistributedClusterStore
130 public void removeNode(NodeId nodeId) { 129 public void removeNode(NodeId nodeId) {
131 DefaultControllerNode node = nodes.remove(nodeId); 130 DefaultControllerNode node = nodes.remove(nodeId);
132 if (node != null) { 131 if (node != null) {
133 - connectionManager.removeNode(node); 132 + communicationAdminService.removeNode(node);
134 } 133 }
135 } 134 }
136 135
137 // Entity to handle back calls from the connection manager. 136 // Entity to handle back calls from the connection manager.
138 private class InnerNodesDelegate implements ClusterNodesDelegate { 137 private class InnerNodesDelegate implements ClusterNodesDelegate {
139 @Override 138 @Override
140 - public void nodeDetected(DefaultControllerNode node) { 139 + public DefaultControllerNode nodeDetected(NodeId nodeId, IpPrefix ip, int tcpPort) {
141 - nodes.put(node.id(), node); 140 + DefaultControllerNode node = nodes.get(nodeId);
142 - states.put(node.id(), State.ACTIVE); 141 + if (node == null) {
142 + node = (DefaultControllerNode) addNode(nodeId, ip, tcpPort);
143 + }
144 + states.put(nodeId, State.ACTIVE);
145 + return node;
143 } 146 }
144 -
145 @Override 147 @Override
146 - public void nodeVanished(DefaultControllerNode node) { 148 + public void nodeVanished(NodeId nodeId) {
147 - states.put(node.id(), State.INACTIVE); 149 + states.put(nodeId, State.INACTIVE);
148 } 150 }
149 } 151 }
152 +
150 } 153 }
......
1 -package org.onlab.onos.store.cluster.impl;
2 -
3 -import org.onlab.onos.cluster.NodeId;
4 -import org.onlab.onos.store.cluster.messaging.ClusterMessage;
5 -
6 -/**
7 - * Created by tom on 9/29/14.
8 - */
9 -public interface MessageSender {
10 -
11 - /**
12 - * Sends the specified message to the given cluster node.
13 - *
14 - * @param nodeId node identifier
15 - * @param message mesage to send
16 - * @return true if the message was sent sucessfully; false if there is
17 - * no stream or if there was an error
18 - */
19 - boolean send(NodeId nodeId, ClusterMessage message);
20 -
21 -}
1 -package org.onlab.onos.store.cluster.impl;
2 -
3 -/**
4 - * Provides means to find a worker IO loop.
5 - */
6 -public interface WorkerFinder {
7 -
8 - /**
9 - * Finds a suitable worker.
10 - *
11 - * @return available worker
12 - */
13 - ClusterIOWorker findWorker();
14 -}
...@@ -29,9 +29,9 @@ public class HelloMessage extends ClusterMessage { ...@@ -29,9 +29,9 @@ public class HelloMessage extends ClusterMessage {
29 */ 29 */
30 public HelloMessage(NodeId nodeId, IpPrefix ipAddress, int tcpPort) { 30 public HelloMessage(NodeId nodeId, IpPrefix ipAddress, int tcpPort) {
31 super(MessageSubject.HELLO); 31 super(MessageSubject.HELLO);
32 - nodeId = nodeId; 32 + this.nodeId = nodeId;
33 - ipAddress = ipAddress; 33 + this.ipAddress = ipAddress;
34 - tcpPort = tcpPort; 34 + this.tcpPort = tcpPort;
35 } 35 }
36 36
37 /** 37 /**
...@@ -60,4 +60,5 @@ public class HelloMessage extends ClusterMessage { ...@@ -60,4 +60,5 @@ public class HelloMessage extends ClusterMessage {
60 public int tcpPort() { 60 public int tcpPort() {
61 return tcpPort; 61 return tcpPort;
62 } 62 }
63 +
63 } 64 }
......
1 package org.onlab.onos.store.cluster.messaging; 1 package org.onlab.onos.store.cluster.messaging;
2 2
3 +import org.onlab.onos.cluster.NodeId;
4 +
3 /** 5 /**
4 * Represents a message consumer. 6 * Represents a message consumer.
5 */ 7 */
...@@ -8,8 +10,9 @@ public interface MessageSubscriber { ...@@ -8,8 +10,9 @@ public interface MessageSubscriber {
8 /** 10 /**
9 * Receives the specified cluster message. 11 * Receives the specified cluster message.
10 * 12 *
11 - * @param message message to be received 13 + * @param message message to be received
14 + * @param fromNodeId node from which the message was received
12 */ 15 */
13 - void receive(ClusterMessage message); 16 + void receive(ClusterMessage message, NodeId fromNodeId);
14 17
15 } 18 }
......
...@@ -3,12 +3,12 @@ package org.onlab.onos.store.cluster.messaging; ...@@ -3,12 +3,12 @@ package org.onlab.onos.store.cluster.messaging;
3 import java.nio.ByteBuffer; 3 import java.nio.ByteBuffer;
4 4
5 /** 5 /**
6 - * Service for serializing/deserializing intra-cluster messages. 6 + * Service for encoding &amp; decoding intra-cluster messages.
7 */ 7 */
8 public interface SerializationService { 8 public interface SerializationService {
9 9
10 /** 10 /**
11 - * Decodes the specified byte buffer to obtain a message within. 11 + * Decodes the specified byte buffer to obtain the message within.
12 * 12 *
13 * @param buffer byte buffer with message(s) 13 * @param buffer byte buffer with message(s)
14 * @return parsed message 14 * @return parsed message
......
1 -package org.onlab.onos.store.cluster.messaging.impl;
2 -
3 -import com.google.common.collect.HashMultimap;
4 -import com.google.common.collect.ImmutableSet;
5 -import com.google.common.collect.Multimap;
6 -import org.apache.felix.scr.annotations.Component;
7 -import org.apache.felix.scr.annotations.Service;
8 -import org.onlab.onos.cluster.NodeId;
9 -import org.onlab.onos.store.cluster.impl.CommunicationsDelegate;
10 -import org.onlab.onos.store.cluster.impl.MessageSender;
11 -import org.onlab.onos.store.cluster.messaging.ClusterCommunicationService;
12 -import org.onlab.onos.store.cluster.messaging.ClusterMessage;
13 -import org.onlab.onos.store.cluster.messaging.MessageSubject;
14 -import org.onlab.onos.store.cluster.messaging.MessageSubscriber;
15 -
16 -import java.util.Set;
17 -
18 -/**
19 - * Implements the cluster communication services to use by other stores.
20 - */
21 -@Component(immediate = true)
22 -@Service
23 -public class ClusterCommunicationManager
24 - implements ClusterCommunicationService, CommunicationsDelegate {
25 -
26 - // TODO: use something different that won't require synchronization
27 - private Multimap<MessageSubject, MessageSubscriber> subscribers = HashMultimap.create();
28 - private MessageSender messageSender;
29 -
30 - @Override
31 - public boolean send(ClusterMessage message, NodeId toNodeId) {
32 - return messageSender.send(toNodeId, message);
33 - }
34 -
35 - @Override
36 - public synchronized void addSubscriber(MessageSubject subject, MessageSubscriber subscriber) {
37 - subscribers.put(subject, subscriber);
38 - }
39 -
40 - @Override
41 - public synchronized void removeSubscriber(MessageSubject subject, MessageSubscriber subscriber) {
42 - subscribers.remove(subject, subscriber);
43 - }
44 -
45 - @Override
46 - public Set<MessageSubscriber> getSubscribers(MessageSubject subject) {
47 - return ImmutableSet.copyOf(subscribers.get(subject));
48 - }
49 -
50 - @Override
51 - public void dispatch(ClusterMessage message) {
52 - Set<MessageSubscriber> set = getSubscribers(message.subject());
53 - if (set != null) {
54 - for (MessageSubscriber subscriber : set) {
55 - subscriber.receive(message);
56 - }
57 - }
58 - }
59 -
60 - @Override
61 - public void setSender(MessageSender messageSender) {
62 - this.messageSender = messageSender;
63 - }
64 -}
1 package org.onlab.onos.store.cluster.messaging.impl; 1 package org.onlab.onos.store.cluster.messaging.impl;
2 2
3 +import org.apache.felix.scr.annotations.Component;
4 +import org.apache.felix.scr.annotations.Service;
5 +import org.onlab.onos.cluster.NodeId;
3 import org.onlab.onos.store.cluster.messaging.ClusterMessage; 6 import org.onlab.onos.store.cluster.messaging.ClusterMessage;
7 +import org.onlab.onos.store.cluster.messaging.HelloMessage;
4 import org.onlab.onos.store.cluster.messaging.MessageSubject; 8 import org.onlab.onos.store.cluster.messaging.MessageSubject;
5 import org.onlab.onos.store.cluster.messaging.SerializationService; 9 import org.onlab.onos.store.cluster.messaging.SerializationService;
10 +import org.onlab.packet.IpPrefix;
11 +import org.slf4j.Logger;
12 +import org.slf4j.LoggerFactory;
6 13
7 import java.nio.ByteBuffer; 14 import java.nio.ByteBuffer;
8 15
...@@ -11,8 +18,12 @@ import static com.google.common.base.Preconditions.checkState; ...@@ -11,8 +18,12 @@ import static com.google.common.base.Preconditions.checkState;
11 /** 18 /**
12 * Factory for parsing messages sent between cluster members. 19 * Factory for parsing messages sent between cluster members.
13 */ 20 */
21 +@Component(immediate = true)
22 +@Service
14 public class MessageSerializer implements SerializationService { 23 public class MessageSerializer implements SerializationService {
15 24
25 + private final Logger log = LoggerFactory.getLogger(getClass());
26 +
16 private static final int METADATA_LENGTH = 16; // 8 + 4 + 4 27 private static final int METADATA_LENGTH = 16; // 8 + 4 + 4
17 private static final int LENGTH_OFFSET = 12; 28 private static final int LENGTH_OFFSET = 12;
18 29
...@@ -46,11 +57,12 @@ public class MessageSerializer implements SerializationService { ...@@ -46,11 +57,12 @@ public class MessageSerializer implements SerializationService {
46 buffer.get(data); 57 buffer.get(data);
47 58
48 // TODO: add deserialization hook here; for now this hack 59 // TODO: add deserialization hook here; for now this hack
49 - return null; // actually deserialize 60 + String[] fields = new String(data).split(":");
61 + return new HelloMessage(new NodeId(fields[0]), IpPrefix.valueOf(fields[1]), Integer.parseInt(fields[2]));
50 62
51 } catch (Exception e) { 63 } catch (Exception e) {
52 // TODO: recover from exceptions by forwarding stream to next marker 64 // TODO: recover from exceptions by forwarding stream to next marker
53 - e.printStackTrace(); 65 + log.warn("Unable to decode message due to: " + e);
54 } 66 }
55 return null; 67 return null;
56 } 68 }
...@@ -58,11 +70,18 @@ public class MessageSerializer implements SerializationService { ...@@ -58,11 +70,18 @@ public class MessageSerializer implements SerializationService {
58 @Override 70 @Override
59 public void encode(ClusterMessage message, ByteBuffer buffer) { 71 public void encode(ClusterMessage message, ByteBuffer buffer) {
60 try { 72 try {
61 - int i = 0; 73 + HelloMessage helloMessage = (HelloMessage) message;
62 - // Type based lookup for proper encoder 74 + buffer.putLong(MARKER);
75 + buffer.putInt(message.subject().ordinal());
76 +
77 + String str = helloMessage.nodeId() + ":" + helloMessage.ipAddress() + ":" + helloMessage.tcpPort();
78 + byte[] data = str.getBytes();
79 + buffer.putInt(data.length + METADATA_LENGTH);
80 + buffer.put(data);
81 +
63 } catch (Exception e) { 82 } catch (Exception e) {
64 // TODO: recover from exceptions by forwarding stream to next marker 83 // TODO: recover from exceptions by forwarding stream to next marker
65 - e.printStackTrace(); 84 + log.warn("Unable to encode message due to: " + e);
66 } 85 }
67 } 86 }
68 87
......
...@@ -2,9 +2,9 @@ ...@@ -2,9 +2,9 @@
2 2
3 export ONOS_NIC=192.168.56.* 3 export ONOS_NIC=192.168.56.*
4 4
5 -export OC1="192.168.56.101" 5 +export OC1="192.168.56.11"
6 -export OC2="192.168.56.102" 6 +export OC2="192.168.56.12"
7 7
8 -export OCN="192.168.56.105" 8 +export OCN="192.168.56.7"
9 9
10 10
......