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 | +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 | } | ... | ... |
core/net/src/test/java/org/onlab/onos/net/device/impl/DistributedDeviceManagerTest.java
deleted
100644 → 0
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) { | ... | ... |
core/store/dist/src/main/java/org/onlab/onos/store/device/impl/GossipDeviceStoreMessageSubjects.java
... | @@ -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 | } | ... | ... |
core/store/dist/src/main/java/org/onlab/onos/store/device/impl/InternalDeviceEventSerializer.java
0 → 100644
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 | +} |
core/store/dist/src/main/java/org/onlab/onos/store/device/impl/InternalDeviceOfflineEvent.java
0 → 100644
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 | +} |
core/store/dist/src/main/java/org/onlab/onos/store/device/impl/InternalDeviceRemovedEvent.java
0 → 100644
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 | } | ... | ... |
core/store/dist/src/main/java/org/onlab/onos/store/device/impl/InternalPortEventSerializer.java
0 → 100644
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 | ... | ... |
core/store/dist/src/main/java/org/onlab/onos/store/serializers/MessageSubjectSerializer.java
0 → 100644
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" | ... | ... |
features/old-features.xml
deleted
100644 → 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 | ... | ... |
tools/test/topos/tt.py
0 → 100644
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 | -} |
-
Please register or login to post a comment