alshabib

Merge branch 'master' of ssh://gerrit.onlab.us:29418/onos-next

Showing 80 changed files with 2155 additions and 672 deletions
...@@ -24,6 +24,11 @@ ...@@ -24,6 +24,11 @@
24 </dependency> 24 </dependency>
25 <dependency> 25 <dependency>
26 <groupId>org.onlab.onos</groupId> 26 <groupId>org.onlab.onos</groupId>
27 + <artifactId>onlab-osgi</artifactId>
28 + <version>${project.version}</version>
29 + </dependency>
30 + <dependency>
31 + <groupId>org.onlab.onos</groupId>
27 <artifactId>onlab-nio</artifactId> 32 <artifactId>onlab-nio</artifactId>
28 <version>${project.version}</version> 33 <version>${project.version}</version>
29 </dependency> 34 </dependency>
......
1 +package org.onlab.onos.foo;
2 +
3 +import java.io.IOException;
4 +
5 +import org.onlab.netty.Message;
6 +import org.onlab.netty.MessageHandler;
7 +import org.slf4j.Logger;
8 +import org.slf4j.LoggerFactory;
9 +
10 +
11 +/**
12 + * Message handler that echos the message back to the sender.
13 + */
14 +public class NettyEchoHandler implements MessageHandler {
15 +
16 + private final Logger log = LoggerFactory.getLogger(getClass());
17 +
18 + @Override
19 + public void handle(Message message) throws IOException {
20 + //log.info("Received message. Echoing it back to the sender.");
21 + message.respond(message.payload());
22 + }
23 +}
1 +package org.onlab.onos.foo;
2 +
3 +import org.onlab.netty.Message;
4 +import org.onlab.netty.MessageHandler;
5 +import org.slf4j.Logger;
6 +import org.slf4j.LoggerFactory;
7 +
8 +/**
9 + * A MessageHandler that simply logs the information.
10 + */
11 +public class NettyLoggingHandler implements MessageHandler {
12 +
13 + private final Logger log = LoggerFactory.getLogger(getClass());
14 +
15 + @Override
16 + public void handle(Message message) {
17 + //log.info("Received message. Payload has {} bytes", message.payload().length);
18 + }
19 +}
...@@ -2,7 +2,6 @@ package org.onlab.onos.foo; ...@@ -2,7 +2,6 @@ package org.onlab.onos.foo;
2 2
3 import java.io.IOException; 3 import java.io.IOException;
4 import java.util.concurrent.ExecutionException; 4 import java.util.concurrent.ExecutionException;
5 -import java.util.concurrent.TimeUnit;
6 import java.util.concurrent.TimeoutException; 5 import java.util.concurrent.TimeoutException;
7 6
8 import org.onlab.metrics.MetricsComponent; 7 import org.onlab.metrics.MetricsComponent;
...@@ -11,11 +10,16 @@ import org.onlab.metrics.MetricsManager; ...@@ -11,11 +10,16 @@ import org.onlab.metrics.MetricsManager;
11 import org.onlab.netty.Endpoint; 10 import org.onlab.netty.Endpoint;
12 import org.onlab.netty.NettyMessagingService; 11 import org.onlab.netty.NettyMessagingService;
13 import org.onlab.netty.Response; 12 import org.onlab.netty.Response;
13 +import org.slf4j.Logger;
14 +import org.slf4j.LoggerFactory;
14 15
15 import com.codahale.metrics.Timer; 16 import com.codahale.metrics.Timer;
16 17
17 // FIXME: Should be move out to test or app 18 // FIXME: Should be move out to test or app
18 public final class SimpleNettyClient { 19 public final class SimpleNettyClient {
20 +
21 +private static Logger log = LoggerFactory.getLogger(SimpleNettyClient.class);
22 +
19 private SimpleNettyClient() { 23 private SimpleNettyClient() {
20 } 24 }
21 25
...@@ -30,33 +34,46 @@ public final class SimpleNettyClient { ...@@ -30,33 +34,46 @@ public final class SimpleNettyClient {
30 34
31 System.exit(0); 35 System.exit(0);
32 } 36 }
33 - public static void startStandalone(String... args) throws Exception { 37 + public static void startStandalone(String[] args) throws Exception {
38 + String host = args.length > 0 ? args[0] : "localhost";
39 + int port = args.length > 1 ? Integer.parseInt(args[1]) : 8081;
40 + int warmup = args.length > 2 ? Integer.parseInt(args[2]) : 1000;
41 + int iterations = args.length > 3 ? Integer.parseInt(args[3]) : 50 * 100000;
34 NettyMessagingService messaging = new TestNettyMessagingService(9081); 42 NettyMessagingService messaging = new TestNettyMessagingService(9081);
35 MetricsManager metrics = new MetricsManager(); 43 MetricsManager metrics = new MetricsManager();
44 + Endpoint endpoint = new Endpoint(host, port);
36 messaging.activate(); 45 messaging.activate();
37 metrics.activate(); 46 metrics.activate();
38 - MetricsFeature feature = new MetricsFeature("timers"); 47 + MetricsFeature feature = new MetricsFeature("latency");
39 MetricsComponent component = metrics.registerComponent("NettyMessaging"); 48 MetricsComponent component = metrics.registerComponent("NettyMessaging");
40 - Timer sendAsyncTimer = metrics.createTimer(component, feature, "AsyncSender"); 49 + log.info("connecting " + host + ":" + port + " warmup:" + warmup + " iterations:" + iterations);
41 - final int warmup = 100; 50 +
42 for (int i = 0; i < warmup; i++) { 51 for (int i = 0; i < warmup; i++) {
52 + messaging.sendAsync(endpoint, "simple", "Hello World".getBytes());
53 + Response response = messaging
54 + .sendAndReceive(endpoint, "echo",
55 + "Hello World".getBytes());
56 + }
57 +
58 + log.info("measuring async sender");
59 + Timer sendAsyncTimer = metrics.createTimer(component, feature, "AsyncSender");
60 +
61 + for (int i = 0; i < iterations; i++) {
43 Timer.Context context = sendAsyncTimer.time(); 62 Timer.Context context = sendAsyncTimer.time();
44 - messaging.sendAsync(new Endpoint("localhost", 8080), "simple", "Hello World".getBytes()); 63 + messaging.sendAsync(endpoint, "simple", "Hello World".getBytes());
45 context.stop(); 64 context.stop();
46 } 65 }
47 - metrics.registerMetric(component, feature, "AsyncTimer", sendAsyncTimer);
48 66
49 Timer sendAndReceiveTimer = metrics.createTimer(component, feature, "SendAndReceive"); 67 Timer sendAndReceiveTimer = metrics.createTimer(component, feature, "SendAndReceive");
50 - final int iterations = 1000000;
51 for (int i = 0; i < iterations; i++) { 68 for (int i = 0; i < iterations; i++) {
52 Timer.Context context = sendAndReceiveTimer.time(); 69 Timer.Context context = sendAndReceiveTimer.time();
53 Response response = messaging 70 Response response = messaging
54 - .sendAndReceive(new Endpoint("localhost", 8080), "echo", 71 + .sendAndReceive(endpoint, "echo",
55 "Hello World".getBytes()); 72 "Hello World".getBytes());
56 - System.out.println("Got back:" + new String(response.get(2, TimeUnit.SECONDS))); 73 + // System.out.println("Got back:" + new String(response.get(2, TimeUnit.SECONDS)));
57 context.stop(); 74 context.stop();
58 } 75 }
59 - metrics.registerMetric(component, feature, "AsyncTimer", sendAndReceiveTimer); 76 + metrics.deactivate();
60 } 77 }
61 78
62 public static class TestNettyMessagingService extends NettyMessagingService { 79 public static class TestNettyMessagingService extends NettyMessagingService {
......
...@@ -13,33 +13,29 @@ import org.onlab.onos.cli.AbstractShellCommand; ...@@ -13,33 +13,29 @@ import org.onlab.onos.cli.AbstractShellCommand;
13 description = "Starts the simple Netty client") 13 description = "Starts the simple Netty client")
14 public class SimpleNettyClientCommand extends AbstractShellCommand { 14 public class SimpleNettyClientCommand extends AbstractShellCommand {
15 15
16 - @Argument(index = 0, name = "serverIp", description = "Server IP address", 16 + //FIXME: replace these arguments with proper ones needed for the test.
17 + @Argument(index = 0, name = "hostname", description = "Server Hostname",
17 required = false, multiValued = false) 18 required = false, multiValued = false)
18 - String serverIp = "127.0.0.1"; 19 + String hostname = "localhost";
19 20
20 - @Argument(index = 1, name = "workers", description = "IO workers", 21 + @Argument(index = 1, name = "port", description = "Port",
21 required = false, multiValued = false) 22 required = false, multiValued = false)
22 - String workers = "6"; 23 + String port = "8081";
23 24
24 - @Argument(index = 2, name = "messageCount", description = "Message count", 25 + @Argument(index = 2, name = "warmupCount", description = "Warm-up count",
25 required = false, multiValued = false) 26 required = false, multiValued = false)
26 - String messageCount = "1000000"; 27 + String warmupCount = "1000";
27 28
28 - @Argument(index = 3, name = "messageLength", description = "Message length (bytes)", 29 + @Argument(index = 3, name = "messageCount", description = "Message count",
29 required = false, multiValued = false) 30 required = false, multiValued = false)
30 - String messageLength = "128"; 31 + String messageCount = "100000";
31 -
32 - @Argument(index = 4, name = "timeoutSecs", description = "Test timeout (seconds)",
33 - required = false, multiValued = false)
34 - String timeoutSecs = "60";
35 32
36 @Override 33 @Override
37 protected void execute() { 34 protected void execute() {
38 try { 35 try {
39 - startStandalone(new String[]{serverIp, workers, messageCount, messageLength, timeoutSecs}); 36 + startStandalone(new String[]{hostname, port, warmupCount, messageCount});
40 } catch (Exception e) { 37 } catch (Exception e) {
41 error("Unable to start client %s", e); 38 error("Unable to start client %s", e);
42 } 39 }
43 } 40 }
44 -
45 } 41 }
......
1 package org.onlab.onos.foo; 1 package org.onlab.onos.foo;
2 2
3 -import org.onlab.netty.EchoHandler;
4 import org.onlab.netty.NettyMessagingService; 3 import org.onlab.netty.NettyMessagingService;
5 import org.slf4j.Logger; 4 import org.slf4j.Logger;
6 import org.slf4j.LoggerFactory; 5 import org.slf4j.LoggerFactory;
...@@ -9,7 +8,7 @@ import org.slf4j.LoggerFactory; ...@@ -9,7 +8,7 @@ import org.slf4j.LoggerFactory;
9 * Test to measure Messaging performance. 8 * Test to measure Messaging performance.
10 */ 9 */
11 public final class SimpleNettyServer { 10 public final class SimpleNettyServer {
12 - private static Logger log = LoggerFactory.getLogger(IOLoopTestServer.class); 11 + private static Logger log = LoggerFactory.getLogger(SimpleNettyServer.class);
13 12
14 private SimpleNettyServer() {} 13 private SimpleNettyServer() {}
15 14
...@@ -19,10 +18,10 @@ import org.slf4j.LoggerFactory; ...@@ -19,10 +18,10 @@ import org.slf4j.LoggerFactory;
19 } 18 }
20 19
21 public static void startStandalone(String[] args) throws Exception { 20 public static void startStandalone(String[] args) throws Exception {
22 - NettyMessagingService server = new NettyMessagingService(8080); 21 + NettyMessagingService server = new NettyMessagingService(8081);
23 server.activate(); 22 server.activate();
24 - server.registerHandler("simple", new org.onlab.netty.LoggingHandler()); 23 + server.registerHandler("simple", new NettyLoggingHandler());
25 - server.registerHandler("echo", new EchoHandler()); 24 + server.registerHandler("echo", new NettyEchoHandler());
26 } 25 }
27 } 26 }
28 27
......
...@@ -9,10 +9,11 @@ import org.onlab.onos.cli.AbstractShellCommand; ...@@ -9,10 +9,11 @@ import org.onlab.onos.cli.AbstractShellCommand;
9 /** 9 /**
10 * Starts the Simple Netty server. 10 * Starts the Simple Netty server.
11 */ 11 */
12 -@Command(scope = "onos", name = "test-netty-server", 12 +@Command(scope = "onos", name = "simple-netty-server",
13 description = "Starts the simple netty server") 13 description = "Starts the simple netty server")
14 public class SimpleNettyServerCommand extends AbstractShellCommand { 14 public class SimpleNettyServerCommand extends AbstractShellCommand {
15 15
16 + //FIXME: Replace these with parameters for
16 @Argument(index = 0, name = "serverIp", description = "Server IP address", 17 @Argument(index = 0, name = "serverIp", description = "Server IP address",
17 required = false, multiValued = false) 18 required = false, multiValued = false)
18 String serverIp = "127.0.0.1"; 19 String serverIp = "127.0.0.1";
......
...@@ -7,6 +7,12 @@ ...@@ -7,6 +7,12 @@
7 <command> 7 <command>
8 <action class="org.onlab.onos.foo.TestIOServerCommand"/> 8 <action class="org.onlab.onos.foo.TestIOServerCommand"/>
9 </command> 9 </command>
10 + <command>
11 + <action class="org.onlab.onos.foo.SimpleNettyServerCommand"/>
12 + </command>
13 + <command>
14 + <action class="org.onlab.onos.foo.SimpleNettyClientCommand"/>
15 + </command>
10 </command-bundle> 16 </command-bundle>
11 17
12 </blueprint> 18 </blueprint>
......
1 package org.onlab.onos.cli; 1 package org.onlab.onos.cli;
2 2
3 import org.apache.karaf.shell.commands.Command; 3 import org.apache.karaf.shell.commands.Command;
4 +import org.onlab.onos.CoreService;
4 import org.onlab.onos.cluster.ClusterService; 5 import org.onlab.onos.cluster.ClusterService;
5 import org.onlab.onos.net.device.DeviceService; 6 import org.onlab.onos.net.device.DeviceService;
6 import org.onlab.onos.net.flow.FlowRuleService; 7 import org.onlab.onos.net.flow.FlowRuleService;
...@@ -21,7 +22,8 @@ public class SummaryCommand extends AbstractShellCommand { ...@@ -21,7 +22,8 @@ public class SummaryCommand extends AbstractShellCommand {
21 protected void execute() { 22 protected void execute() {
22 TopologyService topologyService = get(TopologyService.class); 23 TopologyService topologyService = get(TopologyService.class);
23 Topology topology = topologyService.currentTopology(); 24 Topology topology = topologyService.currentTopology();
24 - print("nodes=%d, devices=%d, links=%d, hosts=%d, clusters=%s, paths=%d, flows=%d, intents=%d", 25 + print("version=%s, nodes=%d, devices=%d, links=%d, hosts=%d, clusters=%s, paths=%d, flows=%d, intents=%d",
26 + get(CoreService.class).version().toString(),
25 get(ClusterService.class).getNodes().size(), 27 get(ClusterService.class).getNodes().size(),
26 get(DeviceService.class).getDeviceCount(), 28 get(DeviceService.class).getDeviceCount(),
27 get(LinkService.class).getLinkCount(), 29 get(LinkService.class).getLinkCount(),
......
...@@ -27,7 +27,7 @@ public class AddHostToHostIntentCommand extends AbstractShellCommand { ...@@ -27,7 +27,7 @@ public class AddHostToHostIntentCommand extends AbstractShellCommand {
27 required = true, multiValued = false) 27 required = true, multiValued = false)
28 String two = null; 28 String two = null;
29 29
30 - private static long id = 1; 30 + private static long id = 0x7870001;
31 31
32 @Override 32 @Override
33 protected void execute() { 33 protected void execute() {
......
1 +package org.onlab.onos.cli.net;
2 +
3 +import org.apache.karaf.shell.commands.Argument;
4 +import org.apache.karaf.shell.commands.Command;
5 +import org.onlab.onos.cli.AbstractShellCommand;
6 +import org.onlab.onos.net.ConnectPoint;
7 +import org.onlab.onos.net.DeviceId;
8 +import org.onlab.onos.net.PortNumber;
9 +import org.onlab.onos.net.flow.DefaultTrafficSelector;
10 +import org.onlab.onos.net.flow.DefaultTrafficTreatment;
11 +import org.onlab.onos.net.flow.TrafficSelector;
12 +import org.onlab.onos.net.flow.TrafficTreatment;
13 +import org.onlab.onos.net.intent.Intent;
14 +import org.onlab.onos.net.intent.IntentId;
15 +import org.onlab.onos.net.intent.IntentService;
16 +import org.onlab.onos.net.intent.PointToPointIntent;
17 +import org.onlab.packet.Ethernet;
18 +
19 +/**
20 + * Installs point-to-point connectivity intents.
21 + */
22 +@Command(scope = "onos", name = "add-point-intent",
23 + description = "Installs point-to-point connectivity intent")
24 +public class AddPointToPointIntentCommand extends AbstractShellCommand {
25 +
26 + @Argument(index = 0, name = "ingressDevice",
27 + description = "Ingress Device/Port Description",
28 + required = true, multiValued = false)
29 + String ingressDeviceString = null;
30 +
31 + @Argument(index = 1, name = "egressDevice",
32 + description = "Egress Device/Port Description",
33 + required = true, multiValued = false)
34 + String egressDeviceString = null;
35 +
36 + private static long id = 0x7470001;
37 +
38 + @Override
39 + protected void execute() {
40 + IntentService service = get(IntentService.class);
41 +
42 + DeviceId ingressDeviceId = DeviceId.deviceId(getDeviceId(ingressDeviceString));
43 + PortNumber ingressPortNumber =
44 + PortNumber.portNumber(getPortNumber(ingressDeviceString));
45 + ConnectPoint ingress = new ConnectPoint(ingressDeviceId, ingressPortNumber);
46 +
47 + DeviceId egressDeviceId = DeviceId.deviceId(getDeviceId(egressDeviceString));
48 + PortNumber egressPortNumber =
49 + PortNumber.portNumber(getPortNumber(egressDeviceString));
50 + ConnectPoint egress = new ConnectPoint(egressDeviceId, egressPortNumber);
51 +
52 + TrafficSelector selector = DefaultTrafficSelector.builder()
53 + .matchEthType(Ethernet.TYPE_IPV4)
54 + .build();
55 + TrafficTreatment treatment = DefaultTrafficTreatment.builder().build();
56 +
57 + Intent intent =
58 + new PointToPointIntent(new IntentId(id++),
59 + selector,
60 + treatment,
61 + ingress,
62 + egress);
63 + service.submit(intent);
64 + }
65 +
66 + /**
67 + * Extracts the port number portion of the ConnectPoint.
68 + *
69 + * @param deviceString string representing the device/port
70 + * @return port number as a string, empty string if the port is not found
71 + */
72 + private String getPortNumber(String deviceString) {
73 + int slash = deviceString.indexOf('/');
74 + if (slash <= 0) {
75 + return "";
76 + }
77 + return deviceString.substring(slash + 1, deviceString.length());
78 + }
79 +
80 + /**
81 + * Extracts the device ID portion of the ConnectPoint.
82 + *
83 + * @param deviceString string representing the device/port
84 + * @return device ID string
85 + */
86 + private String getDeviceId(String deviceString) {
87 + int slash = deviceString.indexOf('/');
88 + if (slash <= 0) {
89 + return "";
90 + }
91 + return deviceString.substring(0, slash);
92 + }
93 +}
1 +package org.onlab.onos.cli.net;
2 +
3 +import java.util.List;
4 +import java.util.SortedSet;
5 +
6 +import org.apache.karaf.shell.console.Completer;
7 +import org.apache.karaf.shell.console.completer.StringsCompleter;
8 +import org.onlab.onos.cli.AbstractShellCommand;
9 +import org.onlab.onos.net.Device;
10 +import org.onlab.onos.net.Port;
11 +import org.onlab.onos.net.device.DeviceService;
12 +
13 +/**
14 + * ConnectPoint completer.
15 + */
16 +public class ConnectPointCompleter implements Completer {
17 + @Override
18 + public int complete(String buffer, int cursor, List<String> candidates) {
19 + // Delegate string completer
20 + StringsCompleter delegate = new StringsCompleter();
21 +
22 + // Fetch our service and feed it's offerings to the string completer
23 + DeviceService service = AbstractShellCommand.get(DeviceService.class);
24 +
25 + // Generate the device ID/port number identifiers
26 + for (Device device : service.getDevices()) {
27 + SortedSet<String> strings = delegate.getStrings();
28 +
29 + for (Port port : service.getPorts(device.id())) {
30 + strings.add(device.id().toString() + "/" + port.number());
31 + }
32 + }
33 +
34 + // Now let the completer do the work for figuring out what to offer.
35 + return delegate.complete(buffer, cursor, candidates);
36 + }
37 +
38 +}
...@@ -13,40 +13,41 @@ import org.onlab.onos.net.intent.IntentService; ...@@ -13,40 +13,41 @@ import org.onlab.onos.net.intent.IntentService;
13 import org.onlab.onos.net.intent.IntentState; 13 import org.onlab.onos.net.intent.IntentState;
14 14
15 /** 15 /**
16 - * Wipes-out the entire network information base, i.e. devices, links, hosts. 16 + * Wipes-out the entire network information base, i.e. devices, links, hosts, intents.
17 */ 17 */
18 @Command(scope = "onos", name = "wipe-out", 18 @Command(scope = "onos", name = "wipe-out",
19 description = "Wipes-out the entire network information base, i.e. devices, links, hosts") 19 description = "Wipes-out the entire network information base, i.e. devices, links, hosts")
20 public class WipeOutCommand extends ClustersListCommand { 20 public class WipeOutCommand extends ClustersListCommand {
21 21
22 - 22 + private static final String DISCLAIMER = "Delete everything please.";
23 - private static final String DISCLAIMER = "Yes, I know it will delete everything!";
24 23
25 @Argument(index = 0, name = "disclaimer", description = "Device ID", 24 @Argument(index = 0, name = "disclaimer", description = "Device ID",
26 - required = true, multiValued = false) 25 + required = false, multiValued = false)
27 String disclaimer = null; 26 String disclaimer = null;
28 27
29 @Override 28 @Override
30 protected void execute() { 29 protected void execute() {
31 - if (!disclaimer.equals(DISCLAIMER)) { 30 + if (disclaimer == null || !disclaimer.equals(DISCLAIMER)) {
32 - print("I'm afraid I can't do that..."); 31 + print("I'm afraid I can't do that!\nPlease acknowledge with phrase: '%s'",
33 - print("You have to acknowledge by: " + DISCLAIMER); 32 + DISCLAIMER);
34 return; 33 return;
35 } 34 }
36 35
37 - print("Good bye..."); 36 + print("Wiping devices");
38 DeviceAdminService deviceAdminService = get(DeviceAdminService.class); 37 DeviceAdminService deviceAdminService = get(DeviceAdminService.class);
39 DeviceService deviceService = get(DeviceService.class); 38 DeviceService deviceService = get(DeviceService.class);
40 for (Device device : deviceService.getDevices()) { 39 for (Device device : deviceService.getDevices()) {
41 deviceAdminService.removeDevice(device.id()); 40 deviceAdminService.removeDevice(device.id());
42 } 41 }
43 42
43 + print("Wiping hosts");
44 HostAdminService hostAdminService = get(HostAdminService.class); 44 HostAdminService hostAdminService = get(HostAdminService.class);
45 HostService hostService = get(HostService.class); 45 HostService hostService = get(HostService.class);
46 for (Host host : hostService.getHosts()) { 46 for (Host host : hostService.getHosts()) {
47 hostAdminService.removeHost(host.id()); 47 hostAdminService.removeHost(host.id());
48 } 48 }
49 49
50 + print("Wiping intents");
50 IntentService intentService = get(IntentService.class); 51 IntentService intentService = get(IntentService.class);
51 for (Intent intent : intentService.getIntents()) { 52 for (Intent intent : intentService.getIntents()) {
52 if (intentService.getIntentState(intent.id()) == IntentState.INSTALLED) { 53 if (intentService.getIntentState(intent.id()) == IntentState.INSTALLED) {
......
...@@ -75,6 +75,13 @@ ...@@ -75,6 +75,13 @@
75 <ref component-id="hostIdCompleter"/> 75 <ref component-id="hostIdCompleter"/>
76 </completers> 76 </completers>
77 </command> 77 </command>
78 + <command>
79 + <action class="org.onlab.onos.cli.net.AddPointToPointIntentCommand"/>
80 + <completers>
81 + <ref component-id="connectPointCompleter"/>
82 + <ref component-id="connectPointCompleter"/>
83 + </completers>
84 + </command>
78 85
79 <command> 86 <command>
80 <action class="org.onlab.onos.cli.net.ClustersListCommand"/> 87 <action class="org.onlab.onos.cli.net.ClustersListCommand"/>
...@@ -116,5 +123,6 @@ ...@@ -116,5 +123,6 @@
116 <bean id="hostIdCompleter" class="org.onlab.onos.cli.net.HostIdCompleter"/> 123 <bean id="hostIdCompleter" class="org.onlab.onos.cli.net.HostIdCompleter"/>
117 <bean id="intentIdCompleter" class="org.onlab.onos.cli.net.IntentIdCompleter"/> 124 <bean id="intentIdCompleter" class="org.onlab.onos.cli.net.IntentIdCompleter"/>
118 <bean id="flowRuleStatusCompleter" class="org.onlab.onos.cli.net.FlowRuleStatusCompleter"/> 125 <bean id="flowRuleStatusCompleter" class="org.onlab.onos.cli.net.FlowRuleStatusCompleter"/>
126 + <bean id="connectPointCompleter" class="org.onlab.onos.cli.net.ConnectPointCompleter"/>
119 127
120 </blueprint> 128 </blueprint>
......
1 +package org.onlab.onos;
2 +
3 +/**
4 + * Service for interacting with the core system of the controller.
5 + */
6 +public interface CoreService {
7 +
8 + /**
9 + * Returns the product version.
10 + *
11 + * @return product version
12 + */
13 + Version version();
14 +
15 +}
1 +package org.onlab.onos;
2 +
3 +import java.util.Objects;
4 +
5 +import static java.lang.Integer.parseInt;
6 +
7 +/**
8 + * Representation of the product version.
9 + */
10 +public final class Version {
11 +
12 + public static final String FORMAT = "%d.%d.%d.%s";
13 +
14 + private final int major;
15 + private final int minor;
16 + private final int patch;
17 + private final String build;
18 +
19 + private final String format;
20 +
21 + // Creates a new version descriptor
22 + private Version(int major, int minor, int patch, String build) {
23 + this.major = major;
24 + this.minor = minor;
25 + this.patch = patch;
26 + this.build = build;
27 + this.format = String.format(FORMAT, major, minor, patch, build);
28 + }
29 +
30 +
31 + /**
32 + * Creates a new version from the specified constituent numbers.
33 + *
34 + * @param major major version number
35 + * @param minor minod version number
36 + * @param patch version patch number
37 + * @param build build string
38 + * @return version descriptor
39 + */
40 + public static Version version(int major, int minor, int patch, String build) {
41 + return new Version(major, minor, patch, build);
42 + }
43 +
44 + /**
45 + * Creates a new version by parsing the specified string.
46 + *
47 + * @param string version string
48 + * @return version descriptor
49 + */
50 + public static Version version(String string) {
51 + String[] fields = string.split("[.-]");
52 + return new Version(parseInt(fields[0]), parseInt(fields[1]),
53 + parseInt(fields[2]), fields[3]);
54 + }
55 +
56 + /**
57 + * Returns the major version number.
58 + *
59 + * @return major version number
60 + */
61 + public int major() {
62 + return major;
63 + }
64 +
65 + /**
66 + * Returns the minor version number.
67 + *
68 + * @return minor version number
69 + */
70 + public int minor() {
71 + return minor;
72 + }
73 +
74 + /**
75 + * Returns the version patch number.
76 + *
77 + * @return patch number
78 + */
79 + public int patch() {
80 + return patch;
81 + }
82 +
83 + /**
84 + * Returns the version build string.
85 + *
86 + * @return build string
87 + */
88 + public String build() {
89 + return build;
90 + }
91 +
92 + @Override
93 + public String toString() {
94 + return format;
95 + }
96 +
97 + @Override
98 + public int hashCode() {
99 + return Objects.hash(format);
100 + }
101 +
102 + @Override
103 + public boolean equals(Object obj) {
104 + if (this == obj) {
105 + return true;
106 + }
107 + if (obj instanceof Version) {
108 + final Version other = (Version) obj;
109 + return Objects.equals(this.format, other.format);
110 + }
111 + return false;
112 + }
113 +}
...@@ -34,7 +34,7 @@ public interface MastershipService { ...@@ -34,7 +34,7 @@ public interface MastershipService {
34 /** 34 /**
35 * Abandons mastership of the specified device on the local node thus 35 * Abandons mastership of the specified device on the local node thus
36 * forcing selection of a new master. If the local node is not a master 36 * forcing selection of a new master. If the local node is not a master
37 - * for this device, no action will be taken. 37 + * for this device, no master selection will occur.
38 * 38 *
39 * @param deviceId the identifier of the device 39 * @param deviceId the identifier of the device
40 */ 40 */
......
...@@ -66,12 +66,25 @@ public interface MastershipStore extends Store<MastershipEvent, MastershipStoreD ...@@ -66,12 +66,25 @@ public interface MastershipStore extends Store<MastershipEvent, MastershipStoreD
66 MastershipTerm getTermFor(DeviceId deviceId); 66 MastershipTerm getTermFor(DeviceId deviceId);
67 67
68 /** 68 /**
69 - * Revokes a controller instance's mastership over a device and hands 69 + * Sets a controller instance's mastership role to STANDBY for a device.
70 - * over mastership to another controller instance. 70 + * If the role is MASTER, another controller instance will be selected
71 + * as a candidate master.
71 * 72 *
72 * @param nodeId the controller instance identifier 73 * @param nodeId the controller instance identifier
73 - * @param deviceId device to revoke mastership for 74 + * @param deviceId device to revoke mastership role for
74 * @return a mastership event 75 * @return a mastership event
75 */ 76 */
76 - MastershipEvent unsetMaster(NodeId nodeId, DeviceId deviceId); 77 + MastershipEvent setStandby(NodeId nodeId, DeviceId deviceId);
78 +
79 + /**
80 + * Allows a controller instance to give up its current role for a device.
81 + * If the role is MASTER, another controller instance will be selected
82 + * as a candidate master.
83 + *
84 + * @param nodeId the controller instance identifier
85 + * @param deviceId device to revoke mastership role for
86 + * @return a mastership event
87 + */
88 + MastershipEvent relinquishRole(NodeId nodeId, DeviceId deviceId);
89 +
77 } 90 }
......
...@@ -3,6 +3,7 @@ package org.onlab.onos.net; ...@@ -3,6 +3,7 @@ package org.onlab.onos.net;
3 import org.onlab.onos.net.provider.ProviderId; 3 import org.onlab.onos.net.provider.ProviderId;
4 4
5 import static com.google.common.base.Preconditions.checkArgument; 5 import static com.google.common.base.Preconditions.checkArgument;
6 +import static com.google.common.base.Preconditions.checkNotNull;
6 7
7 /** 8 /**
8 * Default edge link model implementation. 9 * Default edge link model implementation.
...@@ -52,10 +53,14 @@ public class DefaultEdgeLink extends DefaultLink implements EdgeLink { ...@@ -52,10 +53,14 @@ public class DefaultEdgeLink extends DefaultLink implements EdgeLink {
52 * for network-to-host direction 53 * for network-to-host direction
53 * @return new phantom edge link 54 * @return new phantom edge link
54 */ 55 */
55 - public static DefaultEdgeLink createEdgeLink(HostLocation edgePort, 56 + public static DefaultEdgeLink createEdgeLink(ConnectPoint edgePort,
56 boolean isIngress) { 57 boolean isIngress) {
58 + checkNotNull(edgePort, "Edge port cannot be null");
59 + HostLocation location = (edgePort instanceof HostLocation) ?
60 + (HostLocation) edgePort : new HostLocation(edgePort, 0);
57 return new DefaultEdgeLink(ProviderId.NONE, 61 return new DefaultEdgeLink(ProviderId.NONE,
58 new ConnectPoint(HostId.NONE, PortNumber.P0), 62 new ConnectPoint(HostId.NONE, PortNumber.P0),
59 - edgePort, isIngress); 63 + location, isIngress);
60 } 64 }
65 +
61 } 66 }
......
...@@ -22,6 +22,17 @@ public class HostLocation extends ConnectPoint { ...@@ -22,6 +22,17 @@ public class HostLocation extends ConnectPoint {
22 } 22 }
23 23
24 /** 24 /**
25 + * Creates a new host location derived from the supplied connection point.
26 + *
27 + * @param connectPoint connection point
28 + * @param time time when detected, in millis since start of epoch
29 + */
30 + public HostLocation(ConnectPoint connectPoint, long time) {
31 + super(connectPoint.deviceId(), connectPoint.port());
32 + this.time = time;
33 + }
34 +
35 + /**
25 * Returns the time when the location was established, given in 36 * Returns the time when the location was established, given in
26 * milliseconds since start of epoch. 37 * milliseconds since start of epoch.
27 * 38 *
......
...@@ -96,4 +96,13 @@ public class DefaultDeviceDescription extends AbstractDescription ...@@ -96,4 +96,13 @@ public class DefaultDeviceDescription extends AbstractDescription
96 .toString(); 96 .toString();
97 } 97 }
98 98
99 + // default constructor for serialization
100 + private DefaultDeviceDescription() {
101 + this.uri = null;
102 + this.type = null;
103 + this.manufacturer = null;
104 + this.hwVersion = null;
105 + this.swVersion = null;
106 + this.serialNumber = null;
107 + }
99 } 108 }
......
...@@ -48,4 +48,9 @@ public class DefaultPortDescription extends AbstractDescription ...@@ -48,4 +48,9 @@ public class DefaultPortDescription extends AbstractDescription
48 return isEnabled; 48 return isEnabled;
49 } 49 }
50 50
51 + // default constructor for serialization
52 + private DefaultPortDescription() {
53 + this.number = null;
54 + this.isEnabled = false;
55 + }
51 } 56 }
......
...@@ -42,6 +42,7 @@ public interface DeviceService { ...@@ -42,6 +42,7 @@ public interface DeviceService {
42 * @param deviceId device identifier 42 * @param deviceId device identifier
43 * @return designated mastership role 43 * @return designated mastership role
44 */ 44 */
45 + //XXX do we want this method here when MastershipService already does?
45 MastershipRole getRole(DeviceId deviceId); 46 MastershipRole getRole(DeviceId deviceId);
46 47
47 48
......
1 package org.onlab.onos.net.intent; 1 package org.onlab.onos.net.intent;
2 2
3 +import java.util.concurrent.Future;
4 +
5 +import org.onlab.onos.net.flow.CompletedBatchOperation;
6 +
3 /** 7 /**
4 * Abstraction of entity capable of installing intents to the environment. 8 * Abstraction of entity capable of installing intents to the environment.
5 */ 9 */
...@@ -10,7 +14,7 @@ public interface IntentInstaller<T extends InstallableIntent> { ...@@ -10,7 +14,7 @@ public interface IntentInstaller<T extends InstallableIntent> {
10 * @param intent intent to be installed 14 * @param intent intent to be installed
11 * @throws IntentException if issues are encountered while installing the intent 15 * @throws IntentException if issues are encountered while installing the intent
12 */ 16 */
13 - void install(T intent); 17 + Future<CompletedBatchOperation> install(T intent);
14 18
15 /** 19 /**
16 * Uninstalls the specified intent from the environment. 20 * Uninstalls the specified intent from the environment.
...@@ -18,5 +22,5 @@ public interface IntentInstaller<T extends InstallableIntent> { ...@@ -18,5 +22,5 @@ public interface IntentInstaller<T extends InstallableIntent> {
18 * @param intent intent to be uninstalled 22 * @param intent intent to be uninstalled
19 * @throws IntentException if issues are encountered while uninstalling the intent 23 * @throws IntentException if issues are encountered while uninstalling the intent
20 */ 24 */
21 - void uninstall(T intent); 25 + Future<CompletedBatchOperation> uninstall(T intent);
22 } 26 }
......
...@@ -12,6 +12,7 @@ public interface ClockProviderService { ...@@ -12,6 +12,7 @@ public interface ClockProviderService {
12 12
13 /** 13 /**
14 * Updates the mastership term for the specified deviceId. 14 * Updates the mastership term for the specified deviceId.
15 + *
15 * @param deviceId device identifier. 16 * @param deviceId device identifier.
16 * @param term mastership term. 17 * @param term mastership term.
17 */ 18 */
......
1 +package org.onlab.onos;
2 +
3 +import com.google.common.testing.EqualsTester;
4 +import org.junit.Test;
5 +
6 +import static org.junit.Assert.*;
7 +import static org.onlab.onos.Version.version;
8 +
9 +/**
10 + * Tests of the version descriptor.
11 + */
12 +public class VersionTest {
13 +
14 + @Test
15 + public void fromParts() {
16 + Version v = version(1, 2, 3, "4321");
17 + assertEquals("wrong major", 1, v.major());
18 + assertEquals("wrong minor", 2, v.minor());
19 + assertEquals("wrong patch", 3, v.patch());
20 + assertEquals("wrong build", "4321", v.build());
21 + }
22 +
23 + @Test
24 + public void fromString() {
25 + Version v = version("1.2.3.4321");
26 + assertEquals("wrong major", 1, v.major());
27 + assertEquals("wrong minor", 2, v.minor());
28 + assertEquals("wrong patch", 3, v.patch());
29 + assertEquals("wrong build", "4321", v.build());
30 + }
31 +
32 + @Test
33 + public void snapshot() {
34 + Version v = version("1.2.3-SNAPSHOT");
35 + assertEquals("wrong major", 1, v.major());
36 + assertEquals("wrong minor", 2, v.minor());
37 + assertEquals("wrong patch", 3, v.patch());
38 + assertEquals("wrong build", "SNAPSHOT", v.build());
39 + }
40 +
41 + @Test
42 + public void testEquals() {
43 + new EqualsTester()
44 + .addEqualityGroup(version("1.2.3.4321"), version(1, 2, 3, "4321"))
45 + .addEqualityGroup(version("1.9.3.4321"), version(1, 9, 3, "4321"))
46 + .addEqualityGroup(version("1.2.8.4321"), version(1, 2, 8, "4321"))
47 + .addEqualityGroup(version("1.2.3.x"), version(1, 2, 3, "x"))
48 + .testEquals();
49 + }
50 +}
...\ No newline at end of file ...\ No newline at end of file
...@@ -5,6 +5,7 @@ import org.junit.Test; ...@@ -5,6 +5,7 @@ import org.junit.Test;
5 import org.onlab.onos.net.provider.ProviderId; 5 import org.onlab.onos.net.provider.ProviderId;
6 6
7 import static org.junit.Assert.assertEquals; 7 import static org.junit.Assert.assertEquals;
8 +import static org.onlab.onos.net.DefaultEdgeLink.createEdgeLink;
8 import static org.onlab.onos.net.DefaultLinkTest.cp; 9 import static org.onlab.onos.net.DefaultLinkTest.cp;
9 import static org.onlab.onos.net.DeviceId.deviceId; 10 import static org.onlab.onos.net.DeviceId.deviceId;
10 import static org.onlab.onos.net.HostId.hostId; 11 import static org.onlab.onos.net.HostId.hostId;
...@@ -55,4 +56,24 @@ public class DefaultEdgeLinkTest { ...@@ -55,4 +56,24 @@ public class DefaultEdgeLinkTest {
55 assertEquals("incorrect time", 123L, link.hostLocation().time()); 56 assertEquals("incorrect time", 123L, link.hostLocation().time());
56 } 57 }
57 58
59 + @Test
60 + public void phantomIngress() {
61 + HostLocation hostLocation = new HostLocation(DID1, P1, 123L);
62 + EdgeLink link = createEdgeLink(hostLocation, true);
63 + assertEquals("incorrect dst", hostLocation, link.dst());
64 + assertEquals("incorrect type", Link.Type.EDGE, link.type());
65 + assertEquals("incorrect connect point", hostLocation, link.hostLocation());
66 + assertEquals("incorrect time", 123L, link.hostLocation().time());
67 + }
68 +
69 + @Test
70 + public void phantomEgress() {
71 + ConnectPoint hostLocation = new ConnectPoint(DID1, P1);
72 + EdgeLink link = createEdgeLink(hostLocation, false);
73 + assertEquals("incorrect src", hostLocation, link.src());
74 + assertEquals("incorrect type", Link.Type.EDGE, link.type());
75 + assertEquals("incorrect connect point", hostLocation, link.hostLocation());
76 + assertEquals("incorrect time", 0L, link.hostLocation().time());
77 + }
78 +
58 } 79 }
......
1 package org.onlab.onos.net.intent; 1 package org.onlab.onos.net.intent;
2 2
3 -import org.junit.After; 3 +import static org.junit.Assert.assertEquals;
4 -import org.junit.Before; 4 +import static org.junit.Assert.assertFalse;
5 -import org.junit.Test; 5 +import static org.junit.Assert.assertNull;
6 +import static org.junit.Assert.fail;
7 +import static org.onlab.onos.net.intent.IntentEvent.Type.FAILED;
8 +import static org.onlab.onos.net.intent.IntentEvent.Type.INSTALLED;
9 +import static org.onlab.onos.net.intent.IntentEvent.Type.SUBMITTED;
10 +import static org.onlab.onos.net.intent.IntentEvent.Type.WITHDRAWN;
6 11
7 import java.util.ArrayList; 12 import java.util.ArrayList;
8 import java.util.Arrays; 13 import java.util.Arrays;
9 import java.util.Collections; 14 import java.util.Collections;
10 import java.util.Iterator; 15 import java.util.Iterator;
11 import java.util.List; 16 import java.util.List;
17 +import java.util.concurrent.Future;
12 18
13 -import static org.junit.Assert.*; 19 +import org.junit.After;
14 -import static org.onlab.onos.net.intent.IntentEvent.Type.*; 20 +import org.junit.Before;
21 +import org.junit.Test;
22 +import org.onlab.onos.net.flow.CompletedBatchOperation;
15 23
16 /** 24 /**
17 * Suite of tests for the intent service contract. 25 * Suite of tests for the intent service contract.
...@@ -290,17 +298,19 @@ public class IntentServiceTest { ...@@ -290,17 +298,19 @@ public class IntentServiceTest {
290 } 298 }
291 299
292 @Override 300 @Override
293 - public void install(TestInstallableIntent intent) { 301 + public Future<CompletedBatchOperation> install(TestInstallableIntent intent) {
294 if (fail) { 302 if (fail) {
295 throw new IntentException("install failed by design"); 303 throw new IntentException("install failed by design");
296 } 304 }
305 + return null;
297 } 306 }
298 307
299 @Override 308 @Override
300 - public void uninstall(TestInstallableIntent intent) { 309 + public Future<CompletedBatchOperation> uninstall(TestInstallableIntent intent) {
301 if (fail) { 310 if (fail) {
302 throw new IntentException("remove failed by design"); 311 throw new IntentException("remove failed by design");
303 } 312 }
313 + return null;
304 } 314 }
305 } 315 }
306 316
......
1 +package org.onlab.onos.cluster.impl;
2 +
3 +import org.apache.felix.scr.annotations.Activate;
4 +import org.apache.felix.scr.annotations.Component;
5 +import org.apache.felix.scr.annotations.Service;
6 +import org.onlab.onos.CoreService;
7 +import org.onlab.onos.Version;
8 +import org.onlab.util.Tools;
9 +
10 +import java.io.File;
11 +import java.util.List;
12 +
13 +/**
14 + * Core service implementation.
15 + */
16 +@Component
17 +@Service
18 +public class CoreManager implements CoreService {
19 +
20 + private static final File VERSION_FILE = new File("../VERSION");
21 + private static Version version = Version.version("1.0.0-SNAPSHOT");
22 +
23 + // TODO: work in progress
24 +
25 + @Activate
26 + public void activate() {
27 + List<String> versionLines = Tools.slurp(VERSION_FILE);
28 + if (versionLines != null && !versionLines.isEmpty()) {
29 + version = Version.version(versionLines.get(0));
30 + }
31 + }
32 +
33 + @Override
34 + public Version version() {
35 + return version;
36 + }
37 +
38 +}
...@@ -82,7 +82,7 @@ implements MastershipService, MastershipAdminService { ...@@ -82,7 +82,7 @@ implements MastershipService, MastershipAdminService {
82 if (role.equals(MastershipRole.MASTER)) { 82 if (role.equals(MastershipRole.MASTER)) {
83 event = store.setMaster(nodeId, deviceId); 83 event = store.setMaster(nodeId, deviceId);
84 } else { 84 } else {
85 - event = store.unsetMaster(nodeId, deviceId); 85 + event = store.setStandby(nodeId, deviceId);
86 } 86 }
87 87
88 if (event != null) { 88 if (event != null) {
...@@ -98,13 +98,10 @@ implements MastershipService, MastershipAdminService { ...@@ -98,13 +98,10 @@ implements MastershipService, MastershipAdminService {
98 98
99 @Override 99 @Override
100 public void relinquishMastership(DeviceId deviceId) { 100 public void relinquishMastership(DeviceId deviceId) {
101 - MastershipRole role = getLocalRole(deviceId); 101 + MastershipEvent event = null;
102 - if (!role.equals(MastershipRole.MASTER)) { 102 + event = store.relinquishRole(
103 - return;
104 - }
105 -
106 - MastershipEvent event = store.unsetMaster(
107 clusterService.getLocalNode().id(), deviceId); 103 clusterService.getLocalNode().id(), deviceId);
104 +
108 if (event != null) { 105 if (event != null) {
109 post(event); 106 post(event);
110 } 107 }
......
...@@ -18,6 +18,7 @@ import org.onlab.onos.cluster.MastershipListener; ...@@ -18,6 +18,7 @@ import org.onlab.onos.cluster.MastershipListener;
18 import org.onlab.onos.cluster.MastershipService; 18 import org.onlab.onos.cluster.MastershipService;
19 import org.onlab.onos.cluster.MastershipTermService; 19 import org.onlab.onos.cluster.MastershipTermService;
20 import org.onlab.onos.cluster.MastershipTerm; 20 import org.onlab.onos.cluster.MastershipTerm;
21 +import org.onlab.onos.cluster.NodeId;
21 import org.onlab.onos.event.AbstractListenerRegistry; 22 import org.onlab.onos.event.AbstractListenerRegistry;
22 import org.onlab.onos.event.EventDeliveryService; 23 import org.onlab.onos.event.EventDeliveryService;
23 import org.onlab.onos.net.Device; 24 import org.onlab.onos.net.Device;
...@@ -142,8 +143,12 @@ public class DeviceManager ...@@ -142,8 +143,12 @@ public class DeviceManager
142 143
143 // Applies the specified role to the device; ignores NONE 144 // Applies the specified role to the device; ignores NONE
144 private void applyRole(DeviceId deviceId, MastershipRole newRole) { 145 private void applyRole(DeviceId deviceId, MastershipRole newRole) {
145 - if (newRole != MastershipRole.NONE) { 146 + if (newRole.equals(MastershipRole.NONE)) {
146 Device device = store.getDevice(deviceId); 147 Device device = store.getDevice(deviceId);
148 + // FIXME: Device might not be there yet. (eventual consistent)
149 + if (device == null) {
150 + return;
151 + }
147 DeviceProvider provider = getProvider(device.providerId()); 152 DeviceProvider provider = getProvider(device.providerId());
148 if (provider != null) { 153 if (provider != null) {
149 provider.roleChanged(device, newRole); 154 provider.roleChanged(device, newRole);
...@@ -193,16 +198,50 @@ public class DeviceManager ...@@ -193,16 +198,50 @@ public class DeviceManager
193 checkNotNull(deviceId, DEVICE_ID_NULL); 198 checkNotNull(deviceId, DEVICE_ID_NULL);
194 checkNotNull(deviceDescription, DEVICE_DESCRIPTION_NULL); 199 checkNotNull(deviceDescription, DEVICE_DESCRIPTION_NULL);
195 checkValidity(); 200 checkValidity();
201 +
202 + log.info("Device {} connected", deviceId);
203 + // check my Role
204 + MastershipRole role = mastershipService.requestRoleFor(deviceId);
205 +
206 + if (role != MastershipRole.MASTER) {
207 + // TODO: Do we need to explicitly tell the Provider that
208 + // this instance is no longer the MASTER? probably not
209 + return;
210 + }
211 +
212 + MastershipTerm term = mastershipService.requestTermService()
213 + .getMastershipTerm(deviceId);
214 + if (!term.master().equals(clusterService.getLocalNode().id())) {
215 + // lost mastership after requestRole told this instance was MASTER.
216 + return;
217 + }
218 + // tell clock provider if this instance is the master
219 + clockProviderService.setMastershipTerm(deviceId, term);
220 +
196 DeviceEvent event = store.createOrUpdateDevice(provider().id(), 221 DeviceEvent event = store.createOrUpdateDevice(provider().id(),
197 deviceId, deviceDescription); 222 deviceId, deviceDescription);
198 223
199 - // If there was a change of any kind, trigger role selection 224 + // If there was a change of any kind, tell the provider
200 - // process. 225 + // that this instance is the master.
226 + // Note: event can be null, if mastership was lost between
227 + // roleRequest and store update calls.
201 if (event != null) { 228 if (event != null) {
202 - log.info("Device {} connected", deviceId); 229 + // TODO: Check switch reconnected case. Is it assured that
203 - mastershipService.requestRoleFor(deviceId); 230 + // event will never be null?
204 - provider().roleChanged(event.subject(), 231 + // Could there be a situation MastershipService told this
205 - mastershipService.requestRoleFor(deviceId)); 232 + // instance is the new Master, but
233 + // event returned from the store is null?
234 +
235 + // TODO: Confirm: Mastership could be lost after requestRole
236 + // and createOrUpdateDevice call.
237 + // In that case STANDBY node can
238 + // claim itself to be master against the Device.
239 + // Will the Node, chosen by the MastershipService, retry
240 + // to get the MASTER role when that happen?
241 +
242 + // FIXME: 1st argument should be deviceId, to allow setting
243 + // certain roles even if the store returned null.
244 + provider().roleChanged(event.subject(), role);
206 post(event); 245 post(event);
207 } 246 }
208 } 247 }
...@@ -211,12 +250,23 @@ public class DeviceManager ...@@ -211,12 +250,23 @@ public class DeviceManager
211 public void deviceDisconnected(DeviceId deviceId) { 250 public void deviceDisconnected(DeviceId deviceId) {
212 checkNotNull(deviceId, DEVICE_ID_NULL); 251 checkNotNull(deviceId, DEVICE_ID_NULL);
213 checkValidity(); 252 checkValidity();
253 +
254 + // FIXME: only the MASTER should be marking off-line in normal cases,
255 + // but if I was the last STANDBY connection, etc. and no one else
256 + // was there to mark the device offline, this instance may need to
257 + // temporarily request for Master Role and mark offline.
258 + if (!mastershipService.getLocalRole(deviceId).equals(MastershipRole.MASTER)) {
259 + log.debug("Device {} disconnected, but I am not the master", deviceId);
260 + //let go of any role anyways
261 + mastershipService.relinquishMastership(deviceId);
262 + return;
263 + }
214 DeviceEvent event = store.markOffline(deviceId); 264 DeviceEvent event = store.markOffline(deviceId);
265 + //we're no longer capable of being master or a candidate.
266 + mastershipService.relinquishMastership(deviceId);
215 267
216 - //we're no longer capable of mastership.
217 if (event != null) { 268 if (event != null) {
218 log.info("Device {} disconnected", deviceId); 269 log.info("Device {} disconnected", deviceId);
219 - mastershipService.relinquishMastership(deviceId);
220 post(event); 270 post(event);
221 } 271 }
222 } 272 }
...@@ -256,6 +306,9 @@ public class DeviceManager ...@@ -256,6 +306,9 @@ public class DeviceManager
256 // FIXME: implement response to this notification 306 // FIXME: implement response to this notification
257 log.warn("Failed to assert role [{}] onto Device {}", role, 307 log.warn("Failed to assert role [{}] onto Device {}", role,
258 deviceId); 308 deviceId);
309 + if (role == MastershipRole.MASTER) {
310 + mastershipService.relinquishMastership(deviceId);
311 + }
259 } 312 }
260 } 313 }
261 314
...@@ -267,17 +320,29 @@ public class DeviceManager ...@@ -267,17 +320,29 @@ public class DeviceManager
267 } 320 }
268 321
269 // Intercepts mastership events 322 // Intercepts mastership events
270 - private class InternalMastershipListener 323 + private class InternalMastershipListener implements MastershipListener {
271 - implements MastershipListener { 324 +
272 @Override 325 @Override
273 public void event(MastershipEvent event) { 326 public void event(MastershipEvent event) {
274 - if (event.master().equals(clusterService.getLocalNode().id())) { 327 + final DeviceId did = event.subject();
275 - MastershipTerm term = mastershipService.requestTermService() 328 + if (isAvailable(did)) {
276 - .getMastershipTerm(event.subject()); 329 + final NodeId myNodeId = clusterService.getLocalNode().id();
277 - clockProviderService.setMastershipTerm(event.subject(), term); 330 +
278 - applyRole(event.subject(), MastershipRole.MASTER); 331 + if (myNodeId.equals(event.master())) {
332 + MastershipTerm term = termService.getMastershipTerm(did);
333 +
334 + if (term.master().equals(myNodeId)) {
335 + // only set the new term if I am the master
336 + clockProviderService.setMastershipTerm(did, term);
337 + }
338 + applyRole(did, MastershipRole.MASTER);
339 + } else {
340 + applyRole(did, MastershipRole.STANDBY);
341 + }
279 } else { 342 } else {
280 - applyRole(event.subject(), MastershipRole.STANDBY); 343 + //device dead to node, give up
344 + mastershipService.relinquishMastership(did);
345 + applyRole(did, MastershipRole.STANDBY);
281 } 346 }
282 } 347 }
283 } 348 }
......
...@@ -13,12 +13,14 @@ import static org.onlab.util.Tools.namedThreads; ...@@ -13,12 +13,14 @@ import static org.onlab.util.Tools.namedThreads;
13 import static org.slf4j.LoggerFactory.getLogger; 13 import static org.slf4j.LoggerFactory.getLogger;
14 14
15 import java.util.ArrayList; 15 import java.util.ArrayList;
16 +import java.util.Iterator;
16 import java.util.List; 17 import java.util.List;
17 import java.util.Map; 18 import java.util.Map;
18 import java.util.Objects; 19 import java.util.Objects;
19 import java.util.concurrent.ConcurrentHashMap; 20 import java.util.concurrent.ConcurrentHashMap;
20 import java.util.concurrent.ConcurrentMap; 21 import java.util.concurrent.ConcurrentMap;
21 import java.util.concurrent.ExecutorService; 22 import java.util.concurrent.ExecutorService;
23 +import java.util.concurrent.Future;
22 24
23 import org.apache.felix.scr.annotations.Activate; 25 import org.apache.felix.scr.annotations.Activate;
24 import org.apache.felix.scr.annotations.Component; 26 import org.apache.felix.scr.annotations.Component;
...@@ -28,6 +30,7 @@ import org.apache.felix.scr.annotations.ReferenceCardinality; ...@@ -28,6 +30,7 @@ import org.apache.felix.scr.annotations.ReferenceCardinality;
28 import org.apache.felix.scr.annotations.Service; 30 import org.apache.felix.scr.annotations.Service;
29 import org.onlab.onos.event.AbstractListenerRegistry; 31 import org.onlab.onos.event.AbstractListenerRegistry;
30 import org.onlab.onos.event.EventDeliveryService; 32 import org.onlab.onos.event.EventDeliveryService;
33 +import org.onlab.onos.net.flow.CompletedBatchOperation;
31 import org.onlab.onos.net.intent.InstallableIntent; 34 import org.onlab.onos.net.intent.InstallableIntent;
32 import org.onlab.onos.net.intent.Intent; 35 import org.onlab.onos.net.intent.Intent;
33 import org.onlab.onos.net.intent.IntentCompiler; 36 import org.onlab.onos.net.intent.IntentCompiler;
...@@ -44,7 +47,9 @@ import org.onlab.onos.net.intent.IntentStore; ...@@ -44,7 +47,9 @@ import org.onlab.onos.net.intent.IntentStore;
44 import org.onlab.onos.net.intent.IntentStoreDelegate; 47 import org.onlab.onos.net.intent.IntentStoreDelegate;
45 import org.slf4j.Logger; 48 import org.slf4j.Logger;
46 49
50 +import com.google.common.collect.ImmutableList;
47 import com.google.common.collect.ImmutableMap; 51 import com.google.common.collect.ImmutableMap;
52 +import com.google.common.collect.Lists;
48 53
49 /** 54 /**
50 * An implementation of Intent Manager. 55 * An implementation of Intent Manager.
...@@ -67,7 +72,8 @@ public class IntentManager ...@@ -67,7 +72,8 @@ public class IntentManager
67 private final AbstractListenerRegistry<IntentEvent, IntentListener> 72 private final AbstractListenerRegistry<IntentEvent, IntentListener>
68 listenerRegistry = new AbstractListenerRegistry<>(); 73 listenerRegistry = new AbstractListenerRegistry<>();
69 74
70 - private final ExecutorService executor = newSingleThreadExecutor(namedThreads("onos-intents")); 75 + private ExecutorService executor;
76 + private ExecutorService monitorExecutor;
71 77
72 private final IntentStoreDelegate delegate = new InternalStoreDelegate(); 78 private final IntentStoreDelegate delegate = new InternalStoreDelegate();
73 private final TopologyChangeDelegate topoDelegate = new InternalTopoChangeDelegate(); 79 private final TopologyChangeDelegate topoDelegate = new InternalTopoChangeDelegate();
...@@ -86,6 +92,8 @@ public class IntentManager ...@@ -86,6 +92,8 @@ public class IntentManager
86 store.setDelegate(delegate); 92 store.setDelegate(delegate);
87 trackerService.setDelegate(topoDelegate); 93 trackerService.setDelegate(topoDelegate);
88 eventDispatcher.addSink(IntentEvent.class, listenerRegistry); 94 eventDispatcher.addSink(IntentEvent.class, listenerRegistry);
95 + executor = newSingleThreadExecutor(namedThreads("onos-intents"));
96 + monitorExecutor = newSingleThreadExecutor(namedThreads("onos-intent-monitor"));
89 log.info("Started"); 97 log.info("Started");
90 } 98 }
91 99
...@@ -94,6 +102,8 @@ public class IntentManager ...@@ -94,6 +102,8 @@ public class IntentManager
94 store.unsetDelegate(delegate); 102 store.unsetDelegate(delegate);
95 trackerService.unsetDelegate(topoDelegate); 103 trackerService.unsetDelegate(topoDelegate);
96 eventDispatcher.removeSink(IntentEvent.class); 104 eventDispatcher.removeSink(IntentEvent.class);
105 + executor.shutdown();
106 + monitorExecutor.shutdown();
97 log.info("Stopped"); 107 log.info("Stopped");
98 } 108 }
99 109
...@@ -240,14 +250,23 @@ public class IntentManager ...@@ -240,14 +250,23 @@ public class IntentManager
240 } 250 }
241 } 251 }
242 252
243 - // FIXME: To make SDN-IP workable ASAP, only single level compilation is implemented 253 + /**
244 - // TODO: implement compilation traversing tree structure 254 + * Compiles an intent recursively.
255 + *
256 + * @param intent intent
257 + * @return result of compilation
258 + */
245 private List<InstallableIntent> compileIntent(Intent intent) { 259 private List<InstallableIntent> compileIntent(Intent intent) {
260 + if (intent instanceof InstallableIntent) {
261 + return ImmutableList.of((InstallableIntent) intent);
262 + }
263 +
246 List<InstallableIntent> installable = new ArrayList<>(); 264 List<InstallableIntent> installable = new ArrayList<>();
265 + // TODO do we need to registerSubclassCompiler?
247 for (Intent compiled : getCompiler(intent).compile(intent)) { 266 for (Intent compiled : getCompiler(intent).compile(intent)) {
248 - InstallableIntent installableIntent = (InstallableIntent) compiled; 267 + installable.addAll(compileIntent(compiled));
249 - installable.add(installableIntent);
250 } 268 }
269 +
251 return installable; 270 return installable;
252 } 271 }
253 272
...@@ -261,6 +280,7 @@ public class IntentManager ...@@ -261,6 +280,7 @@ public class IntentManager
261 // Indicate that the intent is entering the installing phase. 280 // Indicate that the intent is entering the installing phase.
262 store.setState(intent, INSTALLING); 281 store.setState(intent, INSTALLING);
263 282
283 + List<Future<CompletedBatchOperation>> installFutures = Lists.newArrayList();
264 try { 284 try {
265 List<InstallableIntent> installables = store.getInstallableIntents(intent.id()); 285 List<InstallableIntent> installables = store.getInstallableIntents(intent.id());
266 if (installables != null) { 286 if (installables != null) {
...@@ -268,17 +288,20 @@ public class IntentManager ...@@ -268,17 +288,20 @@ public class IntentManager
268 registerSubclassInstallerIfNeeded(installable); 288 registerSubclassInstallerIfNeeded(installable);
269 trackerService.addTrackedResources(intent.id(), 289 trackerService.addTrackedResources(intent.id(),
270 installable.requiredLinks()); 290 installable.requiredLinks());
271 - getInstaller(installable).install(installable); 291 + Future<CompletedBatchOperation> future = getInstaller(installable).install(installable);
292 + installFutures.add(future);
272 } 293 }
273 } 294 }
274 - eventDispatcher.post(store.setState(intent, INSTALLED)); 295 + // FIXME we have to wait for the installable intents
275 - 296 + //eventDispatcher.post(store.setState(intent, INSTALLED));
297 + monitorExecutor.execute(new IntentInstallMonitor(intent, installFutures, INSTALLED));
276 } catch (Exception e) { 298 } catch (Exception e) {
277 log.warn("Unable to install intent {} due to: {}", intent.id(), e); 299 log.warn("Unable to install intent {} due to: {}", intent.id(), e);
278 - uninstallIntent(intent); 300 + uninstallIntent(intent, RECOMPILING);
279 301
280 // If compilation failed, kick off the recompiling phase. 302 // If compilation failed, kick off the recompiling phase.
281 - executeRecompilingPhase(intent); 303 + // FIXME
304 + //executeRecompilingPhase(intent);
282 } 305 }
283 } 306 }
284 307
...@@ -327,12 +350,14 @@ public class IntentManager ...@@ -327,12 +350,14 @@ public class IntentManager
327 private void executeWithdrawingPhase(Intent intent) { 350 private void executeWithdrawingPhase(Intent intent) {
328 // Indicate that the intent is being withdrawn. 351 // Indicate that the intent is being withdrawn.
329 store.setState(intent, WITHDRAWING); 352 store.setState(intent, WITHDRAWING);
330 - uninstallIntent(intent); 353 + uninstallIntent(intent, WITHDRAWN);
331 354
332 // If all went well, disassociate the top-level intent with its 355 // If all went well, disassociate the top-level intent with its
333 // installable derivatives and mark it as withdrawn. 356 // installable derivatives and mark it as withdrawn.
334 - store.removeInstalledIntents(intent.id()); 357 + // FIXME need to clean up
335 - eventDispatcher.post(store.setState(intent, WITHDRAWN)); 358 + //store.removeInstalledIntents(intent.id());
359 + // FIXME
360 + //eventDispatcher.post(store.setState(intent, WITHDRAWN));
336 } 361 }
337 362
338 /** 363 /**
...@@ -340,14 +365,17 @@ public class IntentManager ...@@ -340,14 +365,17 @@ public class IntentManager
340 * 365 *
341 * @param intent intent to be uninstalled 366 * @param intent intent to be uninstalled
342 */ 367 */
343 - private void uninstallIntent(Intent intent) { 368 + private void uninstallIntent(Intent intent, IntentState nextState) {
369 + List<Future<CompletedBatchOperation>> uninstallFutures = Lists.newArrayList();
344 try { 370 try {
345 List<InstallableIntent> installables = store.getInstallableIntents(intent.id()); 371 List<InstallableIntent> installables = store.getInstallableIntents(intent.id());
346 if (installables != null) { 372 if (installables != null) {
347 for (InstallableIntent installable : installables) { 373 for (InstallableIntent installable : installables) {
348 - getInstaller(installable).uninstall(installable); 374 + Future<CompletedBatchOperation> future = getInstaller(installable).uninstall(installable);
375 + uninstallFutures.add(future);
349 } 376 }
350 } 377 }
378 + monitorExecutor.execute(new IntentInstallMonitor(intent, uninstallFutures, nextState));
351 } catch (IntentException e) { 379 } catch (IntentException e) {
352 log.warn("Unable to uninstall intent {} due to: {}", intent.id(), e); 380 log.warn("Unable to uninstall intent {} due to: {}", intent.id(), e);
353 } 381 }
...@@ -422,9 +450,10 @@ public class IntentManager ...@@ -422,9 +450,10 @@ public class IntentManager
422 // Attempt recompilation of the specified intents first. 450 // Attempt recompilation of the specified intents first.
423 for (IntentId intentId : intentIds) { 451 for (IntentId intentId : intentIds) {
424 Intent intent = getIntent(intentId); 452 Intent intent = getIntent(intentId);
425 - uninstallIntent(intent); 453 + uninstallIntent(intent, RECOMPILING);
426 454
427 - executeRecompilingPhase(intent); 455 + //FIXME
456 + //executeRecompilingPhase(intent);
428 } 457 }
429 458
430 if (compileAllFailed) { 459 if (compileAllFailed) {
...@@ -460,4 +489,44 @@ public class IntentManager ...@@ -460,4 +489,44 @@ public class IntentManager
460 } 489 }
461 } 490 }
462 491
492 + private class IntentInstallMonitor implements Runnable {
493 +
494 + private final Intent intent;
495 + private final List<Future<CompletedBatchOperation>> futures;
496 + private final IntentState nextState;
497 +
498 + public IntentInstallMonitor(Intent intent,
499 + List<Future<CompletedBatchOperation>> futures, IntentState nextState) {
500 + this.intent = intent;
501 + this.futures = futures;
502 + this.nextState = nextState;
503 + }
504 +
505 + private void updateIntent(Intent intent) {
506 + if (nextState == RECOMPILING) {
507 + executor.execute(new IntentTask(nextState, intent));
508 + } else if (nextState == INSTALLED || nextState == WITHDRAWN) {
509 + eventDispatcher.post(store.setState(intent, nextState));
510 + } else {
511 + log.warn("Invalid next intent state {} for intent {}", nextState, intent);
512 + }
513 + }
514 +
515 + @Override
516 + public void run() {
517 + for (Iterator<Future<CompletedBatchOperation>> i = futures.iterator(); i.hasNext();) {
518 + Future<CompletedBatchOperation> future = i.next();
519 + if (future.isDone()) {
520 + // TODO: we may want to get the future here
521 + i.remove();
522 + }
523 + }
524 + if (futures.isEmpty()) {
525 + updateIntent(intent);
526 + } else {
527 + // resubmit ourselves if we are not done yet
528 + monitorExecutor.submit(this);
529 + }
530 + }
531 + }
463 } 532 }
......
...@@ -5,7 +5,7 @@ import static org.slf4j.LoggerFactory.getLogger; ...@@ -5,7 +5,7 @@ import static org.slf4j.LoggerFactory.getLogger;
5 5
6 import java.util.Iterator; 6 import java.util.Iterator;
7 import java.util.List; 7 import java.util.List;
8 -import java.util.concurrent.ExecutionException; 8 +import java.util.concurrent.Future;
9 9
10 import org.apache.felix.scr.annotations.Activate; 10 import org.apache.felix.scr.annotations.Activate;
11 import org.apache.felix.scr.annotations.Component; 11 import org.apache.felix.scr.annotations.Component;
...@@ -15,6 +15,7 @@ import org.apache.felix.scr.annotations.ReferenceCardinality; ...@@ -15,6 +15,7 @@ import org.apache.felix.scr.annotations.ReferenceCardinality;
15 import org.onlab.onos.ApplicationId; 15 import org.onlab.onos.ApplicationId;
16 import org.onlab.onos.net.ConnectPoint; 16 import org.onlab.onos.net.ConnectPoint;
17 import org.onlab.onos.net.Link; 17 import org.onlab.onos.net.Link;
18 +import org.onlab.onos.net.flow.CompletedBatchOperation;
18 import org.onlab.onos.net.flow.DefaultFlowRule; 19 import org.onlab.onos.net.flow.DefaultFlowRule;
19 import org.onlab.onos.net.flow.DefaultTrafficSelector; 20 import org.onlab.onos.net.flow.DefaultTrafficSelector;
20 import org.onlab.onos.net.flow.FlowRule; 21 import org.onlab.onos.net.flow.FlowRule;
...@@ -57,8 +58,26 @@ public class PathIntentInstaller implements IntentInstaller<PathIntent> { ...@@ -57,8 +58,26 @@ public class PathIntentInstaller implements IntentInstaller<PathIntent> {
57 intentManager.unregisterInstaller(PathIntent.class); 58 intentManager.unregisterInstaller(PathIntent.class);
58 } 59 }
59 60
61 + /**
62 + * Apply a list of FlowRules.
63 + *
64 + * @param rules rules to apply
65 + */
66 + private Future<CompletedBatchOperation> applyBatch(List<FlowRuleBatchEntry> rules) {
67 + FlowRuleBatchOperation batch = new FlowRuleBatchOperation(rules);
68 + Future<CompletedBatchOperation> future = flowRuleService.applyBatch(batch);
69 + return future;
70 +// try {
71 +// //FIXME don't do this here
72 +// future.get();
73 +// } catch (InterruptedException | ExecutionException e) {
74 +// // TODO Auto-generated catch block
75 +// e.printStackTrace();
76 +// }
77 + }
78 +
60 @Override 79 @Override
61 - public void install(PathIntent intent) { 80 + public Future<CompletedBatchOperation> install(PathIntent intent) {
62 TrafficSelector.Builder builder = 81 TrafficSelector.Builder builder =
63 DefaultTrafficSelector.builder(intent.selector()); 82 DefaultTrafficSelector.builder(intent.selector());
64 Iterator<Link> links = intent.path().links().iterator(); 83 Iterator<Link> links = intent.path().links().iterator();
...@@ -74,20 +93,14 @@ public class PathIntentInstaller implements IntentInstaller<PathIntent> { ...@@ -74,20 +93,14 @@ public class PathIntentInstaller implements IntentInstaller<PathIntent> {
74 builder.build(), treatment, 93 builder.build(), treatment,
75 123, appId, 600); 94 123, appId, 600);
76 rules.add(new FlowRuleBatchEntry(FlowRuleOperation.ADD, rule)); 95 rules.add(new FlowRuleBatchEntry(FlowRuleOperation.ADD, rule));
77 - //flowRuleService.applyFlowRules(rule);
78 prev = link.dst(); 96 prev = link.dst();
79 } 97 }
80 - FlowRuleBatchOperation batch = new FlowRuleBatchOperation(rules); 98 +
81 - try { 99 + return applyBatch(rules);
82 - flowRuleService.applyBatch(batch).get();
83 - } catch (InterruptedException | ExecutionException e) {
84 - // TODO Auto-generated catch block
85 - e.printStackTrace();
86 - }
87 } 100 }
88 101
89 @Override 102 @Override
90 - public void uninstall(PathIntent intent) { 103 + public Future<CompletedBatchOperation> uninstall(PathIntent intent) {
91 TrafficSelector.Builder builder = 104 TrafficSelector.Builder builder =
92 DefaultTrafficSelector.builder(intent.selector()); 105 DefaultTrafficSelector.builder(intent.selector());
93 Iterator<Link> links = intent.path().links().iterator(); 106 Iterator<Link> links = intent.path().links().iterator();
...@@ -103,15 +116,131 @@ public class PathIntentInstaller implements IntentInstaller<PathIntent> { ...@@ -103,15 +116,131 @@ public class PathIntentInstaller implements IntentInstaller<PathIntent> {
103 builder.build(), treatment, 116 builder.build(), treatment,
104 123, appId, 600); 117 123, appId, 600);
105 rules.add(new FlowRuleBatchEntry(FlowRuleOperation.REMOVE, rule)); 118 rules.add(new FlowRuleBatchEntry(FlowRuleOperation.REMOVE, rule));
106 - //flowRuleService.removeFlowRules(rule);
107 prev = link.dst(); 119 prev = link.dst();
108 } 120 }
109 - FlowRuleBatchOperation batch = new FlowRuleBatchOperation(rules); 121 + return applyBatch(rules);
110 - try { 122 + }
111 - flowRuleService.applyBatch(batch).get(); 123 +
112 - } catch (InterruptedException | ExecutionException e) { 124 + // TODO refactor below this line... ----------------------------
113 - // TODO Auto-generated catch block 125 +
114 - e.printStackTrace(); 126 + /**
127 + * Generates the series of MatchActionOperations from the
128 + * {@link FlowBatchOperation}.
129 + * <p>
130 + * FIXME: Currently supporting PacketPathFlow and SingleDstTreeFlow only.
131 + * <p>
132 + * FIXME: MatchActionOperations should have dependency field to the other
133 + * match action operations, and this method should use this.
134 + *
135 + * @param op the {@link FlowBatchOperation} object
136 + * @return the list of {@link MatchActionOperations} objects
137 + */
138 + /*
139 + private List<MatchActionOperations>
140 + generateMatchActionOperationsList(FlowBatchOperation op) {
141 +
142 + // MatchAction operations at head (ingress) switches.
143 + MatchActionOperations headOps = matchActionService.createOperationsList();
144 +
145 + // MatchAction operations at rest of the switches.
146 + MatchActionOperations tailOps = matchActionService.createOperationsList();
147 +
148 + MatchActionOperations removeOps = matchActionService.createOperationsList();
149 +
150 + for (BatchOperationEntry<Operator, ?> e : op.getOperations()) {
151 +
152 + if (e.getOperator() == FlowBatchOperation.Operator.ADD) {
153 + generateInstallMatchActionOperations(e, tailOps, headOps);
154 + } else if (e.getOperator() == FlowBatchOperation.Operator.REMOVE) {
155 + generateRemoveMatchActionOperations(e, removeOps);
156 + } else {
157 + throw new UnsupportedOperationException(
158 + "FlowManager supports ADD and REMOVE operations only.");
159 + }
160 +
161 + }
162 +
163 + return Arrays.asList(tailOps, headOps, removeOps);
115 } 164 }
165 + */
166 +
167 + /**
168 + * Generates MatchActionOperations for an INSTALL FlowBatchOperation.
169 + * <p/>
170 + * FIXME: Currently only supports flows that generate exactly two match
171 + * action operation sets.
172 + *
173 + * @param e Flow BatchOperationEntry
174 + * @param tailOps MatchActionOperation set that the tail
175 + * MatchActionOperations will be placed in
176 + * @param headOps MatchActionOperation set that the head
177 + * MatchActionOperations will be placed in
178 + */
179 + /*
180 + private void generateInstallMatchActionOperations(
181 + BatchOperationEntry<Operator, ?> e,
182 + MatchActionOperations tailOps,
183 + MatchActionOperations headOps) {
184 +
185 + if (!(e.getTarget() instanceof Flow)) {
186 + throw new IllegalStateException(
187 + "The target is not Flow object: " + e.getTarget());
188 + }
189 +
190 + // Compile flows to match-actions
191 + Flow flow = (Flow) e.getTarget();
192 + List<MatchActionOperations> maOps = flow.compile(
193 + e.getOperator(), matchActionService);
194 + verifyNotNull(maOps, "Could not compile the flow: " + flow);
195 + verify(maOps.size() == 2,
196 + "The flow generates unspported match-action operations.");
197 +
198 + // Map FlowId to MatchActionIds
199 + for (MatchActionOperations maOp : maOps) {
200 + for (MatchActionOperationEntry entry : maOp.getOperations()) {
201 + flowMatchActionsMap.put(
202 + KryoFactory.serialize(flow.getId()),
203 + KryoFactory.serialize(entry.getTarget()));
204 + }
205 + }
206 +
207 + // Merge match-action operations
208 + for (MatchActionOperationEntry mae : maOps.get(0).getOperations()) {
209 + verify(mae.getOperator() == MatchActionOperations.Operator.INSTALL);
210 + tailOps.addOperation(mae);
211 + }
212 + for (MatchActionOperationEntry mae : maOps.get(1).getOperations()) {
213 + verify(mae.getOperator() == MatchActionOperations.Operator.INSTALL);
214 + headOps.addOperation(mae);
116 } 215 }
216 + }
217 + */
218 + /**
219 + * Generates MatchActionOperations for a REMOVE FlowBatchOperation.
220 + *
221 + * @param e Flow BatchOperationEntry
222 + * @param removeOps MatchActionOperation set that the remove
223 + * MatchActionOperations will be placed in
224 + */
225 + /*
226 + private void generateRemoveMatchActionOperations(
227 + BatchOperationEntry<Operator, ?> e,
228 + MatchActionOperations removeOps) {
229 +
230 + if (!(e.getTarget() instanceof FlowId)) {
231 + throw new IllegalStateException(
232 + "The target is not a FlowId object: " + e.getTarget());
233 + }
234 +
235 + // Compile flows to match-actions
236 + FlowId flowId = (FlowId) e.getTarget();
237 +
238 + for (byte[] matchActionIdBytes :
239 + flowMatchActionsMap.remove(KryoFactory.serialize(flowId))) {
240 + MatchActionId matchActionId = KryoFactory.deserialize(matchActionIdBytes);
241 + removeOps.addOperation(new MatchActionOperationEntry(
242 + MatchActionOperations.Operator.REMOVE, matchActionId));
243 + }
244 + }
245 + */
117 } 246 }
......
1 +package org.onlab.onos.net.intent.impl;
2 +
3 +import java.util.ArrayList;
4 +import java.util.Arrays;
5 +import java.util.List;
6 +import java.util.Set;
7 +
8 +import org.apache.felix.scr.annotations.Activate;
9 +import org.apache.felix.scr.annotations.Component;
10 +import org.apache.felix.scr.annotations.Deactivate;
11 +import org.apache.felix.scr.annotations.Reference;
12 +import org.apache.felix.scr.annotations.ReferenceCardinality;
13 +import org.onlab.onos.net.ConnectPoint;
14 +import org.onlab.onos.net.DefaultEdgeLink;
15 +import org.onlab.onos.net.DefaultPath;
16 +import org.onlab.onos.net.Link;
17 +import org.onlab.onos.net.Path;
18 +import org.onlab.onos.net.host.HostService;
19 +import org.onlab.onos.net.intent.IdGenerator;
20 +import org.onlab.onos.net.intent.Intent;
21 +import org.onlab.onos.net.intent.IntentCompiler;
22 +import org.onlab.onos.net.intent.IntentExtensionService;
23 +import org.onlab.onos.net.intent.IntentId;
24 +import org.onlab.onos.net.intent.PathIntent;
25 +import org.onlab.onos.net.intent.PointToPointIntent;
26 +import org.onlab.onos.net.provider.ProviderId;
27 +import org.onlab.onos.net.topology.PathService;
28 +
29 +/**
30 + * A intent compiler for {@link org.onlab.onos.net.intent.HostToHostIntent}.
31 + */
32 +@Component(immediate = true)
33 +public class PointToPointIntentCompiler
34 + implements IntentCompiler<PointToPointIntent> {
35 +
36 + private static final ProviderId PID = new ProviderId("core", "org.onlab.onos.core", true);
37 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
38 + protected IntentExtensionService intentManager;
39 +
40 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
41 + protected PathService pathService;
42 +
43 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
44 + protected HostService hostService;
45 +
46 + private IdGenerator<IntentId> intentIdGenerator;
47 +
48 + @Activate
49 + public void activate() {
50 + IdBlockAllocator idBlockAllocator = new DummyIdBlockAllocator();
51 + intentIdGenerator = new IdBlockAllocatorBasedIntentIdGenerator(idBlockAllocator);
52 + intentManager.registerCompiler(PointToPointIntent.class, this);
53 + }
54 +
55 + @Deactivate
56 + public void deactivate() {
57 + intentManager.unregisterCompiler(PointToPointIntent.class);
58 + }
59 +
60 + @Override
61 + public List<Intent> compile(PointToPointIntent intent) {
62 + Path path = getPath(intent.ingressPoint(), intent.egressPoint());
63 +
64 + List<Link> links = new ArrayList<>();
65 + links.add(DefaultEdgeLink.createEdgeLink(intent.ingressPoint(), true));
66 + links.addAll(path.links());
67 + links.add(DefaultEdgeLink.createEdgeLink(intent.egressPoint(), false));
68 +
69 + return Arrays.asList(createPathIntent(new DefaultPath(PID, links, path.cost() + 2,
70 + path.annotations()),
71 + intent));
72 + }
73 +
74 + /**
75 + * Creates a path intent from the specified path and original
76 + * connectivity intent.
77 + *
78 + * @param path path to create an intent for
79 + * @param intent original intent
80 + */
81 + private Intent createPathIntent(Path path,
82 + PointToPointIntent intent) {
83 +
84 + return new PathIntent(intentIdGenerator.getNewId(),
85 + intent.selector(), intent.treatment(),
86 + path.src(), path.dst(), path);
87 + }
88 +
89 + /**
90 + * Computes a path between two ConnectPoints.
91 + *
92 + * @param one start of the path
93 + * @param two end of the path
94 + * @return Path between the two
95 + * @throws PathNotFoundException if a path cannot be found
96 + */
97 + private Path getPath(ConnectPoint one, ConnectPoint two) {
98 + Set<Path> paths = pathService.getPaths(one.deviceId(), two.deviceId());
99 + if (paths.isEmpty()) {
100 + throw new PathNotFoundException("No path from " + one + " to " + two);
101 + }
102 + // TODO: let's be more intelligent about this eventually
103 + return paths.iterator().next();
104 + }
105 +}
1 package org.onlab.onos.net.device.impl; 1 package org.onlab.onos.net.device.impl;
2 2
3 import com.google.common.collect.Sets; 3 import com.google.common.collect.Sets;
4 +
4 import org.junit.After; 5 import org.junit.After;
5 import org.junit.Before; 6 import org.junit.Before;
6 import org.junit.Ignore; 7 import org.junit.Ignore;
7 import org.junit.Test; 8 import org.junit.Test;
9 +import org.onlab.onos.cluster.ClusterEventListener;
10 +import org.onlab.onos.cluster.ClusterService;
11 +import org.onlab.onos.cluster.ControllerNode;
12 +import org.onlab.onos.cluster.DefaultControllerNode;
8 import org.onlab.onos.cluster.MastershipServiceAdapter; 13 import org.onlab.onos.cluster.MastershipServiceAdapter;
14 +import org.onlab.onos.cluster.MastershipTerm;
15 +import org.onlab.onos.cluster.MastershipTermService;
9 import org.onlab.onos.cluster.NodeId; 16 import org.onlab.onos.cluster.NodeId;
17 +import org.onlab.onos.cluster.ControllerNode.State;
10 import org.onlab.onos.event.Event; 18 import org.onlab.onos.event.Event;
11 import org.onlab.onos.event.impl.TestEventDispatcher; 19 import org.onlab.onos.event.impl.TestEventDispatcher;
12 import org.onlab.onos.net.Device; 20 import org.onlab.onos.net.Device;
...@@ -27,7 +35,9 @@ import org.onlab.onos.net.device.DeviceService; ...@@ -27,7 +35,9 @@ import org.onlab.onos.net.device.DeviceService;
27 import org.onlab.onos.net.device.PortDescription; 35 import org.onlab.onos.net.device.PortDescription;
28 import org.onlab.onos.net.provider.AbstractProvider; 36 import org.onlab.onos.net.provider.AbstractProvider;
29 import org.onlab.onos.net.provider.ProviderId; 37 import org.onlab.onos.net.provider.ProviderId;
38 +import org.onlab.onos.store.ClockProviderService;
30 import org.onlab.onos.store.trivial.impl.SimpleDeviceStore; 39 import org.onlab.onos.store.trivial.impl.SimpleDeviceStore;
40 +import org.onlab.packet.IpPrefix;
31 41
32 import java.util.ArrayList; 42 import java.util.ArrayList;
33 import java.util.Iterator; 43 import java.util.Iterator;
...@@ -56,6 +66,8 @@ public class DeviceManagerTest { ...@@ -56,6 +66,8 @@ public class DeviceManagerTest {
56 private static final PortNumber P1 = PortNumber.portNumber(1); 66 private static final PortNumber P1 = PortNumber.portNumber(1);
57 private static final PortNumber P2 = PortNumber.portNumber(2); 67 private static final PortNumber P2 = PortNumber.portNumber(2);
58 private static final PortNumber P3 = PortNumber.portNumber(3); 68 private static final PortNumber P3 = PortNumber.portNumber(3);
69 + private static final NodeId NID_LOCAL = new NodeId("local");
70 + private static final IpPrefix LOCALHOST = IpPrefix.valueOf("127.0.0.1");
59 71
60 private DeviceManager mgr; 72 private DeviceManager mgr;
61 73
...@@ -75,6 +87,8 @@ public class DeviceManagerTest { ...@@ -75,6 +87,8 @@ public class DeviceManagerTest {
75 mgr.store = new SimpleDeviceStore(); 87 mgr.store = new SimpleDeviceStore();
76 mgr.eventDispatcher = new TestEventDispatcher(); 88 mgr.eventDispatcher = new TestEventDispatcher();
77 mgr.mastershipService = new TestMastershipService(); 89 mgr.mastershipService = new TestMastershipService();
90 + mgr.clusterService = new TestClusterService();
91 + mgr.clockProviderService = new TestClockProviderService();
78 mgr.activate(); 92 mgr.activate();
79 93
80 service.addListener(listener); 94 service.addListener(listener);
...@@ -258,7 +272,8 @@ public class DeviceManagerTest { ...@@ -258,7 +272,8 @@ public class DeviceManagerTest {
258 } 272 }
259 } 273 }
260 274
261 - private static class TestMastershipService extends MastershipServiceAdapter { 275 + private static class TestMastershipService
276 + extends MastershipServiceAdapter {
262 @Override 277 @Override
263 public MastershipRole getLocalRole(DeviceId deviceId) { 278 public MastershipRole getLocalRole(DeviceId deviceId) {
264 return MastershipRole.MASTER; 279 return MastershipRole.MASTER;
...@@ -273,6 +288,59 @@ public class DeviceManagerTest { ...@@ -273,6 +288,59 @@ public class DeviceManagerTest {
273 public MastershipRole requestRoleFor(DeviceId deviceId) { 288 public MastershipRole requestRoleFor(DeviceId deviceId) {
274 return MastershipRole.MASTER; 289 return MastershipRole.MASTER;
275 } 290 }
291 +
292 + @Override
293 + public MastershipTermService requestTermService() {
294 + return new MastershipTermService() {
295 + @Override
296 + public MastershipTerm getMastershipTerm(DeviceId deviceId) {
297 + // FIXME: just returning something not null
298 + return MastershipTerm.of(NID_LOCAL, 1);
299 + }
300 + };
301 + }
302 + }
303 +
304 + // code clone
305 + private final class TestClusterService implements ClusterService {
306 +
307 + ControllerNode local = new DefaultControllerNode(NID_LOCAL, LOCALHOST);
308 +
309 + @Override
310 + public ControllerNode getLocalNode() {
311 + return local;
312 + }
313 +
314 + @Override
315 + public Set<ControllerNode> getNodes() {
316 + return null;
276 } 317 }
277 318
319 + @Override
320 + public ControllerNode getNode(NodeId nodeId) {
321 + return null;
322 + }
323 +
324 + @Override
325 + public State getState(NodeId nodeId) {
326 + return null;
327 + }
328 +
329 + @Override
330 + public void addListener(ClusterEventListener listener) {
331 + }
332 +
333 + @Override
334 + public void removeListener(ClusterEventListener listener) {
335 + }
336 + }
337 +
338 + private final class TestClockProviderService implements
339 + ClockProviderService {
340 +
341 + @Override
342 + public void setMastershipTerm(DeviceId deviceId, MastershipTerm term) {
343 + // TODO Auto-generated method stub
344 + }
345 + }
278 } 346 }
......
1 -package org.onlab.onos.net.device.impl;
2 -
3 -import com.google.common.collect.Iterables;
4 -import com.google.common.collect.Sets;
5 -import com.hazelcast.config.Config;
6 -import com.hazelcast.core.Hazelcast;
7 -
8 -import org.junit.After;
9 -import org.junit.Before;
10 -import org.junit.Test;
11 -import org.onlab.onos.cluster.DefaultControllerNode;
12 -import org.onlab.onos.cluster.MastershipServiceAdapter;
13 -import org.onlab.onos.cluster.NodeId;
14 -import org.onlab.onos.event.Event;
15 -import org.onlab.onos.event.EventDeliveryService;
16 -import org.onlab.onos.event.impl.TestEventDispatcher;
17 -import org.onlab.onos.net.Device;
18 -import org.onlab.onos.net.DeviceId;
19 -import org.onlab.onos.net.MastershipRole;
20 -import org.onlab.onos.net.Port;
21 -import org.onlab.onos.net.PortNumber;
22 -import org.onlab.onos.net.device.DefaultDeviceDescription;
23 -import org.onlab.onos.net.device.DefaultPortDescription;
24 -import org.onlab.onos.net.device.DeviceAdminService;
25 -import org.onlab.onos.net.device.DeviceDescription;
26 -import org.onlab.onos.net.device.DeviceEvent;
27 -import org.onlab.onos.net.device.DeviceListener;
28 -import org.onlab.onos.net.device.DeviceProvider;
29 -import org.onlab.onos.net.device.DeviceProviderRegistry;
30 -import org.onlab.onos.net.device.DeviceProviderService;
31 -import org.onlab.onos.net.device.DeviceService;
32 -import org.onlab.onos.net.device.PortDescription;
33 -import org.onlab.onos.net.provider.AbstractProvider;
34 -import org.onlab.onos.net.provider.ProviderId;
35 -import org.onlab.onos.store.common.StoreManager;
36 -import org.onlab.onos.store.common.StoreService;
37 -import org.onlab.onos.store.common.TestStoreManager;
38 -import org.onlab.onos.store.device.impl.DistributedDeviceStore;
39 -import org.onlab.packet.IpPrefix;
40 -
41 -import java.util.ArrayList;
42 -import java.util.HashSet;
43 -import java.util.Iterator;
44 -import java.util.List;
45 -import java.util.Map.Entry;
46 -import java.util.Set;
47 -import java.util.concurrent.BlockingQueue;
48 -import java.util.concurrent.ConcurrentHashMap;
49 -import java.util.concurrent.ConcurrentMap;
50 -import java.util.concurrent.LinkedBlockingQueue;
51 -import java.util.concurrent.TimeUnit;
52 -
53 -import static org.junit.Assert.*;
54 -import static org.onlab.onos.net.Device.Type.SWITCH;
55 -import static org.onlab.onos.net.DeviceId.deviceId;
56 -import static org.onlab.onos.net.device.DeviceEvent.Type.*;
57 -
58 -// FIXME This test is slow starting up Hazelcast on each test cases.
59 -// FIXME DistributedDeviceStore should have it's own test cases.
60 -
61 -/**
62 - * Test codifying the device service & device provider service contracts.
63 - */
64 -public class DistributedDeviceManagerTest {
65 -
66 - private static final ProviderId PID = new ProviderId("of", "foo");
67 - private static final DeviceId DID1 = deviceId("of:foo");
68 - private static final DeviceId DID2 = deviceId("of:bar");
69 - private static final String MFR = "whitebox";
70 - private static final String HW = "1.1.x";
71 - private static final String SW1 = "3.8.1";
72 - private static final String SW2 = "3.9.5";
73 - private static final String SN = "43311-12345";
74 -
75 - private static final PortNumber P1 = PortNumber.portNumber(1);
76 - private static final PortNumber P2 = PortNumber.portNumber(2);
77 - private static final PortNumber P3 = PortNumber.portNumber(3);
78 -
79 - private static final DefaultControllerNode SELF
80 - = new DefaultControllerNode(new NodeId("foobar"),
81 - IpPrefix.valueOf("127.0.0.1"));
82 -
83 -
84 - private DeviceManager mgr;
85 -
86 - protected StoreManager storeManager;
87 - protected DeviceService service;
88 - protected DeviceAdminService admin;
89 - protected DeviceProviderRegistry registry;
90 - protected DeviceProviderService providerService;
91 - protected TestProvider provider;
92 - protected TestListener listener = new TestListener();
93 - private DistributedDeviceStore dstore;
94 - private TestMastershipManager masterManager;
95 - private EventDeliveryService eventService;
96 -
97 - @Before
98 - public void setUp() {
99 - mgr = new DeviceManager();
100 - service = mgr;
101 - admin = mgr;
102 - registry = mgr;
103 - // TODO should find a way to clean Hazelcast instance without shutdown.
104 - Config config = TestStoreManager.getTestConfig();
105 -
106 - masterManager = new TestMastershipManager();
107 -
108 - storeManager = new TestStoreManager(Hazelcast.newHazelcastInstance(config));
109 - storeManager.activate();
110 -
111 - dstore = new TestDistributedDeviceStore(storeManager);
112 - dstore.activate();
113 -
114 - mgr.store = dstore;
115 - eventService = new TestEventDispatcher();
116 - mgr.eventDispatcher = eventService;
117 - mgr.mastershipService = masterManager;
118 - mgr.activate();
119 -
120 - service.addListener(listener);
121 -
122 - provider = new TestProvider();
123 - providerService = registry.register(provider);
124 - assertTrue("provider should be registered",
125 - registry.getProviders().contains(provider.id()));
126 - }
127 -
128 - @After
129 - public void tearDown() {
130 - registry.unregister(provider);
131 - assertFalse("provider should not be registered",
132 - registry.getProviders().contains(provider.id()));
133 - service.removeListener(listener);
134 - mgr.deactivate();
135 -
136 - dstore.deactivate();
137 - storeManager.deactivate();
138 - }
139 -
140 - private void connectDevice(DeviceId deviceId, String swVersion) {
141 - DeviceDescription description =
142 - new DefaultDeviceDescription(deviceId.uri(), SWITCH, MFR,
143 - HW, swVersion, SN);
144 - providerService.deviceConnected(deviceId, description);
145 - assertNotNull("device should be found", service.getDevice(DID1));
146 - }
147 -
148 - @Test
149 - public void deviceConnected() {
150 - assertNull("device should not be found", service.getDevice(DID1));
151 - connectDevice(DID1, SW1);
152 - validateEvents(DEVICE_ADDED);
153 -
154 - assertEquals("only one device expected", 1, Iterables.size(service.getDevices()));
155 - Iterator<Device> it = service.getDevices().iterator();
156 - assertNotNull("one device expected", it.next());
157 - assertFalse("only one device expected", it.hasNext());
158 -
159 - assertEquals("incorrect device count", 1, service.getDeviceCount());
160 - assertTrue("device should be available", service.isAvailable(DID1));
161 - }
162 -
163 - @Test
164 - public void deviceDisconnected() {
165 - connectDevice(DID1, SW1);
166 - connectDevice(DID2, SW1);
167 - validateEvents(DEVICE_ADDED, DEVICE_ADDED);
168 - assertTrue("device should be available", service.isAvailable(DID1));
169 -
170 - // Disconnect
171 - providerService.deviceDisconnected(DID1);
172 - assertNotNull("device should not be found", service.getDevice(DID1));
173 - assertFalse("device should not be available", service.isAvailable(DID1));
174 - validateEvents(DEVICE_AVAILABILITY_CHANGED);
175 -
176 - // Reconnect
177 - connectDevice(DID1, SW1);
178 - validateEvents(DEVICE_AVAILABILITY_CHANGED);
179 -
180 - assertEquals("incorrect device count", 2, service.getDeviceCount());
181 - }
182 -
183 - @Test
184 - public void deviceUpdated() {
185 - connectDevice(DID1, SW1);
186 - validateEvents(DEVICE_ADDED);
187 -
188 - connectDevice(DID1, SW2);
189 - validateEvents(DEVICE_UPDATED);
190 - }
191 -
192 - @Test
193 - public void getRole() {
194 - connectDevice(DID1, SW1);
195 - assertEquals("incorrect role", MastershipRole.MASTER, service.getRole(DID1));
196 - }
197 -
198 - @Test
199 - public void updatePorts() {
200 - connectDevice(DID1, SW1);
201 - List<PortDescription> pds = new ArrayList<>();
202 - pds.add(new DefaultPortDescription(P1, true));
203 - pds.add(new DefaultPortDescription(P2, true));
204 - pds.add(new DefaultPortDescription(P3, true));
205 - providerService.updatePorts(DID1, pds);
206 - validateEvents(DEVICE_ADDED, PORT_ADDED, PORT_ADDED, PORT_ADDED);
207 - pds.clear();
208 -
209 - pds.add(new DefaultPortDescription(P1, false));
210 - pds.add(new DefaultPortDescription(P3, true));
211 - providerService.updatePorts(DID1, pds);
212 - validateEvents(PORT_UPDATED, PORT_REMOVED);
213 - }
214 -
215 - @Test
216 - public void updatePortStatus() {
217 - connectDevice(DID1, SW1);
218 - List<PortDescription> pds = new ArrayList<>();
219 - pds.add(new DefaultPortDescription(P1, true));
220 - pds.add(new DefaultPortDescription(P2, true));
221 - providerService.updatePorts(DID1, pds);
222 - validateEvents(DEVICE_ADDED, PORT_ADDED, PORT_ADDED);
223 -
224 - providerService.portStatusChanged(DID1, new DefaultPortDescription(P1, false));
225 - validateEvents(PORT_UPDATED);
226 - providerService.portStatusChanged(DID1, new DefaultPortDescription(P1, false));
227 - assertTrue("no events expected", listener.events.isEmpty());
228 - }
229 -
230 - @Test
231 - public void getPorts() {
232 - connectDevice(DID1, SW1);
233 - List<PortDescription> pds = new ArrayList<>();
234 - pds.add(new DefaultPortDescription(P1, true));
235 - pds.add(new DefaultPortDescription(P2, true));
236 - providerService.updatePorts(DID1, pds);
237 - validateEvents(DEVICE_ADDED, PORT_ADDED, PORT_ADDED);
238 - assertEquals("wrong port count", 2, service.getPorts(DID1).size());
239 -
240 - Port port = service.getPort(DID1, P1);
241 - assertEquals("incorrect port", P1, service.getPort(DID1, P1).number());
242 - assertEquals("incorrect state", true, service.getPort(DID1, P1).isEnabled());
243 - }
244 -
245 - @Test
246 - public void removeDevice() {
247 - connectDevice(DID1, SW1);
248 - connectDevice(DID2, SW2);
249 - assertEquals("incorrect device count", 2, service.getDeviceCount());
250 - admin.removeDevice(DID1);
251 - validateEvents(DEVICE_ADDED, DEVICE_ADDED, DEVICE_REMOVED);
252 - assertNull("device should not be found", service.getDevice(DID1));
253 - assertNotNull("device should be found", service.getDevice(DID2));
254 - assertEquals("incorrect device count", 1, service.getDeviceCount());
255 - }
256 -
257 - protected void validateEvents(Enum... types) {
258 - for (Enum type : types) {
259 - try {
260 - Event event = listener.events.poll(1, TimeUnit.SECONDS);
261 - assertNotNull("Timed out waiting for " + event, event);
262 - assertEquals("incorrect event type", type, event.type());
263 - } catch (InterruptedException e) {
264 - fail("Unexpected interrupt");
265 - }
266 - }
267 - assertTrue("Unexpected events left", listener.events.isEmpty());
268 - listener.events.clear();
269 - }
270 -
271 -
272 - private class TestProvider extends AbstractProvider implements DeviceProvider {
273 - private Device deviceReceived;
274 - private MastershipRole roleReceived;
275 -
276 - public TestProvider() {
277 - super(PID);
278 - }
279 -
280 - @Override
281 - public void triggerProbe(Device device) {
282 - }
283 -
284 - @Override
285 - public void roleChanged(Device device, MastershipRole newRole) {
286 - deviceReceived = device;
287 - roleReceived = newRole;
288 - }
289 - }
290 -
291 - private static class TestListener implements DeviceListener {
292 - final BlockingQueue<DeviceEvent> events = new LinkedBlockingQueue<>();
293 -
294 - @Override
295 - public void event(DeviceEvent event) {
296 - events.add(event);
297 - }
298 - }
299 -
300 - private class TestDistributedDeviceStore extends DistributedDeviceStore {
301 -
302 - public TestDistributedDeviceStore(StoreService storeService) {
303 - this.storeService = storeService;
304 - }
305 - }
306 -
307 - private static class TestMastershipManager extends MastershipServiceAdapter {
308 -
309 - private ConcurrentMap<DeviceId, NodeId> masters = new ConcurrentHashMap<>();
310 -
311 - public TestMastershipManager() {
312 - // SELF master of all initially
313 - masters.put(DID1, SELF.id());
314 - masters.put(DID1, SELF.id());
315 - }
316 - @Override
317 - public MastershipRole getLocalRole(DeviceId deviceId) {
318 - return MastershipRole.MASTER;
319 - }
320 -
321 - @Override
322 - public Set<DeviceId> getDevicesOf(NodeId nodeId) {
323 - HashSet<DeviceId> set = Sets.newHashSet();
324 - for (Entry<DeviceId, NodeId> e : masters.entrySet()) {
325 - if (e.getValue().equals(nodeId)) {
326 - set.add(e.getKey());
327 - }
328 - }
329 - return set;
330 - }
331 -
332 - @Override
333 - public MastershipRole requestRoleFor(DeviceId deviceId) {
334 - if (SELF.id().equals(masters.get(deviceId))) {
335 - return MastershipRole.MASTER;
336 - } else {
337 - return MastershipRole.STANDBY;
338 - }
339 - }
340 -
341 - @Override
342 - public void relinquishMastership(DeviceId deviceId) {
343 - masters.remove(deviceId, SELF.id());
344 - }
345 - }
346 -}
...@@ -2,8 +2,7 @@ package org.onlab.onos.store.cluster.messaging; ...@@ -2,8 +2,7 @@ package org.onlab.onos.store.cluster.messaging;
2 2
3 import org.onlab.onos.cluster.NodeId; 3 import org.onlab.onos.cluster.NodeId;
4 4
5 -// TODO: ClusterMessage should be aware about how to serialize the payload 5 +// TODO: Should payload type be ByteBuffer?
6 -// TODO: Should payload type be made generic?
7 /** 6 /**
8 * Base message for cluster-wide communications. 7 * Base message for cluster-wide communications.
9 */ 8 */
...@@ -12,7 +11,6 @@ public class ClusterMessage { ...@@ -12,7 +11,6 @@ public class ClusterMessage {
12 private final NodeId sender; 11 private final NodeId sender;
13 private final MessageSubject subject; 12 private final MessageSubject subject;
14 private final byte[] payload; 13 private final byte[] payload;
15 - // TODO: add field specifying Serializer for payload
16 14
17 /** 15 /**
18 * Creates a cluster message. 16 * Creates a cluster message.
......
...@@ -45,4 +45,9 @@ public class MessageSubject { ...@@ -45,4 +45,9 @@ public class MessageSubject {
45 MessageSubject that = (MessageSubject) obj; 45 MessageSubject that = (MessageSubject) obj;
46 return Objects.equals(this.value, that.value); 46 return Objects.equals(this.value, that.value);
47 } 47 }
48 +
49 + // for serializer
50 + protected MessageSubject() {
51 + this.value = "";
52 + }
48 } 53 }
......
...@@ -3,12 +3,9 @@ package org.onlab.onos.store.cluster.messaging.impl; ...@@ -3,12 +3,9 @@ package org.onlab.onos.store.cluster.messaging.impl;
3 import static com.google.common.base.Preconditions.checkArgument; 3 import static com.google.common.base.Preconditions.checkArgument;
4 4
5 import java.io.IOException; 5 import java.io.IOException;
6 -import java.util.HashMap;
7 -import java.util.Map;
8 import java.util.Set; 6 import java.util.Set;
9 import java.util.Timer; 7 import java.util.Timer;
10 import java.util.TimerTask; 8 import java.util.TimerTask;
11 -
12 import org.apache.felix.scr.annotations.Activate; 9 import org.apache.felix.scr.annotations.Activate;
13 import org.apache.felix.scr.annotations.Component; 10 import org.apache.felix.scr.annotations.Component;
14 import org.apache.felix.scr.annotations.Deactivate; 11 import org.apache.felix.scr.annotations.Deactivate;
...@@ -26,8 +23,10 @@ import org.onlab.onos.store.cluster.messaging.ClusterCommunicationService; ...@@ -26,8 +23,10 @@ import org.onlab.onos.store.cluster.messaging.ClusterCommunicationService;
26 import org.onlab.onos.store.cluster.messaging.ClusterMessage; 23 import org.onlab.onos.store.cluster.messaging.ClusterMessage;
27 import org.onlab.onos.store.cluster.messaging.ClusterMessageHandler; 24 import org.onlab.onos.store.cluster.messaging.ClusterMessageHandler;
28 import org.onlab.onos.store.cluster.messaging.MessageSubject; 25 import org.onlab.onos.store.cluster.messaging.MessageSubject;
26 +import org.onlab.onos.store.serializers.ClusterMessageSerializer;
29 import org.onlab.onos.store.serializers.KryoPoolUtil; 27 import org.onlab.onos.store.serializers.KryoPoolUtil;
30 import org.onlab.onos.store.serializers.KryoSerializer; 28 import org.onlab.onos.store.serializers.KryoSerializer;
29 +import org.onlab.onos.store.serializers.MessageSubjectSerializer;
31 import org.onlab.util.KryoPool; 30 import org.onlab.util.KryoPool;
32 import org.onlab.netty.Endpoint; 31 import org.onlab.netty.Endpoint;
33 import org.onlab.netty.Message; 32 import org.onlab.netty.Message;
...@@ -50,8 +49,6 @@ public class ClusterCommunicationManager ...@@ -50,8 +49,6 @@ public class ClusterCommunicationManager
50 private ClusterService clusterService; 49 private ClusterService clusterService;
51 50
52 private ClusterNodesDelegate nodesDelegate; 51 private ClusterNodesDelegate nodesDelegate;
53 - // FIXME: `members` should go away and should be using ClusterService
54 - private Map<NodeId, ControllerNode> members = new HashMap<>();
55 private final Timer timer = new Timer("onos-controller-heatbeats"); 52 private final Timer timer = new Timer("onos-controller-heatbeats");
56 public static final long HEART_BEAT_INTERVAL_MILLIS = 1000L; 53 public static final long HEART_BEAT_INTERVAL_MILLIS = 1000L;
57 54
...@@ -59,11 +56,14 @@ public class ClusterCommunicationManager ...@@ -59,11 +56,14 @@ public class ClusterCommunicationManager
59 private MessagingService messagingService; 56 private MessagingService messagingService;
60 57
61 private static final KryoSerializer SERIALIZER = new KryoSerializer() { 58 private static final KryoSerializer SERIALIZER = new KryoSerializer() {
59 + @Override
62 protected void setupKryoPool() { 60 protected void setupKryoPool() {
63 serializerPool = KryoPool.newBuilder() 61 serializerPool = KryoPool.newBuilder()
64 .register(KryoPoolUtil.API) 62 .register(KryoPoolUtil.API)
65 - .register(ClusterMessage.class) 63 + .register(ClusterMessage.class, new ClusterMessageSerializer())
66 .register(ClusterMembershipEvent.class) 64 .register(ClusterMembershipEvent.class)
65 + .register(byte[].class)
66 + .register(MessageSubject.class, new MessageSubjectSerializer())
67 .build() 67 .build()
68 .populate(1); 68 .populate(1);
69 } 69 }
...@@ -73,7 +73,15 @@ public class ClusterCommunicationManager ...@@ -73,7 +73,15 @@ public class ClusterCommunicationManager
73 @Activate 73 @Activate
74 public void activate() { 74 public void activate() {
75 localNode = clusterService.getLocalNode(); 75 localNode = clusterService.getLocalNode();
76 - messagingService = new NettyMessagingService(localNode.tcpPort()); 76 + NettyMessagingService netty = new NettyMessagingService(localNode.tcpPort());
77 + // FIXME: workaround until it becomes a service.
78 + try {
79 + netty.activate();
80 + } catch (Exception e) {
81 + // TODO Auto-generated catch block
82 + log.error("NettyMessagingService#activate", e);
83 + }
84 + messagingService = netty;
77 log.info("Started"); 85 log.info("Started");
78 } 86 }
79 87
...@@ -86,7 +94,7 @@ public class ClusterCommunicationManager ...@@ -86,7 +94,7 @@ public class ClusterCommunicationManager
86 @Override 94 @Override
87 public boolean broadcast(ClusterMessage message) { 95 public boolean broadcast(ClusterMessage message) {
88 boolean ok = true; 96 boolean ok = true;
89 - for (ControllerNode node : members.values()) { 97 + for (ControllerNode node : clusterService.getNodes()) {
90 if (!node.equals(localNode)) { 98 if (!node.equals(localNode)) {
91 ok = unicast(message, node.id()) && ok; 99 ok = unicast(message, node.id()) && ok;
92 } 100 }
...@@ -107,11 +115,12 @@ public class ClusterCommunicationManager ...@@ -107,11 +115,12 @@ public class ClusterCommunicationManager
107 115
108 @Override 116 @Override
109 public boolean unicast(ClusterMessage message, NodeId toNodeId) { 117 public boolean unicast(ClusterMessage message, NodeId toNodeId) {
110 - ControllerNode node = members.get(toNodeId); 118 + ControllerNode node = clusterService.getNode(toNodeId);
111 checkArgument(node != null, "Unknown nodeId: %s", toNodeId); 119 checkArgument(node != null, "Unknown nodeId: %s", toNodeId);
112 Endpoint nodeEp = new Endpoint(node.ip().toString(), node.tcpPort()); 120 Endpoint nodeEp = new Endpoint(node.ip().toString(), node.tcpPort());
113 try { 121 try {
114 - messagingService.sendAsync(nodeEp, message.subject().value(), SERIALIZER.encode(message)); 122 + messagingService.sendAsync(nodeEp,
123 + message.subject().value(), SERIALIZER.encode(message));
115 return true; 124 return true;
116 } catch (IOException e) { 125 } catch (IOException e) {
117 log.error("Failed to send cluster message to nodeId: " + toNodeId, e); 126 log.error("Failed to send cluster message to nodeId: " + toNodeId, e);
...@@ -137,7 +146,7 @@ public class ClusterCommunicationManager ...@@ -137,7 +146,7 @@ public class ClusterCommunicationManager
137 146
138 @Override 147 @Override
139 public void addNode(ControllerNode node) { 148 public void addNode(ControllerNode node) {
140 - members.put(node.id(), node); 149 + //members.put(node.id(), node);
141 } 150 }
142 151
143 @Override 152 @Override
...@@ -146,7 +155,7 @@ public class ClusterCommunicationManager ...@@ -146,7 +155,7 @@ public class ClusterCommunicationManager
146 localNode.id(), 155 localNode.id(),
147 new MessageSubject("CLUSTER_MEMBERSHIP_EVENT"), 156 new MessageSubject("CLUSTER_MEMBERSHIP_EVENT"),
148 SERIALIZER.encode(new ClusterMembershipEvent(ClusterMembershipEventType.LEAVING_MEMBER, node)))); 157 SERIALIZER.encode(new ClusterMembershipEvent(ClusterMembershipEventType.LEAVING_MEMBER, node))));
149 - members.remove(node.id()); 158 + //members.remove(node.id());
150 } 159 }
151 160
152 // Sends a heart beat to all peers. 161 // Sends a heart beat to all peers.
...@@ -181,7 +190,7 @@ public class ClusterCommunicationManager ...@@ -181,7 +190,7 @@ public class ClusterCommunicationManager
181 } 190 }
182 } 191 }
183 192
184 - private static class InternalClusterMessageHandler implements MessageHandler { 193 + private final class InternalClusterMessageHandler implements MessageHandler {
185 194
186 private final ClusterMessageHandler handler; 195 private final ClusterMessageHandler handler;
187 196
...@@ -191,8 +200,13 @@ public class ClusterCommunicationManager ...@@ -191,8 +200,13 @@ public class ClusterCommunicationManager
191 200
192 @Override 201 @Override
193 public void handle(Message message) { 202 public void handle(Message message) {
203 + try {
194 ClusterMessage clusterMessage = SERIALIZER.decode(message.payload()); 204 ClusterMessage clusterMessage = SERIALIZER.decode(message.payload());
195 handler.handle(clusterMessage); 205 handler.handle(clusterMessage);
206 + } catch (Exception e) {
207 + log.error("Exception caught during ClusterMessageHandler", e);
208 + throw e;
209 + }
196 } 210 }
197 } 211 }
198 } 212 }
......
...@@ -82,7 +82,7 @@ public final class Timestamped<T> { ...@@ -82,7 +82,7 @@ public final class Timestamped<T> {
82 82
83 // Default constructor for serialization 83 // Default constructor for serialization
84 @Deprecated 84 @Deprecated
85 - protected Timestamped() { 85 + private Timestamped() {
86 this.value = null; 86 this.value = null;
87 this.timestamp = null; 87 this.timestamp = null;
88 } 88 }
......
...@@ -42,6 +42,7 @@ import org.onlab.onos.store.common.impl.MastershipBasedTimestamp; ...@@ -42,6 +42,7 @@ import org.onlab.onos.store.common.impl.MastershipBasedTimestamp;
42 import org.onlab.onos.store.common.impl.Timestamped; 42 import org.onlab.onos.store.common.impl.Timestamped;
43 import org.onlab.onos.store.serializers.KryoPoolUtil; 43 import org.onlab.onos.store.serializers.KryoPoolUtil;
44 import org.onlab.onos.store.serializers.KryoSerializer; 44 import org.onlab.onos.store.serializers.KryoSerializer;
45 +import org.onlab.onos.store.serializers.MastershipBasedTimestampSerializer;
45 import org.onlab.util.KryoPool; 46 import org.onlab.util.KryoPool;
46 import org.onlab.util.NewConcurrentHashMap; 47 import org.onlab.util.NewConcurrentHashMap;
47 import org.slf4j.Logger; 48 import org.slf4j.Logger;
...@@ -113,14 +114,18 @@ public class GossipDeviceStore ...@@ -113,14 +114,18 @@ public class GossipDeviceStore
113 protected ClusterService clusterService; 114 protected ClusterService clusterService;
114 115
115 private static final KryoSerializer SERIALIZER = new KryoSerializer() { 116 private static final KryoSerializer SERIALIZER = new KryoSerializer() {
117 + @Override
116 protected void setupKryoPool() { 118 protected void setupKryoPool() {
117 serializerPool = KryoPool.newBuilder() 119 serializerPool = KryoPool.newBuilder()
118 .register(KryoPoolUtil.API) 120 .register(KryoPoolUtil.API)
119 - .register(InternalDeviceEvent.class) 121 + .register(InternalDeviceEvent.class, new InternalDeviceEventSerializer())
120 - .register(InternalPortEvent.class) 122 + .register(InternalDeviceOfflineEvent.class, new InternalDeviceOfflineEventSerializer())
121 - .register(InternalPortStatusEvent.class) 123 + .register(InternalDeviceRemovedEvent.class)
124 + .register(InternalPortEvent.class, new InternalPortEventSerializer())
125 + .register(InternalPortStatusEvent.class, new InternalPortStatusEventSerializer())
126 + .register(Timestamp.class)
122 .register(Timestamped.class) 127 .register(Timestamped.class)
123 - .register(MastershipBasedTimestamp.class) 128 + .register(MastershipBasedTimestamp.class, new MastershipBasedTimestampSerializer())
124 .build() 129 .build()
125 .populate(1); 130 .populate(1);
126 } 131 }
...@@ -132,6 +137,10 @@ public class GossipDeviceStore ...@@ -132,6 +137,10 @@ public class GossipDeviceStore
132 clusterCommunicator.addSubscriber( 137 clusterCommunicator.addSubscriber(
133 GossipDeviceStoreMessageSubjects.DEVICE_UPDATE, new InternalDeviceEventListener()); 138 GossipDeviceStoreMessageSubjects.DEVICE_UPDATE, new InternalDeviceEventListener());
134 clusterCommunicator.addSubscriber( 139 clusterCommunicator.addSubscriber(
140 + GossipDeviceStoreMessageSubjects.DEVICE_OFFLINE, new InternalDeviceOfflineEventListener());
141 + clusterCommunicator.addSubscriber(
142 + GossipDeviceStoreMessageSubjects.DEVICE_REMOVED, new InternalDeviceRemovedEventListener());
143 + clusterCommunicator.addSubscriber(
135 GossipDeviceStoreMessageSubjects.PORT_UPDATE, new InternalPortEventListener()); 144 GossipDeviceStoreMessageSubjects.PORT_UPDATE, new InternalPortEventListener());
136 clusterCommunicator.addSubscriber( 145 clusterCommunicator.addSubscriber(
137 GossipDeviceStoreMessageSubjects.PORT_STATUS_UPDATE, new InternalPortStatusEventListener()); 146 GossipDeviceStoreMessageSubjects.PORT_STATUS_UPDATE, new InternalPortStatusEventListener());
...@@ -175,7 +184,7 @@ public class GossipDeviceStore ...@@ -175,7 +184,7 @@ public class GossipDeviceStore
175 try { 184 try {
176 notifyPeers(new InternalDeviceEvent(providerId, deviceId, deltaDesc)); 185 notifyPeers(new InternalDeviceEvent(providerId, deviceId, deltaDesc));
177 } catch (IOException e) { 186 } catch (IOException e) {
178 - log.error("Failed to notify peers of a device update topology event or providerId: " 187 + log.error("Failed to notify peers of a device update topology event for providerId: "
179 + providerId + " and deviceId: " + deviceId, e); 188 + providerId + " and deviceId: " + deviceId, e);
180 } 189 }
181 } 190 }
...@@ -278,7 +287,18 @@ public class GossipDeviceStore ...@@ -278,7 +287,18 @@ public class GossipDeviceStore
278 @Override 287 @Override
279 public DeviceEvent markOffline(DeviceId deviceId) { 288 public DeviceEvent markOffline(DeviceId deviceId) {
280 Timestamp timestamp = clockService.getTimestamp(deviceId); 289 Timestamp timestamp = clockService.getTimestamp(deviceId);
281 - return markOfflineInternal(deviceId, timestamp); 290 + DeviceEvent event = markOfflineInternal(deviceId, timestamp);
291 + if (event != null) {
292 + log.info("Notifying peers of a device offline topology event for deviceId: {}",
293 + deviceId);
294 + try {
295 + notifyPeers(new InternalDeviceOfflineEvent(deviceId, timestamp));
296 + } catch (IOException e) {
297 + log.error("Failed to notify peers of a device offline topology event for deviceId: {}",
298 + deviceId);
299 + }
300 + }
301 + return event;
282 } 302 }
283 303
284 private DeviceEvent markOfflineInternal(DeviceId deviceId, Timestamp timestamp) { 304 private DeviceEvent markOfflineInternal(DeviceId deviceId, Timestamp timestamp) {
...@@ -566,7 +586,16 @@ public class GossipDeviceStore ...@@ -566,7 +586,16 @@ public class GossipDeviceStore
566 public synchronized DeviceEvent removeDevice(DeviceId deviceId) { 586 public synchronized DeviceEvent removeDevice(DeviceId deviceId) {
567 Timestamp timestamp = clockService.getTimestamp(deviceId); 587 Timestamp timestamp = clockService.getTimestamp(deviceId);
568 DeviceEvent event = removeDeviceInternal(deviceId, timestamp); 588 DeviceEvent event = removeDeviceInternal(deviceId, timestamp);
569 - // TODO: broadcast removal event 589 + if (event != null) {
590 + log.info("Notifying peers of a device removed topology event for deviceId: {}",
591 + deviceId);
592 + try {
593 + notifyPeers(new InternalDeviceRemovedEvent(deviceId, timestamp));
594 + } catch (IOException e) {
595 + log.error("Failed to notify peers of a device removed topology event for deviceId: {}",
596 + deviceId);
597 + }
598 + }
570 return event; 599 return event;
571 } 600 }
572 601
...@@ -809,6 +838,22 @@ public class GossipDeviceStore ...@@ -809,6 +838,22 @@ public class GossipDeviceStore
809 clusterCommunicator.broadcast(message); 838 clusterCommunicator.broadcast(message);
810 } 839 }
811 840
841 + private void notifyPeers(InternalDeviceOfflineEvent event) throws IOException {
842 + ClusterMessage message = new ClusterMessage(
843 + clusterService.getLocalNode().id(),
844 + GossipDeviceStoreMessageSubjects.DEVICE_OFFLINE,
845 + SERIALIZER.encode(event));
846 + clusterCommunicator.broadcast(message);
847 + }
848 +
849 + private void notifyPeers(InternalDeviceRemovedEvent event) throws IOException {
850 + ClusterMessage message = new ClusterMessage(
851 + clusterService.getLocalNode().id(),
852 + GossipDeviceStoreMessageSubjects.DEVICE_REMOVED,
853 + SERIALIZER.encode(event));
854 + clusterCommunicator.broadcast(message);
855 + }
856 +
812 private void notifyPeers(InternalPortEvent event) throws IOException { 857 private void notifyPeers(InternalPortEvent event) throws IOException {
813 ClusterMessage message = new ClusterMessage( 858 ClusterMessage message = new ClusterMessage(
814 clusterService.getLocalNode().id(), 859 clusterService.getLocalNode().id(),
...@@ -828,15 +873,46 @@ public class GossipDeviceStore ...@@ -828,15 +873,46 @@ public class GossipDeviceStore
828 private class InternalDeviceEventListener implements ClusterMessageHandler { 873 private class InternalDeviceEventListener implements ClusterMessageHandler {
829 @Override 874 @Override
830 public void handle(ClusterMessage message) { 875 public void handle(ClusterMessage message) {
876 +
831 log.info("Received device update event from peer: {}", message.sender()); 877 log.info("Received device update event from peer: {}", message.sender());
832 InternalDeviceEvent event = (InternalDeviceEvent) SERIALIZER.decode(message.payload()); 878 InternalDeviceEvent event = (InternalDeviceEvent) SERIALIZER.decode(message.payload());
879 +
833 ProviderId providerId = event.providerId(); 880 ProviderId providerId = event.providerId();
834 DeviceId deviceId = event.deviceId(); 881 DeviceId deviceId = event.deviceId();
835 Timestamped<DeviceDescription> deviceDescription = event.deviceDescription(); 882 Timestamped<DeviceDescription> deviceDescription = event.deviceDescription();
883 +
836 createOrUpdateDeviceInternal(providerId, deviceId, deviceDescription); 884 createOrUpdateDeviceInternal(providerId, deviceId, deviceDescription);
837 } 885 }
838 } 886 }
839 887
888 + private class InternalDeviceOfflineEventListener implements ClusterMessageHandler {
889 + @Override
890 + public void handle(ClusterMessage message) {
891 +
892 + log.info("Received device offline event from peer: {}", message.sender());
893 + InternalDeviceOfflineEvent event = (InternalDeviceOfflineEvent) SERIALIZER.decode(message.payload());
894 +
895 + DeviceId deviceId = event.deviceId();
896 + Timestamp timestamp = event.timestamp();
897 +
898 + markOfflineInternal(deviceId, timestamp);
899 + }
900 + }
901 +
902 + private class InternalDeviceRemovedEventListener implements ClusterMessageHandler {
903 + @Override
904 + public void handle(ClusterMessage message) {
905 +
906 + log.info("Received device removed event from peer: {}", message.sender());
907 + InternalDeviceRemovedEvent event = (InternalDeviceRemovedEvent) SERIALIZER.decode(message.payload());
908 +
909 + DeviceId deviceId = event.deviceId();
910 + Timestamp timestamp = event.timestamp();
911 +
912 + removeDeviceInternal(deviceId, timestamp);
913 + }
914 + }
915 +
840 private class InternalPortEventListener implements ClusterMessageHandler { 916 private class InternalPortEventListener implements ClusterMessageHandler {
841 @Override 917 @Override
842 public void handle(ClusterMessage message) { 918 public void handle(ClusterMessage message) {
......
...@@ -3,13 +3,15 @@ package org.onlab.onos.store.device.impl; ...@@ -3,13 +3,15 @@ package org.onlab.onos.store.device.impl;
3 import org.onlab.onos.store.cluster.messaging.MessageSubject; 3 import org.onlab.onos.store.cluster.messaging.MessageSubject;
4 4
5 /** 5 /**
6 - * MessageSubjects used by GossipDeviceStore. 6 + * MessageSubjects used by GossipDeviceStore peer-peer communication.
7 */ 7 */
8 public final class GossipDeviceStoreMessageSubjects { 8 public final class GossipDeviceStoreMessageSubjects {
9 9
10 private GossipDeviceStoreMessageSubjects() {} 10 private GossipDeviceStoreMessageSubjects() {}
11 11
12 public static final MessageSubject DEVICE_UPDATE = new MessageSubject("peer-device-update"); 12 public static final MessageSubject DEVICE_UPDATE = new MessageSubject("peer-device-update");
13 + public static final MessageSubject DEVICE_OFFLINE = new MessageSubject("peer-device-offline");
14 + public static final MessageSubject DEVICE_REMOVED = new MessageSubject("peer-device-removed");
13 public static final MessageSubject PORT_UPDATE = new MessageSubject("peer-port-update"); 15 public static final MessageSubject PORT_UPDATE = new MessageSubject("peer-port-update");
14 public static final MessageSubject PORT_STATUS_UPDATE = new MessageSubject("peer-port-status-update"); 16 public static final MessageSubject PORT_STATUS_UPDATE = new MessageSubject("peer-port-status-update");
15 } 17 }
......
...@@ -35,4 +35,11 @@ public class InternalDeviceEvent { ...@@ -35,4 +35,11 @@ public class InternalDeviceEvent {
35 public Timestamped<DeviceDescription> deviceDescription() { 35 public Timestamped<DeviceDescription> deviceDescription() {
36 return deviceDescription; 36 return deviceDescription;
37 } 37 }
38 +
39 + // for serializer
40 + protected InternalDeviceEvent() {
41 + this.providerId = null;
42 + this.deviceId = null;
43 + this.deviceDescription = null;
44 + }
38 } 45 }
......
1 +package org.onlab.onos.store.device.impl;
2 +
3 +import org.onlab.onos.net.DeviceId;
4 +import org.onlab.onos.net.device.DeviceDescription;
5 +import org.onlab.onos.net.provider.ProviderId;
6 +import org.onlab.onos.store.common.impl.Timestamped;
7 +
8 +import com.esotericsoftware.kryo.Kryo;
9 +import com.esotericsoftware.kryo.Serializer;
10 +import com.esotericsoftware.kryo.io.Input;
11 +import com.esotericsoftware.kryo.io.Output;
12 +
13 +/**
14 + * Kryo Serializer for {@link InternalDeviceEvent}.
15 + */
16 +public class InternalDeviceEventSerializer extends Serializer<InternalDeviceEvent> {
17 +
18 + /**
19 + * Creates a serializer for {@link InternalDeviceEvent}.
20 + */
21 + public InternalDeviceEventSerializer() {
22 + // does not accept null
23 + super(false);
24 + }
25 +
26 + @Override
27 + public void write(Kryo kryo, Output output, InternalDeviceEvent event) {
28 + kryo.writeClassAndObject(output, event.providerId());
29 + kryo.writeClassAndObject(output, event.deviceId());
30 + kryo.writeClassAndObject(output, event.deviceDescription());
31 + }
32 +
33 + @Override
34 + public InternalDeviceEvent read(Kryo kryo, Input input,
35 + Class<InternalDeviceEvent> type) {
36 + ProviderId providerId = (ProviderId) kryo.readClassAndObject(input);
37 + DeviceId deviceId = (DeviceId) kryo.readClassAndObject(input);
38 + Timestamped<DeviceDescription> deviceDescription
39 + = (Timestamped<DeviceDescription>) kryo.readClassAndObject(input);
40 +
41 + return new InternalDeviceEvent(providerId, deviceId, deviceDescription);
42 + }
43 +}
1 +package org.onlab.onos.store.device.impl;
2 +
3 +import org.onlab.onos.net.DeviceId;
4 +import org.onlab.onos.store.Timestamp;
5 +
6 +/**
7 + * Information published by GossipDeviceStore to notify peers of a device
8 + * going offline.
9 + */
10 +public class InternalDeviceOfflineEvent {
11 +
12 + private final DeviceId deviceId;
13 + private final Timestamp timestamp;
14 +
15 + /**
16 + * Creates a InternalDeviceOfflineEvent.
17 + * @param deviceId identifier of device going offline.
18 + * @param timestamp timestamp of when the device went offline.
19 + */
20 + public InternalDeviceOfflineEvent(DeviceId deviceId, Timestamp timestamp) {
21 + this.deviceId = deviceId;
22 + this.timestamp = timestamp;
23 + }
24 +
25 + public DeviceId deviceId() {
26 + return deviceId;
27 + }
28 +
29 + public Timestamp timestamp() {
30 + return timestamp;
31 + }
32 +
33 + // for serializer
34 + @SuppressWarnings("unused")
35 + private InternalDeviceOfflineEvent() {
36 + deviceId = null;
37 + timestamp = null;
38 + }
39 +}
1 +package org.onlab.onos.store.device.impl;
2 +
3 +import org.onlab.onos.net.DeviceId;
4 +import org.onlab.onos.store.Timestamp;
5 +
6 +import com.esotericsoftware.kryo.Kryo;
7 +import com.esotericsoftware.kryo.Serializer;
8 +import com.esotericsoftware.kryo.io.Input;
9 +import com.esotericsoftware.kryo.io.Output;
10 +
11 +/**
12 + * Kryo Serializer for {@link InternalDeviceOfflineEvent}.
13 + */
14 +public class InternalDeviceOfflineEventSerializer extends Serializer<InternalDeviceOfflineEvent> {
15 +
16 + /**
17 + * Creates a serializer for {@link InternalDeviceOfflineEvent}.
18 + */
19 + public InternalDeviceOfflineEventSerializer() {
20 + // does not accept null
21 + super(false);
22 + }
23 +
24 + @Override
25 + public void write(Kryo kryo, Output output, InternalDeviceOfflineEvent event) {
26 + kryo.writeClassAndObject(output, event.deviceId());
27 + kryo.writeClassAndObject(output, event.timestamp());
28 + }
29 +
30 + @Override
31 + public InternalDeviceOfflineEvent read(Kryo kryo, Input input,
32 + Class<InternalDeviceOfflineEvent> type) {
33 + DeviceId deviceId = (DeviceId) kryo.readClassAndObject(input);
34 + Timestamp timestamp = (Timestamp) kryo.readClassAndObject(input);
35 +
36 + return new InternalDeviceOfflineEvent(deviceId, timestamp);
37 + }
38 +}
1 +package org.onlab.onos.store.device.impl;
2 +
3 +import org.onlab.onos.net.DeviceId;
4 +import org.onlab.onos.store.Timestamp;
5 +
6 +/**
7 + * Information published by GossipDeviceStore to notify peers of a device
8 + * being administratively removed.
9 + */
10 +public class InternalDeviceRemovedEvent {
11 +
12 + private final DeviceId deviceId;
13 + private final Timestamp timestamp;
14 +
15 + /**
16 + * Creates a InternalDeviceRemovedEvent.
17 + * @param deviceId identifier of the removed device.
18 + * @param timestamp timestamp of when the device was administratively removed.
19 + */
20 + public InternalDeviceRemovedEvent(DeviceId deviceId, Timestamp timestamp) {
21 + this.deviceId = deviceId;
22 + this.timestamp = timestamp;
23 + }
24 +
25 + public DeviceId deviceId() {
26 + return deviceId;
27 + }
28 +
29 + public Timestamp timestamp() {
30 + return timestamp;
31 + }
32 +
33 + // for serializer
34 + @SuppressWarnings("unused")
35 + private InternalDeviceRemovedEvent() {
36 + deviceId = null;
37 + timestamp = null;
38 + }
39 +}
...@@ -37,4 +37,11 @@ public class InternalPortEvent { ...@@ -37,4 +37,11 @@ public class InternalPortEvent {
37 public Timestamped<List<PortDescription>> portDescriptions() { 37 public Timestamped<List<PortDescription>> portDescriptions() {
38 return portDescriptions; 38 return portDescriptions;
39 } 39 }
40 +
41 + // for serializer
42 + protected InternalPortEvent() {
43 + this.providerId = null;
44 + this.deviceId = null;
45 + this.portDescriptions = null;
46 + }
40 } 47 }
......
1 +package org.onlab.onos.store.device.impl;
2 +
3 +import java.util.List;
4 +
5 +import org.onlab.onos.net.DeviceId;
6 +import org.onlab.onos.net.device.PortDescription;
7 +import org.onlab.onos.net.provider.ProviderId;
8 +import org.onlab.onos.store.common.impl.Timestamped;
9 +
10 +import com.esotericsoftware.kryo.Kryo;
11 +import com.esotericsoftware.kryo.Serializer;
12 +import com.esotericsoftware.kryo.io.Input;
13 +import com.esotericsoftware.kryo.io.Output;
14 +
15 +/**
16 + * Kryo Serializer for {@link InternalPortEvent}.
17 + */
18 +public class InternalPortEventSerializer extends Serializer<InternalPortEvent> {
19 +
20 + /**
21 + * Creates a serializer for {@link InternalPortEvent}.
22 + */
23 + public InternalPortEventSerializer() {
24 + // does not accept null
25 + super(false);
26 + }
27 +
28 + @Override
29 + public void write(Kryo kryo, Output output, InternalPortEvent event) {
30 + kryo.writeClassAndObject(output, event.providerId());
31 + kryo.writeClassAndObject(output, event.deviceId());
32 + kryo.writeClassAndObject(output, event.portDescriptions());
33 + }
34 +
35 + @Override
36 + public InternalPortEvent read(Kryo kryo, Input input,
37 + Class<InternalPortEvent> type) {
38 + ProviderId providerId = (ProviderId) kryo.readClassAndObject(input);
39 + DeviceId deviceId = (DeviceId) kryo.readClassAndObject(input);
40 + Timestamped<List<PortDescription>> portDescriptions
41 + = (Timestamped<List<PortDescription>>) kryo.readClassAndObject(input);
42 +
43 + return new InternalPortEvent(providerId, deviceId, portDescriptions);
44 + }
45 +}
...@@ -35,4 +35,11 @@ public class InternalPortStatusEvent { ...@@ -35,4 +35,11 @@ public class InternalPortStatusEvent {
35 public Timestamped<PortDescription> portDescription() { 35 public Timestamped<PortDescription> portDescription() {
36 return portDescription; 36 return portDescription;
37 } 37 }
38 +
39 + // for serializer
40 + protected InternalPortStatusEvent() {
41 + this.providerId = null;
42 + this.deviceId = null;
43 + this.portDescription = null;
44 + }
38 } 45 }
......
1 +package org.onlab.onos.store.device.impl;
2 +
3 +import org.onlab.onos.net.DeviceId;
4 +import org.onlab.onos.net.device.PortDescription;
5 +import org.onlab.onos.net.provider.ProviderId;
6 +import org.onlab.onos.store.common.impl.Timestamped;
7 +
8 +import com.esotericsoftware.kryo.Kryo;
9 +import com.esotericsoftware.kryo.Serializer;
10 +import com.esotericsoftware.kryo.io.Input;
11 +import com.esotericsoftware.kryo.io.Output;
12 +
13 +/**
14 + * Kryo Serializer for {@link InternalPortStatusEvent}.
15 + */
16 +public class InternalPortStatusEventSerializer extends Serializer<InternalPortStatusEvent> {
17 +
18 + /**
19 + * Creates a serializer for {@link InternalPortStatusEvent}.
20 + */
21 + public InternalPortStatusEventSerializer() {
22 + // does not accept null
23 + super(false);
24 + }
25 +
26 + @Override
27 + public void write(Kryo kryo, Output output, InternalPortStatusEvent event) {
28 + kryo.writeClassAndObject(output, event.providerId());
29 + kryo.writeClassAndObject(output, event.deviceId());
30 + kryo.writeClassAndObject(output, event.portDescription());
31 + }
32 +
33 + @Override
34 + public InternalPortStatusEvent read(Kryo kryo, Input input,
35 + Class<InternalPortStatusEvent> type) {
36 + ProviderId providerId = (ProviderId) kryo.readClassAndObject(input);
37 + DeviceId deviceId = (DeviceId) kryo.readClassAndObject(input);
38 + Timestamped<PortDescription> portDescription = (Timestamped<PortDescription>) kryo.readClassAndObject(input);
39 +
40 + return new InternalPortStatusEvent(providerId, deviceId, portDescription);
41 + }
42 +}
...@@ -3,7 +3,6 @@ package org.onlab.onos.store.serializers; ...@@ -3,7 +3,6 @@ package org.onlab.onos.store.serializers;
3 import org.onlab.onos.cluster.NodeId; 3 import org.onlab.onos.cluster.NodeId;
4 import org.onlab.onos.store.cluster.messaging.ClusterMessage; 4 import org.onlab.onos.store.cluster.messaging.ClusterMessage;
5 import org.onlab.onos.store.cluster.messaging.MessageSubject; 5 import org.onlab.onos.store.cluster.messaging.MessageSubject;
6 -
7 import com.esotericsoftware.kryo.Kryo; 6 import com.esotericsoftware.kryo.Kryo;
8 import com.esotericsoftware.kryo.Serializer; 7 import com.esotericsoftware.kryo.Serializer;
9 import com.esotericsoftware.kryo.io.Input; 8 import com.esotericsoftware.kryo.io.Input;
...@@ -11,6 +10,9 @@ import com.esotericsoftware.kryo.io.Output; ...@@ -11,6 +10,9 @@ import com.esotericsoftware.kryo.io.Output;
11 10
12 public final class ClusterMessageSerializer extends Serializer<ClusterMessage> { 11 public final class ClusterMessageSerializer extends Serializer<ClusterMessage> {
13 12
13 + /**
14 + * Creates a serializer for {@link ClusterMessage}.
15 + */
14 public ClusterMessageSerializer() { 16 public ClusterMessageSerializer() {
15 // does not accept null 17 // does not accept null
16 super(false); 18 super(false);
......
...@@ -14,7 +14,7 @@ import com.esotericsoftware.kryo.io.Output; ...@@ -14,7 +14,7 @@ import com.esotericsoftware.kryo.io.Output;
14 public class MastershipBasedTimestampSerializer extends Serializer<MastershipBasedTimestamp> { 14 public class MastershipBasedTimestampSerializer extends Serializer<MastershipBasedTimestamp> {
15 15
16 /** 16 /**
17 - * Default constructor. 17 + * Creates a serializer for {@link MastershipBasedTimestamp}.
18 */ 18 */
19 public MastershipBasedTimestampSerializer() { 19 public MastershipBasedTimestampSerializer() {
20 // non-null, immutable 20 // non-null, immutable
......
1 +package org.onlab.onos.store.serializers;
2 +
3 +import org.onlab.onos.store.cluster.messaging.MessageSubject;
4 +
5 +import com.esotericsoftware.kryo.Kryo;
6 +import com.esotericsoftware.kryo.Serializer;
7 +import com.esotericsoftware.kryo.io.Input;
8 +import com.esotericsoftware.kryo.io.Output;
9 +
10 +public final class MessageSubjectSerializer extends Serializer<MessageSubject> {
11 +
12 + /**
13 + * Creates a serializer for {@link MessageSubject}.
14 + */
15 + public MessageSubjectSerializer() {
16 + // non-null, immutable
17 + super(false, true);
18 + }
19 +
20 +
21 + @Override
22 + public void write(Kryo kryo, Output output, MessageSubject object) {
23 + output.writeString(object.value());
24 + }
25 +
26 + @Override
27 + public MessageSubject read(Kryo kryo, Input input,
28 + Class<MessageSubject> type) {
29 + return new MessageSubject(input.readString());
30 + }
31 +}
1 package org.onlab.onos.store.cluster.impl; 1 package org.onlab.onos.store.cluster.impl;
2 2
3 -import static com.google.common.cache.CacheBuilder.newBuilder;
4 import static org.onlab.onos.cluster.MastershipEvent.Type.MASTER_CHANGED; 3 import static org.onlab.onos.cluster.MastershipEvent.Type.MASTER_CHANGED;
5 4
6 import java.util.Map; 5 import java.util.Map;
7 -import java.util.Objects;
8 import java.util.Set; 6 import java.util.Set;
9 7
10 import org.apache.felix.scr.annotations.Activate; 8 import org.apache.felix.scr.annotations.Activate;
...@@ -21,17 +19,16 @@ import org.onlab.onos.cluster.MastershipTerm; ...@@ -21,17 +19,16 @@ import org.onlab.onos.cluster.MastershipTerm;
21 import org.onlab.onos.cluster.NodeId; 19 import org.onlab.onos.cluster.NodeId;
22 import org.onlab.onos.net.DeviceId; 20 import org.onlab.onos.net.DeviceId;
23 import org.onlab.onos.net.MastershipRole; 21 import org.onlab.onos.net.MastershipRole;
24 -import org.onlab.onos.store.common.AbsentInvalidatingLoadingCache;
25 import org.onlab.onos.store.common.AbstractHazelcastStore; 22 import org.onlab.onos.store.common.AbstractHazelcastStore;
26 -import org.onlab.onos.store.common.OptionalCacheLoader;
27 23
28 -import com.google.common.base.Optional;
29 -import com.google.common.cache.LoadingCache;
30 import com.google.common.collect.ImmutableSet; 24 import com.google.common.collect.ImmutableSet;
25 +import com.hazelcast.core.ILock;
31 import com.hazelcast.core.IMap; 26 import com.hazelcast.core.IMap;
27 +import com.hazelcast.core.MultiMap;
32 28
33 /** 29 /**
34 - * Distributed implementation of the cluster nodes store. 30 + * Distributed implementation of the mastership store. The store is
31 + * responsible for the master selection process.
35 */ 32 */
36 @Component(immediate = true) 33 @Component(immediate = true)
37 @Service 34 @Service
...@@ -39,8 +36,21 @@ public class DistributedMastershipStore ...@@ -39,8 +36,21 @@ public class DistributedMastershipStore
39 extends AbstractHazelcastStore<MastershipEvent, MastershipStoreDelegate> 36 extends AbstractHazelcastStore<MastershipEvent, MastershipStoreDelegate>
40 implements MastershipStore { 37 implements MastershipStore {
41 38
42 - private IMap<byte[], byte[]> rawMasters; 39 + //arbitrary lock name
43 - private LoadingCache<DeviceId, Optional<NodeId>> masters; 40 + private static final String LOCK = "lock";
41 + //initial term/TTL value
42 + private static final Integer INIT = 0;
43 +
44 + //devices to masters
45 + protected IMap<byte[], byte[]> masters;
46 + //devices to terms
47 + protected IMap<byte[], Integer> terms;
48 +
49 + //re-election related, disjoint-set structures:
50 + //device-nodes multiset of available nodes
51 + protected MultiMap<byte[], byte[]> standbys;
52 + //device-nodes multiset for nodes that have given up on device
53 + protected MultiMap<byte[], byte[]> unusable;
44 54
45 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) 55 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
46 protected ClusterService clusterService; 56 protected ClusterService clusterService;
...@@ -50,90 +60,264 @@ implements MastershipStore { ...@@ -50,90 +60,264 @@ implements MastershipStore {
50 public void activate() { 60 public void activate() {
51 super.activate(); 61 super.activate();
52 62
53 - rawMasters = theInstance.getMap("masters"); 63 + masters = theInstance.getMap("masters");
54 - OptionalCacheLoader<DeviceId, NodeId> nodeLoader 64 + terms = theInstance.getMap("terms");
55 - = new OptionalCacheLoader<>(serializer, rawMasters); 65 + standbys = theInstance.getMultiMap("backups");
56 - masters = new AbsentInvalidatingLoadingCache<>(newBuilder().build(nodeLoader)); 66 + unusable = theInstance.getMultiMap("unusable");
57 - rawMasters.addEntryListener(new RemoteMasterShipEventHandler(masters), true);
58 67
59 - loadMasters(); 68 + masters.addEntryListener(new RemoteMasterShipEventHandler(), true);
60 69
61 log.info("Started"); 70 log.info("Started");
62 } 71 }
63 72
64 - private void loadMasters() {
65 - for (byte[] keyBytes : rawMasters.keySet()) {
66 - final DeviceId id = deserialize(keyBytes);
67 - masters.refresh(id);
68 - }
69 - }
70 -
71 @Deactivate 73 @Deactivate
72 public void deactivate() { 74 public void deactivate() {
73 log.info("Stopped"); 75 log.info("Stopped");
74 } 76 }
75 77
76 @Override 78 @Override
79 + public MastershipRole getRole(NodeId nodeId, DeviceId deviceId) {
80 + byte[] did = serialize(deviceId);
81 + byte[] nid = serialize(nodeId);
82 +
83 + NodeId current = deserialize(masters.get(did));
84 + if (current == null) {
85 + if (standbys.containsEntry(did, nid)) {
86 + //was previously standby, or set to standby from master
87 + return MastershipRole.STANDBY;
88 + } else {
89 + return MastershipRole.NONE;
90 + }
91 + } else {
92 + if (current.equals(nodeId)) {
93 + //*should* be in unusable, not always
94 + return MastershipRole.MASTER;
95 + } else {
96 + //may be in backups or unusable from earlier retirement
97 + return MastershipRole.STANDBY;
98 + }
99 + }
100 + }
101 +
102 + @Override
77 public MastershipEvent setMaster(NodeId nodeId, DeviceId deviceId) { 103 public MastershipEvent setMaster(NodeId nodeId, DeviceId deviceId) {
78 - synchronized (this) { 104 + byte [] did = serialize(deviceId);
79 - NodeId currentMaster = getMaster(deviceId); 105 + byte [] nid = serialize(nodeId);
80 - if (Objects.equals(currentMaster, nodeId)) { 106 +
107 + ILock lock = theInstance.getLock(LOCK);
108 + lock.lock();
109 + try {
110 + MastershipRole role = getRole(nodeId, deviceId);
111 + switch (role) {
112 + case MASTER:
113 + //reinforce mastership
114 + evict(nid, did);
81 return null; 115 return null;
116 + case STANDBY:
117 + //make current master standby
118 + byte [] current = masters.get(did);
119 + if (current != null) {
120 + backup(current, did);
82 } 121 }
83 - 122 + //assign specified node as new master
84 - // FIXME: for now implementing semantics of setMaster 123 + masters.put(did, nid);
85 - rawMasters.put(serialize(deviceId), serialize(nodeId)); 124 + evict(nid, did);
86 - masters.put(deviceId, Optional.of(nodeId)); 125 + updateTerm(did);
87 - return new MastershipEvent(MastershipEvent.Type.MASTER_CHANGED, deviceId, nodeId); 126 + return new MastershipEvent(MASTER_CHANGED, deviceId, nodeId);
127 + case NONE:
128 + masters.put(did, nid);
129 + evict(nid, did);
130 + updateTerm(did);
131 + return new MastershipEvent(MASTER_CHANGED, deviceId, nodeId);
132 + default:
133 + log.warn("unknown Mastership Role {}", role);
134 + return null;
135 + }
136 + } finally {
137 + lock.unlock();
88 } 138 }
89 } 139 }
90 140
91 @Override 141 @Override
92 public NodeId getMaster(DeviceId deviceId) { 142 public NodeId getMaster(DeviceId deviceId) {
93 - return masters.getUnchecked(deviceId).orNull(); 143 + return deserialize(masters.get(serialize(deviceId)));
94 } 144 }
95 145
96 @Override 146 @Override
97 public Set<DeviceId> getDevices(NodeId nodeId) { 147 public Set<DeviceId> getDevices(NodeId nodeId) {
98 ImmutableSet.Builder<DeviceId> builder = ImmutableSet.builder(); 148 ImmutableSet.Builder<DeviceId> builder = ImmutableSet.builder();
99 - for (Map.Entry<DeviceId, Optional<NodeId>> entry : masters.asMap().entrySet()) { 149 +
100 - if (nodeId.equals(entry.getValue().get())) { 150 + for (Map.Entry<byte[], byte[]> entry : masters.entrySet()) {
101 - builder.add(entry.getKey()); 151 + if (nodeId.equals(deserialize(entry.getValue()))) {
152 + builder.add((DeviceId) deserialize(entry.getKey()));
102 } 153 }
103 } 154 }
155 +
104 return builder.build(); 156 return builder.build();
105 } 157 }
106 158
107 @Override 159 @Override
108 public MastershipRole requestRole(DeviceId deviceId) { 160 public MastershipRole requestRole(DeviceId deviceId) {
109 - // FIXME: for now we are 'selecting' as master whoever asks 161 + NodeId local = clusterService.getLocalNode().id();
110 - setMaster(clusterService.getLocalNode().id(), deviceId); 162 + byte [] did = serialize(deviceId);
111 - return MastershipRole.MASTER; 163 + byte [] lnid = serialize(local);
112 - }
113 164
114 - @Override 165 + ILock lock = theInstance.getLock(LOCK);
115 - public MastershipRole getRole(NodeId nodeId, DeviceId deviceId) { 166 + lock.lock();
116 - NodeId master = masters.getUnchecked(deviceId).orNull(); 167 + try {
117 - return nodeId.equals(master) ? MastershipRole.MASTER : MastershipRole.STANDBY; 168 + MastershipRole role = getRole(local, deviceId);
169 + switch (role) {
170 + case MASTER:
171 + evict(lnid, did);
172 + break;
173 + case STANDBY:
174 + backup(lnid, did);
175 + terms.putIfAbsent(did, INIT);
176 + break;
177 + case NONE:
178 + //claim mastership
179 + masters.put(did, lnid);
180 + evict(lnid, did);
181 + updateTerm(did);
182 + role = MastershipRole.MASTER;
183 + break;
184 + default:
185 + log.warn("unknown Mastership Role {}", role);
186 + }
187 + return role;
188 + } finally {
189 + lock.unlock();
190 + }
118 } 191 }
119 192
120 @Override 193 @Override
121 public MastershipTerm getTermFor(DeviceId deviceId) { 194 public MastershipTerm getTermFor(DeviceId deviceId) {
122 - // TODO Auto-generated method stub 195 + byte[] did = serialize(deviceId);
196 + if ((masters.get(did) == null) ||
197 + (terms.get(did) == null)) {
123 return null; 198 return null;
124 } 199 }
200 + return MastershipTerm.of(
201 + (NodeId) deserialize(masters.get(did)), terms.get(did));
202 + }
125 203
126 @Override 204 @Override
127 - public MastershipEvent unsetMaster(NodeId nodeId, DeviceId deviceId) { 205 + public MastershipEvent setStandby(NodeId nodeId, DeviceId deviceId) {
128 - // TODO Auto-generated method stub 206 + byte [] did = serialize(deviceId);
207 + byte [] nid = serialize(nodeId);
208 + MastershipEvent event = null;
209 +
210 + ILock lock = theInstance.getLock(LOCK);
211 + lock.lock();
212 + try {
213 + MastershipRole role = getRole(nodeId, deviceId);
214 + switch (role) {
215 + case MASTER:
216 + event = reelect(nodeId, deviceId);
217 + backup(nid, did);
218 + break;
219 + case STANDBY:
220 + //fall through to reinforce role
221 + case NONE:
222 + backup(nid, did);
223 + break;
224 + default:
225 + log.warn("unknown Mastership Role {}", role);
226 + }
227 + return event;
228 + } finally {
229 + lock.unlock();
230 + }
231 + }
232 +
233 + @Override
234 + public MastershipEvent relinquishRole(NodeId nodeId, DeviceId deviceId) {
235 + byte [] did = serialize(deviceId);
236 + byte [] nid = serialize(nodeId);
237 + MastershipEvent event = null;
238 +
239 + ILock lock = theInstance.getLock(LOCK);
240 + lock.lock();
241 + try {
242 + MastershipRole role = getRole(nodeId, deviceId);
243 + switch (role) {
244 + case MASTER:
245 + event = reelect(nodeId, deviceId);
246 + evict(nid, did);
247 + break;
248 + case STANDBY:
249 + //fall through to reinforce relinquishment
250 + case NONE:
251 + evict(nid, did);
252 + break;
253 + default:
254 + log.warn("unknown Mastership Role {}", role);
255 + }
256 + return event;
257 + } finally {
258 + lock.unlock();
259 + }
260 + }
261 +
262 + //helper to fetch a new master candidate for a given device.
263 + private MastershipEvent reelect(NodeId current, DeviceId deviceId) {
264 + byte [] did = serialize(deviceId);
265 + byte [] nid = serialize(current);
266 +
267 + //if this is an queue it'd be neater.
268 + byte [] backup = null;
269 + for (byte [] n : standbys.get(serialize(deviceId))) {
270 + if (!current.equals(deserialize(n))) {
271 + backup = n;
272 + break;
273 + }
274 + }
275 +
276 + if (backup == null) {
277 + masters.remove(did, nid);
129 return null; 278 return null;
279 + } else {
280 + masters.put(did, backup);
281 + evict(backup, did);
282 + Integer term = terms.get(did);
283 + terms.put(did, ++term);
284 + return new MastershipEvent(
285 + MASTER_CHANGED, deviceId, (NodeId) deserialize(backup));
286 + }
130 } 287 }
131 288
132 - private class RemoteMasterShipEventHandler extends RemoteCacheEventHandler<DeviceId, NodeId> { 289 + //adds node to pool(s) of backups and moves them from unusable.
133 - public RemoteMasterShipEventHandler(LoadingCache<DeviceId, Optional<NodeId>> cache) { 290 + private void backup(byte [] nodeId, byte [] deviceId) {
134 - super(cache); 291 + if (!standbys.containsEntry(deviceId, nodeId)) {
292 + standbys.put(deviceId, nodeId);
293 + }
294 + if (unusable.containsEntry(deviceId, nodeId)) {
295 + unusable.remove(deviceId, nodeId);
296 + }
297 + }
298 +
299 + //adds node to unusable and evicts it from backup pool.
300 + private void evict(byte [] nodeId, byte [] deviceId) {
301 + if (!unusable.containsEntry(deviceId, nodeId)) {
302 + unusable.put(deviceId, nodeId);
303 + }
304 + if (standbys.containsEntry(deviceId, nodeId)) {
305 + standbys.remove(deviceId, nodeId);
306 + }
135 } 307 }
136 308
309 + //adds or updates term information.
310 + private void updateTerm(byte [] deviceId) {
311 + Integer term = terms.get(deviceId);
312 + if (term == null) {
313 + terms.put(deviceId, INIT);
314 + } else {
315 + terms.put(deviceId, ++term);
316 + }
317 + }
318 +
319 + private class RemoteMasterShipEventHandler extends RemoteEventHandler<DeviceId, NodeId> {
320 +
137 @Override 321 @Override
138 protected void onAdd(DeviceId deviceId, NodeId nodeId) { 322 protected void onAdd(DeviceId deviceId, NodeId nodeId) {
139 notifyDelegate(new MastershipEvent(MASTER_CHANGED, deviceId, nodeId)); 323 notifyDelegate(new MastershipEvent(MASTER_CHANGED, deviceId, nodeId));
...@@ -141,12 +325,13 @@ implements MastershipStore { ...@@ -141,12 +325,13 @@ implements MastershipStore {
141 325
142 @Override 326 @Override
143 protected void onRemove(DeviceId deviceId, NodeId nodeId) { 327 protected void onRemove(DeviceId deviceId, NodeId nodeId) {
144 - notifyDelegate(new MastershipEvent(MASTER_CHANGED, deviceId, nodeId)); 328 + //notifyDelegate(new MastershipEvent(MASTER_CHANGED, deviceId, nodeId));
145 } 329 }
146 330
147 @Override 331 @Override
148 protected void onUpdate(DeviceId deviceId, NodeId oldNodeId, NodeId nodeId) { 332 protected void onUpdate(DeviceId deviceId, NodeId oldNodeId, NodeId nodeId) {
149 - notifyDelegate(new MastershipEvent(MASTER_CHANGED, deviceId, nodeId)); 333 + //only addition indicates a change in mastership
334 + //notifyDelegate(new MastershipEvent(MASTER_CHANGED, deviceId, nodeId));
150 } 335 }
151 } 336 }
152 337
......
1 +package org.onlab.onos.store.cluster.impl;
2 +
3 +import static org.junit.Assert.assertEquals;
4 +import static org.junit.Assert.assertNull;
5 +import static org.junit.Assert.assertTrue;
6 +import static org.onlab.onos.net.MastershipRole.*;
7 +
8 +import java.util.Map;
9 +import java.util.Set;
10 +import java.util.concurrent.CountDownLatch;
11 +import java.util.concurrent.TimeUnit;
12 +
13 +import org.junit.After;
14 +import org.junit.AfterClass;
15 +import org.junit.Before;
16 +import org.junit.BeforeClass;
17 +import org.junit.Ignore;
18 +import org.junit.Test;
19 +import org.onlab.onos.cluster.ClusterEventListener;
20 +import org.onlab.onos.cluster.ClusterService;
21 +import org.onlab.onos.cluster.ControllerNode;
22 +import org.onlab.onos.cluster.ControllerNode.State;
23 +import org.onlab.onos.cluster.DefaultControllerNode;
24 +import org.onlab.onos.cluster.MastershipEvent;
25 +import org.onlab.onos.cluster.MastershipEvent.Type;
26 +import org.onlab.onos.cluster.MastershipStoreDelegate;
27 +import org.onlab.onos.cluster.MastershipTerm;
28 +import org.onlab.onos.cluster.NodeId;
29 +import org.onlab.onos.net.DeviceId;
30 +import org.onlab.onos.store.common.StoreManager;
31 +import org.onlab.onos.store.common.StoreService;
32 +import org.onlab.onos.store.common.TestStoreManager;
33 +import org.onlab.onos.store.serializers.KryoSerializer;
34 +import org.onlab.packet.IpPrefix;
35 +
36 +import com.google.common.collect.Sets;
37 +import com.hazelcast.config.Config;
38 +import com.hazelcast.core.Hazelcast;
39 +
40 +/**
41 + * Test of the Hazelcast-based distributed MastershipStore implementation.
42 + */
43 +public class DistributedMastershipStoreTest {
44 +
45 + private static final DeviceId DID1 = DeviceId.deviceId("of:01");
46 + private static final DeviceId DID2 = DeviceId.deviceId("of:02");
47 + private static final DeviceId DID3 = DeviceId.deviceId("of:03");
48 +
49 + private static final IpPrefix IP = IpPrefix.valueOf("127.0.0.1");
50 +
51 + private static final NodeId N1 = new NodeId("node1");
52 + private static final NodeId N2 = new NodeId("node2");
53 +
54 + private static final ControllerNode CN1 = new DefaultControllerNode(N1, IP);
55 + private static final ControllerNode CN2 = new DefaultControllerNode(N2, IP);
56 +
57 + private DistributedMastershipStore dms;
58 + private TestDistributedMastershipStore testStore;
59 + private KryoSerializer serializationMgr;
60 + private StoreManager storeMgr;
61 +
62 + @BeforeClass
63 + public static void setUpBeforeClass() throws Exception {
64 + }
65 +
66 + @AfterClass
67 + public static void tearDownAfterClass() throws Exception {
68 + }
69 +
70 + @Before
71 + public void setUp() throws Exception {
72 + // TODO should find a way to clean Hazelcast instance without shutdown.
73 + Config config = TestStoreManager.getTestConfig();
74 +
75 + storeMgr = new TestStoreManager(Hazelcast.newHazelcastInstance(config));
76 + storeMgr.activate();
77 +
78 + serializationMgr = new KryoSerializer();
79 +
80 + dms = new TestDistributedMastershipStore(storeMgr, serializationMgr);
81 + dms.clusterService = new TestClusterService();
82 + dms.activate();
83 +
84 + testStore = (TestDistributedMastershipStore) dms;
85 + }
86 +
87 + @After
88 + public void tearDown() throws Exception {
89 + dms.deactivate();
90 +
91 + storeMgr.deactivate();
92 + }
93 +
94 + @Test
95 + public void getRole() {
96 + assertEquals("wrong role:", NONE, dms.getRole(N1, DID1));
97 + testStore.put(DID1, N1, true, false, true);
98 + assertEquals("wrong role:", MASTER, dms.getRole(N1, DID1));
99 + assertEquals("wrong role:", STANDBY, dms.getRole(N2, DID1));
100 + }
101 +
102 + @Test
103 + public void getMaster() {
104 + assertTrue("wrong store state:", dms.masters.isEmpty());
105 +
106 + testStore.put(DID1, N1, true, false, false);
107 + assertEquals("wrong master:", N1, dms.getMaster(DID1));
108 + assertNull("wrong master:", dms.getMaster(DID2));
109 + }
110 +
111 + @Test
112 + public void getDevices() {
113 + assertTrue("wrong store state:", dms.masters.isEmpty());
114 +
115 + testStore.put(DID1, N1, true, false, false);
116 + testStore.put(DID2, N1, true, false, false);
117 + testStore.put(DID3, N2, true, false, false);
118 +
119 + assertEquals("wrong devices",
120 + Sets.newHashSet(DID1, DID2), dms.getDevices(N1));
121 + }
122 +
123 + @Test
124 + public void requestRoleAndTerm() {
125 + //CN1 is "local"
126 + testStore.setCurrent(CN1);
127 +
128 + //if already MASTER, nothing should happen
129 + testStore.put(DID2, N1, true, false, false);
130 + assertEquals("wrong role for MASTER:", MASTER, dms.requestRole(DID2));
131 +
132 + //populate maps with DID1, N1 thru NONE case
133 + assertEquals("wrong role for NONE:", MASTER, dms.requestRole(DID1));
134 + assertTrue("wrong state for store:", !dms.terms.isEmpty());
135 + assertEquals("wrong term",
136 + MastershipTerm.of(N1, 0), dms.getTermFor(DID1));
137 +
138 + //CN2 now local. DID2 has N1 as MASTER so N2 is STANDBY
139 + testStore.setCurrent(CN2);
140 + assertEquals("wrong role for STANDBY:", STANDBY, dms.requestRole(DID2));
141 + assertEquals("wrong number of entries:", 2, dms.terms.size());
142 +
143 + //change term and requestRole() again; should persist
144 + testStore.increment(DID2);
145 + assertEquals("wrong role for STANDBY:", STANDBY, dms.requestRole(DID2));
146 + assertEquals("wrong term", MastershipTerm.of(N1, 1), dms.getTermFor(DID2));
147 + }
148 +
149 + @Test
150 + public void setMaster() {
151 + //populate maps with DID1, N1 as MASTER thru NONE case
152 + testStore.setCurrent(CN1);
153 + assertEquals("wrong role for NONE:", MASTER, dms.requestRole(DID1));
154 + assertNull("wrong event:", dms.setMaster(N1, DID1));
155 +
156 + //switch over to N2
157 + assertEquals("wrong event:", Type.MASTER_CHANGED, dms.setMaster(N2, DID1).type());
158 + assertEquals("wrong term", MastershipTerm.of(N2, 1), dms.getTermFor(DID1));
159 +
160 + //orphan switch - should be rare case
161 + assertEquals("wrong event:", Type.MASTER_CHANGED, dms.setMaster(N2, DID2).type());
162 + assertEquals("wrong term", MastershipTerm.of(N2, 0), dms.getTermFor(DID2));
163 + //disconnect and reconnect - sign of failing re-election or single-instance channel
164 + testStore.reset(true, false, false);
165 + dms.setMaster(N2, DID2);
166 + assertEquals("wrong term", MastershipTerm.of(N2, 1), dms.getTermFor(DID2));
167 + }
168 +
169 + @Test
170 + public void relinquishRole() {
171 + //populate maps with DID1, N1 as MASTER thru NONE case
172 + testStore.setCurrent(CN1);
173 + assertEquals("wrong role for NONE:", MASTER, dms.requestRole(DID1));
174 + //no backup, no new MASTER/event
175 + assertNull("wrong event:", dms.relinquishRole(N1, DID1));
176 +
177 + dms.requestRole(DID1);
178 +
179 + //add backup CN2, get it elected MASTER by relinquishing
180 + testStore.setCurrent(CN2);
181 + assertEquals("wrong role for NONE:", STANDBY, dms.requestRole(DID1));
182 + assertEquals("wrong event:", Type.MASTER_CHANGED, dms.relinquishRole(N1, DID1).type());
183 + assertEquals("wrong master", N2, dms.getMaster(DID1));
184 +
185 + //STANDBY - nothing here, either
186 + assertNull("wrong event:", dms.relinquishRole(N1, DID1));
187 + assertEquals("wrong role for node:", STANDBY, dms.getRole(N1, DID1));
188 +
189 + //all nodes "give up" on device, which goes back to NONE.
190 + assertNull("wrong event:", dms.relinquishRole(N2, DID1));
191 + assertEquals("wrong role for node:", NONE, dms.getRole(N2, DID1));
192 + assertEquals("wrong role for node:", NONE, dms.getRole(N1, DID1));
193 +
194 + assertEquals("wrong number of retired nodes", 2, dms.unusable.size());
195 +
196 + //bring nodes back
197 + assertEquals("wrong role for NONE:", MASTER, dms.requestRole(DID1));
198 + testStore.setCurrent(CN1);
199 + assertEquals("wrong role for NONE:", STANDBY, dms.requestRole(DID1));
200 + assertEquals("wrong number of backup nodes", 1, dms.standbys.size());
201 +
202 + //NONE - nothing happens
203 + assertNull("wrong event:", dms.relinquishRole(N1, DID2));
204 + assertEquals("wrong role for node:", NONE, dms.getRole(N1, DID2));
205 +
206 + }
207 +
208 + @Ignore("Ignore until Delegate spec. is clear.")
209 + @Test
210 + public void testEvents() throws InterruptedException {
211 + //shamelessly copy other distributed store tests
212 + final CountDownLatch addLatch = new CountDownLatch(1);
213 +
214 + MastershipStoreDelegate checkAdd = new MastershipStoreDelegate() {
215 + @Override
216 + public void notify(MastershipEvent event) {
217 + assertEquals("wrong event:", Type.MASTER_CHANGED, event.type());
218 + assertEquals("wrong subject", DID1, event.subject());
219 + assertEquals("wrong subject", N1, event.master());
220 + addLatch.countDown();
221 + }
222 + };
223 +
224 + dms.setDelegate(checkAdd);
225 + dms.setMaster(N1, DID1);
226 + //this will fail until we do something about single-instance-ness
227 + assertTrue("Add event fired", addLatch.await(1, TimeUnit.SECONDS));
228 + }
229 +
230 + private class TestDistributedMastershipStore extends
231 + DistributedMastershipStore {
232 + public TestDistributedMastershipStore(StoreService storeService,
233 + KryoSerializer kryoSerialization) {
234 + this.storeService = storeService;
235 + this.serializer = kryoSerialization;
236 + }
237 +
238 + //helper to populate master/backup structures
239 + public void put(DeviceId dev, NodeId node,
240 + boolean master, boolean backup, boolean term) {
241 + byte [] n = serialize(node);
242 + byte [] d = serialize(dev);
243 +
244 + if (master) {
245 + dms.masters.put(d, n);
246 + dms.unusable.put(d, n);
247 + dms.standbys.remove(d, n);
248 + }
249 + if (backup) {
250 + dms.standbys.put(d, n);
251 + dms.masters.remove(d, n);
252 + dms.unusable.remove(d, n);
253 + }
254 + if (term) {
255 + dms.terms.put(d, 0);
256 + }
257 + }
258 +
259 + //a dumb utility function.
260 + public void dump() {
261 + System.out.println("standbys");
262 + for (Map.Entry<byte [], byte []> e : standbys.entrySet()) {
263 + System.out.println(deserialize(e.getKey()) + ":" + deserialize(e.getValue()));
264 + }
265 + System.out.println("unusable");
266 + for (Map.Entry<byte [], byte []> e : unusable.entrySet()) {
267 + System.out.println(deserialize(e.getKey()) + ":" + deserialize(e.getValue()));
268 + }
269 + }
270 +
271 + //clears structures
272 + public void reset(boolean store, boolean backup, boolean term) {
273 + if (store) {
274 + dms.masters.clear();
275 + dms.unusable.clear();
276 + }
277 + if (backup) {
278 + dms.standbys.clear();
279 + }
280 + if (term) {
281 + dms.terms.clear();
282 + }
283 + }
284 +
285 + //increment term for a device
286 + public void increment(DeviceId dev) {
287 + Integer t = dms.terms.get(serialize(dev));
288 + if (t != null) {
289 + dms.terms.put(serialize(dev), ++t);
290 + }
291 + }
292 +
293 + //sets the "local" node
294 + public void setCurrent(ControllerNode node) {
295 + ((TestClusterService) clusterService).current = node;
296 + }
297 + }
298 +
299 + private class TestClusterService implements ClusterService {
300 +
301 + protected ControllerNode current;
302 +
303 + @Override
304 + public ControllerNode getLocalNode() {
305 + return current;
306 + }
307 +
308 + @Override
309 + public Set<ControllerNode> getNodes() {
310 + return Sets.newHashSet(CN1, CN2);
311 + }
312 +
313 + @Override
314 + public ControllerNode getNode(NodeId nodeId) {
315 + return null;
316 + }
317 +
318 + @Override
319 + public State getState(NodeId nodeId) {
320 + return null;
321 + }
322 +
323 + @Override
324 + public void addListener(ClusterEventListener listener) {
325 + }
326 +
327 + @Override
328 + public void removeListener(ClusterEventListener listener) {
329 + }
330 +
331 + }
332 +
333 +}
...@@ -16,7 +16,7 @@ import org.onlab.onos.event.Event; ...@@ -16,7 +16,7 @@ import org.onlab.onos.event.Event;
16 import org.onlab.onos.store.AbstractStore; 16 import org.onlab.onos.store.AbstractStore;
17 import org.onlab.onos.store.StoreDelegate; 17 import org.onlab.onos.store.StoreDelegate;
18 import org.onlab.onos.store.serializers.KryoSerializer; 18 import org.onlab.onos.store.serializers.KryoSerializer;
19 -import org.onlab.onos.store.serializers.Serializer; 19 +import org.onlab.onos.store.serializers.StoreSerializer;
20 import org.slf4j.Logger; 20 import org.slf4j.Logger;
21 21
22 import static com.google.common.base.Preconditions.checkNotNull; 22 import static com.google.common.base.Preconditions.checkNotNull;
...@@ -34,7 +34,7 @@ public abstract class AbstractHazelcastStore<E extends Event, D extends StoreDel ...@@ -34,7 +34,7 @@ public abstract class AbstractHazelcastStore<E extends Event, D extends StoreDel
34 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) 34 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
35 protected StoreService storeService; 35 protected StoreService storeService;
36 36
37 - protected Serializer serializer; 37 + protected StoreSerializer serializer;
38 38
39 protected HazelcastInstance theInstance; 39 protected HazelcastInstance theInstance;
40 40
......
...@@ -2,7 +2,7 @@ package org.onlab.onos.store.common; ...@@ -2,7 +2,7 @@ package org.onlab.onos.store.common;
2 2
3 import static com.google.common.base.Preconditions.checkNotNull; 3 import static com.google.common.base.Preconditions.checkNotNull;
4 4
5 -import org.onlab.onos.store.serializers.Serializer; 5 +import org.onlab.onos.store.serializers.StoreSerializer;
6 6
7 import com.google.common.base.Optional; 7 import com.google.common.base.Optional;
8 import com.google.common.cache.CacheLoader; 8 import com.google.common.cache.CacheLoader;
...@@ -18,7 +18,7 @@ import com.hazelcast.core.IMap; ...@@ -18,7 +18,7 @@ import com.hazelcast.core.IMap;
18 public final class OptionalCacheLoader<K, V> extends 18 public final class OptionalCacheLoader<K, V> extends
19 CacheLoader<K, Optional<V>> { 19 CacheLoader<K, Optional<V>> {
20 20
21 - private final Serializer serializer; 21 + private final StoreSerializer serializer;
22 private IMap<byte[], byte[]> rawMap; 22 private IMap<byte[], byte[]> rawMap;
23 23
24 /** 24 /**
...@@ -27,7 +27,7 @@ public final class OptionalCacheLoader<K, V> extends ...@@ -27,7 +27,7 @@ public final class OptionalCacheLoader<K, V> extends
27 * @param serializer to use for serialization 27 * @param serializer to use for serialization
28 * @param rawMap underlying IMap 28 * @param rawMap underlying IMap
29 */ 29 */
30 - public OptionalCacheLoader(Serializer serializer, IMap<byte[], byte[]> rawMap) { 30 + public OptionalCacheLoader(StoreSerializer serializer, IMap<byte[], byte[]> rawMap) {
31 this.serializer = checkNotNull(serializer); 31 this.serializer = checkNotNull(serializer);
32 this.rawMap = checkNotNull(rawMap); 32 this.rawMap = checkNotNull(rawMap);
33 } 33 }
......
...@@ -7,9 +7,9 @@ import org.slf4j.LoggerFactory; ...@@ -7,9 +7,9 @@ import org.slf4j.LoggerFactory;
7 import java.nio.ByteBuffer; 7 import java.nio.ByteBuffer;
8 8
9 /** 9 /**
10 - * Serializer implementation using Kryo. 10 + * StoreSerializer implementation using Kryo.
11 */ 11 */
12 -public class KryoSerializer implements Serializer { 12 +public class KryoSerializer implements StoreSerializer {
13 13
14 private final Logger log = LoggerFactory.getLogger(getClass()); 14 private final Logger log = LoggerFactory.getLogger(getClass());
15 protected KryoPool serializerPool; 15 protected KryoPool serializerPool;
......
...@@ -6,7 +6,7 @@ import java.nio.ByteBuffer; ...@@ -6,7 +6,7 @@ import java.nio.ByteBuffer;
6 /** 6 /**
7 * Service to serialize Objects into byte array. 7 * Service to serialize Objects into byte array.
8 */ 8 */
9 -public interface Serializer { 9 +public interface StoreSerializer {
10 10
11 /** 11 /**
12 * Serializes the specified object into bytes. 12 * Serializes the specified object into bytes.
......
...@@ -174,7 +174,7 @@ public class SimpleMastershipStore ...@@ -174,7 +174,7 @@ public class SimpleMastershipStore
174 } 174 }
175 175
176 @Override 176 @Override
177 - public MastershipEvent unsetMaster(NodeId nodeId, DeviceId deviceId) { 177 + public MastershipEvent setStandby(NodeId nodeId, DeviceId deviceId) {
178 MastershipRole role = getRole(nodeId, deviceId); 178 MastershipRole role = getRole(nodeId, deviceId);
179 synchronized (this) { 179 synchronized (this) {
180 switch (role) { 180 switch (role) {
...@@ -214,4 +214,9 @@ public class SimpleMastershipStore ...@@ -214,4 +214,9 @@ public class SimpleMastershipStore
214 return backup; 214 return backup;
215 } 215 }
216 216
217 + @Override
218 + public MastershipEvent relinquishRole(NodeId nodeId, DeviceId deviceId) {
219 + return setStandby(nodeId, deviceId);
220 + }
221 +
217 } 222 }
......
...@@ -129,22 +129,22 @@ public class SimpleMastershipStoreTest { ...@@ -129,22 +129,22 @@ public class SimpleMastershipStoreTest {
129 public void unsetMaster() { 129 public void unsetMaster() {
130 //NONE - record backup but take no other action 130 //NONE - record backup but take no other action
131 put(DID1, N1, false, false); 131 put(DID1, N1, false, false);
132 - sms.unsetMaster(N1, DID1); 132 + sms.setStandby(N1, DID1);
133 assertTrue("not backed up", sms.backups.contains(N1)); 133 assertTrue("not backed up", sms.backups.contains(N1));
134 sms.termMap.clear(); 134 sms.termMap.clear();
135 - sms.unsetMaster(N1, DID1); 135 + sms.setStandby(N1, DID1);
136 assertTrue("term not set", sms.termMap.containsKey(DID1)); 136 assertTrue("term not set", sms.termMap.containsKey(DID1));
137 137
138 //no backup, MASTER 138 //no backup, MASTER
139 put(DID1, N1, true, true); 139 put(DID1, N1, true, true);
140 - assertNull("wrong event", sms.unsetMaster(N1, DID1)); 140 + assertNull("wrong event", sms.setStandby(N1, DID1));
141 assertNull("wrong node", sms.masterMap.get(DID1)); 141 assertNull("wrong node", sms.masterMap.get(DID1));
142 142
143 //backup, switch 143 //backup, switch
144 sms.masterMap.clear(); 144 sms.masterMap.clear();
145 put(DID1, N1, true, true); 145 put(DID1, N1, true, true);
146 put(DID2, N2, true, true); 146 put(DID2, N2, true, true);
147 - assertEquals("wrong event", MASTER_CHANGED, sms.unsetMaster(N1, DID1).type()); 147 + assertEquals("wrong event", MASTER_CHANGED, sms.setStandby(N1, DID1).type());
148 } 148 }
149 149
150 //helper to populate master/backup structures 150 //helper to populate master/backup structures
......
...@@ -153,6 +153,7 @@ ...@@ -153,6 +153,7 @@
153 description="ONOS sample playground application"> 153 description="ONOS sample playground application">
154 <feature>onos-api</feature> 154 <feature>onos-api</feature>
155 <bundle>mvn:org.onlab.onos/onos-app-foo/1.0.0-SNAPSHOT</bundle> 155 <bundle>mvn:org.onlab.onos/onos-app-foo/1.0.0-SNAPSHOT</bundle>
156 + <bundle>mvn:org.onlab.onos/onlab-netty/1.0.0-SNAPSHOT</bundle>
156 </feature> 157 </feature>
157 158
158 <feature name="onos-app-config" version="1.0.0" 159 <feature name="onos-app-config" version="1.0.0"
......
1 -<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
2 -<!--
3 - ~ Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
4 - ~
5 - ~ This program and the accompanying materials are made available under the
6 - ~ terms of the Eclipse Public License v1.0 which accompanies this distribution,
7 - ~ and is available at http://www.eclipse.org/legal/epl-v10.html
8 - -->
9 -
10 -<features xmlns="http://karaf.apache.org/xmlns/features/v1.2.0"
11 - name="net.onrc.onos-1.0.0">
12 - <repository>mvn:net.onrc.onos/onos-features/1.0.0-SNAPSHOT/xml/features</repository>
13 -
14 - <feature name="thirdparty" version="1.0.0"
15 - description="ONOS 3rd party dependencies">
16 - <bundle>mvn:com.google.code.findbugs/annotations/2.0.2</bundle>
17 - <bundle>mvn:io.netty/netty/3.9.2.Final</bundle>
18 - <bundle>mvn:com.google.guava/guava/17.0</bundle>
19 - <bundle>mvn:com.google.guava/guava/15.0</bundle>
20 -
21 - </feature>
22 -
23 - <feature name="base" version="1.0.0"
24 - description="ONOS Base">
25 - <feature>scr</feature>
26 - <feature>thirdparty</feature>
27 - <bundle>mvn:net.onrc.onos.sb/onos-sb/0.0.1</bundle>
28 - <bundle>mvn:org.projectfloodlight/openflowj/0.3.6-SNAPSHOT</bundle>
29 - </feature>
30 -
31 -</features>
...@@ -17,14 +17,11 @@ ...@@ -17,14 +17,11 @@
17 <description>ONOS OpenFlow controller subsystem API</description> 17 <description>ONOS OpenFlow controller subsystem API</description>
18 18
19 <repositories> 19 <repositories>
20 - <!-- FIXME: for Loxigen. Decide how to use Loxigen before release. --> 20 + <!-- FIXME: for Loxigen + optical experimenter. Decide how to use Loxigen before release. -->
21 <repository> 21 <repository>
22 - <id>sonatype-oss-snapshot</id> 22 + <id>onlab-temp</id>
23 - <name>Sonatype OSS snapshot repository</name> 23 + <name>ON.lab temporary repository</name>
24 - <url>https://oss.sonatype.org/content/repositories/snapshots</url> 24 + <url>http://mavenrepo.onlab.us:8081/nexus/content/repositories/releases</url>
25 - <releases>
26 - <enabled>false</enabled>
27 - </releases>
28 </repository> 25 </repository>
29 </repositories> 26 </repositories>
30 27
...@@ -32,7 +29,8 @@ ...@@ -32,7 +29,8 @@
32 <dependency> 29 <dependency>
33 <groupId>org.projectfloodlight</groupId> 30 <groupId>org.projectfloodlight</groupId>
34 <artifactId>openflowj</artifactId> 31 <artifactId>openflowj</artifactId>
35 - <version>0.3.8-SNAPSHOT</version> 32 + <!-- FIXME once experimenter gets merged to upstream -->
33 + <version>0.3.8-optical_experimenter</version>
36 </dependency> 34 </dependency>
37 <dependency> 35 <dependency>
38 <groupId>io.netty</groupId> 36 <groupId>io.netty</groupId>
......
...@@ -981,11 +981,13 @@ class OFChannelHandler extends IdleStateAwareChannelHandler { ...@@ -981,11 +981,13 @@ class OFChannelHandler extends IdleStateAwareChannelHandler {
981 // switch was a duplicate-dpid, calling the method below would clear 981 // switch was a duplicate-dpid, calling the method below would clear
982 // all state for the original switch (with the same dpid), 982 // all state for the original switch (with the same dpid),
983 // which we obviously don't want. 983 // which we obviously don't want.
984 + log.info("{}:removal called");
984 sw.removeConnectedSwitch(); 985 sw.removeConnectedSwitch();
985 } else { 986 } else {
986 // A duplicate was disconnected on this ChannelHandler, 987 // A duplicate was disconnected on this ChannelHandler,
987 // this is the same switch reconnecting, but the original state was 988 // this is the same switch reconnecting, but the original state was
988 // not cleaned up - XXX check liveness of original ChannelHandler 989 // not cleaned up - XXX check liveness of original ChannelHandler
990 + log.info("{}:duplicate found");
989 duplicateDpidFound = Boolean.FALSE; 991 duplicateDpidFound = Boolean.FALSE;
990 } 992 }
991 } else { 993 } else {
......
...@@ -307,9 +307,11 @@ public class OpenFlowControllerImpl implements OpenFlowController { ...@@ -307,9 +307,11 @@ public class OpenFlowControllerImpl implements OpenFlowController {
307 connectedSwitches.remove(dpid); 307 connectedSwitches.remove(dpid);
308 OpenFlowSwitch sw = activeMasterSwitches.remove(dpid); 308 OpenFlowSwitch sw = activeMasterSwitches.remove(dpid);
309 if (sw == null) { 309 if (sw == null) {
310 + log.warn("sw was null for {}", dpid);
310 sw = activeEqualSwitches.remove(dpid); 311 sw = activeEqualSwitches.remove(dpid);
311 } 312 }
312 for (OpenFlowSwitchListener l : ofSwitchListener) { 313 for (OpenFlowSwitchListener l : ofSwitchListener) {
314 + log.warn("removal for {}", dpid);
313 l.switchRemoved(dpid); 315 l.switchRemoved(dpid);
314 } 316 }
315 } 317 }
......
...@@ -9,10 +9,14 @@ export KARAF_ZIP=${KARAF_ZIP:-~/Downloads/apache-karaf-3.0.1.zip} ...@@ -9,10 +9,14 @@ export KARAF_ZIP=${KARAF_ZIP:-~/Downloads/apache-karaf-3.0.1.zip}
9 export KARAF_TAR=${KARAF_TAR:-~/Downloads/apache-karaf-3.0.1.tar.gz} 9 export KARAF_TAR=${KARAF_TAR:-~/Downloads/apache-karaf-3.0.1.tar.gz}
10 export KARAF_DIST=$(basename $KARAF_ZIP .zip) 10 export KARAF_DIST=$(basename $KARAF_ZIP .zip)
11 11
12 +# Fallback build number us derived from from the user name & time
13 +export BUILD_NUMBER=${BUILD_NUMBER:-$(id -un)~$(date +'%Y/%m/%d@%H:%M')}
14 +
12 # ONOS Version and onos.tar.gz staging environment 15 # ONOS Version and onos.tar.gz staging environment
13 -export ONOS_VERSION=${ONOS_VERSION:-1.0.0-SNAPSHOT} 16 +export ONOS_POM_VERSION="1.0.0-SNAPSHOT"
17 +export ONOS_VERSION=${ONOS_VERSION:-1.0.0.$BUILD_NUMBER}
18 +export ONOS_BITS=onos-${ONOS_VERSION%~*}
14 export ONOS_STAGE_ROOT=${ONOS_STAGE_ROOT:-/tmp} 19 export ONOS_STAGE_ROOT=${ONOS_STAGE_ROOT:-/tmp}
15 -export ONOS_BITS=onos-$ONOS_VERSION
16 export ONOS_STAGE=$ONOS_STAGE_ROOT/$ONOS_BITS 20 export ONOS_STAGE=$ONOS_STAGE_ROOT/$ONOS_BITS
17 export ONOS_TAR=$ONOS_STAGE.tar.gz 21 export ONOS_TAR=$ONOS_STAGE.tar.gz
18 22
......
...@@ -49,7 +49,7 @@ export ONOS_FEATURES="${ONOS_FEATURES:-webconsole,onos-api,onos-core,onos-cli,on ...@@ -49,7 +49,7 @@ export ONOS_FEATURES="${ONOS_FEATURES:-webconsole,onos-api,onos-core,onos-cli,on
49 # ONOS Patching ---------------------------------------------------------------- 49 # ONOS Patching ----------------------------------------------------------------
50 50
51 # Patch the Apache Karaf distribution file to add ONOS features repository 51 # Patch the Apache Karaf distribution file to add ONOS features repository
52 -perl -pi.old -e "s|^(featuresRepositories=.*)|\1,mvn:org.onlab.onos/onos-features/$ONOS_VERSION/xml/features|" \ 52 +perl -pi.old -e "s|^(featuresRepositories=.*)|\1,mvn:org.onlab.onos/onos-features/$ONOS_POM_VERSION/xml/features|" \
53 $ONOS_STAGE/$KARAF_DIST/etc/org.apache.karaf.features.cfg 53 $ONOS_STAGE/$KARAF_DIST/etc/org.apache.karaf.features.cfg
54 54
55 # Patch the Apache Karaf distribution file to load ONOS features 55 # Patch the Apache Karaf distribution file to load ONOS features
...@@ -57,10 +57,14 @@ perl -pi.old -e "s|^(featuresBoot=.*)|\1,$ONOS_FEATURES|" \ ...@@ -57,10 +57,14 @@ perl -pi.old -e "s|^(featuresBoot=.*)|\1,$ONOS_FEATURES|" \
57 $ONOS_STAGE/$KARAF_DIST/etc/org.apache.karaf.features.cfg 57 $ONOS_STAGE/$KARAF_DIST/etc/org.apache.karaf.features.cfg
58 58
59 # Patch the Apache Karaf distribution with ONOS branding bundle 59 # Patch the Apache Karaf distribution with ONOS branding bundle
60 -cp $M2_REPO/org/onlab/onos/onos-branding/$ONOS_VERSION/onos-branding-*.jar \ 60 +cp $M2_REPO/org/onlab/onos/onos-branding/$ONOS_POM_VERSION/onos-branding-*.jar \
61 $ONOS_STAGE/$KARAF_DIST/lib 61 $ONOS_STAGE/$KARAF_DIST/lib
62 62
63 +# Patch in the ONOS version file
64 +echo $ONOS_VERSION > $ONOS_STAGE/VERSION
65 +
63 # Now package up the ONOS tar file 66 # Now package up the ONOS tar file
64 cd $ONOS_STAGE_ROOT 67 cd $ONOS_STAGE_ROOT
65 COPYFILE_DISABLE=1 tar zcf $ONOS_TAR $ONOS_BITS 68 COPYFILE_DISABLE=1 tar zcf $ONOS_TAR $ONOS_BITS
66 ls -l $ONOS_TAR >&2 69 ls -l $ONOS_TAR >&2
70 +rm -r $ONOS_STAGE
......
...@@ -33,6 +33,7 @@ alias obs='onos-build-selective' ...@@ -33,6 +33,7 @@ alias obs='onos-build-selective'
33 alias op='onos-package' 33 alias op='onos-package'
34 alias ot='onos-test' 34 alias ot='onos-test'
35 alias ol='onos-log' 35 alias ol='onos-log'
36 +alias go='ob && ot && onos -w'
36 alias pub='onos-push-update-bundle' 37 alias pub='onos-push-update-bundle'
37 38
38 # Short-hand for tailing the ONOS (karaf) log 39 # Short-hand for tailing the ONOS (karaf) log
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
4 #------------------------------------------------------------------------------- 4 #-------------------------------------------------------------------------------
5 5
6 export JAVA_HOME=${JAVA_HOME:-/usr/lib/jvm/java-7-openjdk-amd64/} 6 export JAVA_HOME=${JAVA_HOME:-/usr/lib/jvm/java-7-openjdk-amd64/}
7 +export JAVA_OPTS="-Xms256M -Xmx2048M"
7 8
8 cd /opt/onos 9 cd /opt/onos
9 /opt/onos/apache-karaf-3.0.1/bin/karaf "$@" 10 /opt/onos/apache-karaf-3.0.1/bin/karaf "$@"
......
...@@ -15,7 +15,7 @@ bundle=$(echo $(basename $jar .jar) | sed 's/-[0-9].*//g') ...@@ -15,7 +15,7 @@ bundle=$(echo $(basename $jar .jar) | sed 's/-[0-9].*//g')
15 15
16 nodes=$(env | sort | egrep "OC[0-9]+" | cut -d= -f2) 16 nodes=$(env | sort | egrep "OC[0-9]+" | cut -d= -f2)
17 for node in $nodes; do 17 for node in $nodes; do
18 + scp -q $jar $ONOS_USER@$node:.m2/repository/$jar
18 scp -q $jar $ONOS_USER@$node:$ONOS_INSTALL_DIR/$KARAF_DIST/system/$jar 19 scp -q $jar $ONOS_USER@$node:$ONOS_INSTALL_DIR/$KARAF_DIST/system/$jar
19 - ssh $ONOS_USER@$node "ls -l $ONOS_INSTALL_DIR/$KARAF_DIST/system/$jar"
20 ssh $ONOS_USER@$node "$ONOS_INSTALL_DIR/bin/onos \"bundle:update -f $bundle\"" 2>/dev/null 20 ssh $ONOS_USER@$node "$ONOS_INSTALL_DIR/bin/onos \"bundle:update -f $bundle\"" 2>/dev/null
21 done 21 done
......
1 +#!/usr/bin/python
2 +# Launches mininet with Tower topology configuration.
3 +import sys, tower
4 +net = tower.Tower(cip=sys.argv[1])
5 +net.run()
1 package org.onlab.metrics; 1 package org.onlab.metrics;
2 2
3 -import java.io.File;
4 -import java.util.Locale;
5 import java.util.Map; 3 import java.util.Map;
6 import java.util.concurrent.ConcurrentHashMap; 4 import java.util.concurrent.ConcurrentHashMap;
7 import java.util.concurrent.ConcurrentMap; 5 import java.util.concurrent.ConcurrentMap;
...@@ -10,9 +8,11 @@ import java.util.concurrent.TimeUnit; ...@@ -10,9 +8,11 @@ import java.util.concurrent.TimeUnit;
10 import org.apache.felix.scr.annotations.Activate; 8 import org.apache.felix.scr.annotations.Activate;
11 import org.apache.felix.scr.annotations.Component; 9 import org.apache.felix.scr.annotations.Component;
12 import org.apache.felix.scr.annotations.Deactivate; 10 import org.apache.felix.scr.annotations.Deactivate;
11 +import org.slf4j.Logger;
12 +import org.slf4j.LoggerFactory;
13 13
14 +import com.codahale.metrics.ConsoleReporter;
14 import com.codahale.metrics.Counter; 15 import com.codahale.metrics.Counter;
15 -import com.codahale.metrics.CsvReporter;
16 import com.codahale.metrics.Gauge; 16 import com.codahale.metrics.Gauge;
17 import com.codahale.metrics.Histogram; 17 import com.codahale.metrics.Histogram;
18 import com.codahale.metrics.Meter; 18 import com.codahale.metrics.Meter;
...@@ -56,6 +56,7 @@ import com.codahale.metrics.Timer; ...@@ -56,6 +56,7 @@ import com.codahale.metrics.Timer;
56 @Component(immediate = true) 56 @Component(immediate = true)
57 public final class MetricsManager implements MetricsService { 57 public final class MetricsManager implements MetricsService {
58 58
59 + private final Logger log = LoggerFactory.getLogger(getClass());
59 /** 60 /**
60 * Registry to hold the Components defined in the system. 61 * Registry to hold the Components defined in the system.
61 */ 62 */
...@@ -69,15 +70,20 @@ public final class MetricsManager implements MetricsService { ...@@ -69,15 +70,20 @@ public final class MetricsManager implements MetricsService {
69 /** 70 /**
70 * Default Reporter for this metrics manager. 71 * Default Reporter for this metrics manager.
71 */ 72 */
72 - private final CsvReporter reporter; 73 + //private final Slf4jReporter reporter;
74 + private final ConsoleReporter reporter;
73 75
74 public MetricsManager() { 76 public MetricsManager() {
75 this.metricsRegistry = new MetricRegistry(); 77 this.metricsRegistry = new MetricRegistry();
76 - this.reporter = CsvReporter.forRegistry(metricsRegistry) 78 +// this.reporter = Slf4jReporter.forRegistry(this.metricsRegistry)
77 - .formatFor(Locale.US) 79 +// .outputTo(log)
80 +// .convertRatesTo(TimeUnit.SECONDS)
81 +// .convertDurationsTo(TimeUnit.MICROSECONDS)
82 +// .build();
83 + this.reporter = ConsoleReporter.forRegistry(this.metricsRegistry)
78 .convertRatesTo(TimeUnit.SECONDS) 84 .convertRatesTo(TimeUnit.SECONDS)
79 .convertDurationsTo(TimeUnit.MICROSECONDS) 85 .convertDurationsTo(TimeUnit.MICROSECONDS)
80 - .build(new File("/var/onos/log/metrics/")); 86 + .build();
81 } 87 }
82 88
83 @Activate 89 @Activate
......
...@@ -4,6 +4,12 @@ import com.google.common.base.Strings; ...@@ -4,6 +4,12 @@ import com.google.common.base.Strings;
4 import com.google.common.primitives.UnsignedLongs; 4 import com.google.common.primitives.UnsignedLongs;
5 import com.google.common.util.concurrent.ThreadFactoryBuilder; 5 import com.google.common.util.concurrent.ThreadFactoryBuilder;
6 6
7 +import java.io.BufferedReader;
8 +import java.io.File;
9 +import java.io.FileReader;
10 +import java.io.IOException;
11 +import java.util.ArrayList;
12 +import java.util.List;
7 import java.util.concurrent.ThreadFactory; 13 import java.util.concurrent.ThreadFactory;
8 14
9 public abstract class Tools { 15 public abstract class Tools {
...@@ -66,4 +72,24 @@ public abstract class Tools { ...@@ -66,4 +72,24 @@ public abstract class Tools {
66 } 72 }
67 } 73 }
68 74
75 + /**
76 + * Slurps the contents of a file into a list of strings, one per line.
77 + *
78 + * @param path file path
79 + * @return file contents
80 + */
81 + public static List<String> slurp(File path) {
82 + try (BufferedReader br = new BufferedReader(new FileReader(path))) {
83 + List<String> lines = new ArrayList<>();
84 + String line;
85 + while ((line = br.readLine()) != null) {
86 + lines.add(line);
87 + }
88 + return lines;
89 +
90 + } catch (IOException e) {
91 + return null;
92 + }
93 + }
94 +
69 } 95 }
......
...@@ -8,11 +8,16 @@ import io.netty.handler.codec.ReplayingDecoder; ...@@ -8,11 +8,16 @@ import io.netty.handler.codec.ReplayingDecoder;
8 import java.util.Arrays; 8 import java.util.Arrays;
9 import java.util.List; 9 import java.util.List;
10 10
11 +import org.slf4j.Logger;
12 +import org.slf4j.LoggerFactory;
13 +
11 /** 14 /**
12 * Decoder for inbound messages. 15 * Decoder for inbound messages.
13 */ 16 */
14 public class MessageDecoder extends ReplayingDecoder<DecoderState> { 17 public class MessageDecoder extends ReplayingDecoder<DecoderState> {
15 18
19 + private final Logger log = LoggerFactory.getLogger(getClass());
20 +
16 private final NettyMessagingService messagingService; 21 private final NettyMessagingService messagingService;
17 22
18 private static final KryoSerializer SERIALIZER = new KryoSerializer(); 23 private static final KryoSerializer SERIALIZER = new KryoSerializer();
...@@ -57,4 +62,10 @@ public class MessageDecoder extends ReplayingDecoder<DecoderState> { ...@@ -57,4 +62,10 @@ public class MessageDecoder extends ReplayingDecoder<DecoderState> {
57 checkState(false, "Must not be here"); 62 checkState(false, "Must not be here");
58 } 63 }
59 } 64 }
65 +
66 + @Override
67 + public void exceptionCaught(ChannelHandlerContext context, Throwable cause) {
68 + log.error("Exception inside channel handling pipeline.", cause);
69 + context.close();
70 + }
60 } 71 }
......
1 package org.onlab.netty; 1 package org.onlab.netty;
2 2
3 +import org.slf4j.Logger;
4 +import org.slf4j.LoggerFactory;
5 +
3 import io.netty.buffer.ByteBuf; 6 import io.netty.buffer.ByteBuf;
4 import io.netty.channel.ChannelHandler.Sharable; 7 import io.netty.channel.ChannelHandler.Sharable;
5 import io.netty.channel.ChannelHandlerContext; 8 import io.netty.channel.ChannelHandlerContext;
...@@ -11,6 +14,8 @@ import io.netty.handler.codec.MessageToByteEncoder; ...@@ -11,6 +14,8 @@ import io.netty.handler.codec.MessageToByteEncoder;
11 @Sharable 14 @Sharable
12 public class MessageEncoder extends MessageToByteEncoder<InternalMessage> { 15 public class MessageEncoder extends MessageToByteEncoder<InternalMessage> {
13 16
17 + private final Logger log = LoggerFactory.getLogger(getClass());
18 +
14 // onosiscool in ascii 19 // onosiscool in ascii
15 public static final byte[] PREAMBLE = "onosiscool".getBytes(); 20 public static final byte[] PREAMBLE = "onosiscool".getBytes();
16 public static final int HEADER_VERSION = 1; 21 public static final int HEADER_VERSION = 1;
...@@ -31,11 +36,6 @@ public class MessageEncoder extends MessageToByteEncoder<InternalMessage> { ...@@ -31,11 +36,6 @@ public class MessageEncoder extends MessageToByteEncoder<InternalMessage> {
31 // write preamble 36 // write preamble
32 out.writeBytes(PREAMBLE); 37 out.writeBytes(PREAMBLE);
33 38
34 - try {
35 - SERIALIZER.encode(message);
36 - } catch (Exception e) {
37 - e.printStackTrace();
38 - }
39 byte[] payload = SERIALIZER.encode(message); 39 byte[] payload = SERIALIZER.encode(message);
40 40
41 // write payload length 41 // write payload length
...@@ -47,4 +47,10 @@ public class MessageEncoder extends MessageToByteEncoder<InternalMessage> { ...@@ -47,4 +47,10 @@ public class MessageEncoder extends MessageToByteEncoder<InternalMessage> {
47 // write payload. 47 // write payload.
48 out.writeBytes(payload); 48 out.writeBytes(payload);
49 } 49 }
50 +
51 + @Override
52 + public void exceptionCaught(ChannelHandlerContext context, Throwable cause) {
53 + log.error("Exception inside channel handling pipeline.", cause);
54 + context.close();
55 + }
50 } 56 }
......
...@@ -248,6 +248,7 @@ public class NettyMessagingService implements MessagingService { ...@@ -248,6 +248,7 @@ public class NettyMessagingService implements MessagingService {
248 248
249 @Override 249 @Override
250 public void exceptionCaught(ChannelHandlerContext context, Throwable cause) { 250 public void exceptionCaught(ChannelHandlerContext context, Throwable cause) {
251 + log.error("Exception inside channel handling pipeline.", cause);
251 context.close(); 252 context.close();
252 } 253 }
253 } 254 }
......
1 -package org.onlab.netty;
2 -
3 -import java.util.concurrent.TimeUnit;
4 -
5 -import org.onlab.metrics.MetricsComponent;
6 -import org.onlab.metrics.MetricsFeature;
7 -import org.onlab.metrics.MetricsManager;
8 -
9 -import com.codahale.metrics.Timer;
10 -
11 -// FIXME: Should be move out to test or app
12 -public final class SimpleClient {
13 - private SimpleClient() {
14 - }
15 -
16 - public static void main(String... args) throws Exception {
17 - NettyMessagingService messaging = new TestNettyMessagingService(9081);
18 - MetricsManager metrics = new MetricsManager();
19 - messaging.activate();
20 - metrics.activate();
21 - MetricsFeature feature = new MetricsFeature("timers");
22 - MetricsComponent component = metrics.registerComponent("NettyMessaging");
23 - Timer sendAsyncTimer = metrics.createTimer(component, feature, "AsyncSender");
24 - final int warmup = 100;
25 - for (int i = 0; i < warmup; i++) {
26 - Timer.Context context = sendAsyncTimer.time();
27 - messaging.sendAsync(new Endpoint("localhost", 8080), "simple", "Hello World".getBytes());
28 - context.stop();
29 - }
30 - metrics.registerMetric(component, feature, "AsyncTimer", sendAsyncTimer);
31 -
32 - Timer sendAndReceiveTimer = metrics.createTimer(component, feature, "SendAndReceive");
33 - final int iterations = 1000000;
34 - for (int i = 0; i < iterations; i++) {
35 - Timer.Context context = sendAndReceiveTimer.time();
36 - Response response = messaging
37 - .sendAndReceive(new Endpoint("localhost", 8080), "echo",
38 - "Hello World".getBytes());
39 - System.out.println("Got back:" + new String(response.get(2, TimeUnit.SECONDS)));
40 - context.stop();
41 - }
42 - metrics.registerMetric(component, feature, "AsyncTimer", sendAndReceiveTimer);
43 - }
44 -
45 - public static class TestNettyMessagingService extends NettyMessagingService {
46 - public TestNettyMessagingService(int port) throws Exception {
47 - super(port);
48 - }
49 - }
50 -}
1 -package org.onlab.netty;
2 -
3 -//FIXME: Should be move out to test or app
4 -public final class SimpleServer {
5 - private SimpleServer() {}
6 -
7 - public static void main(String... args) throws Exception {
8 - NettyMessagingService server = new NettyMessagingService(8080);
9 - server.activate();
10 - server.registerHandler("simple", new LoggingHandler());
11 - server.registerHandler("echo", new EchoHandler());
12 - }
13 -}