Ayaka Koshibe

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

Conflicts:
	core/net/src/main/java/org/onlab/onos/net/device/impl/DeviceManager.java
	core/store/hz/cluster/src/main/java/org/onlab/onos/store/cluster/impl/DistributedMastershipStore.java

Change-Id: Ia1274657b27e01366a4a87196a13068d7104ee80
Showing 265 changed files with 8815 additions and 2389 deletions
...@@ -24,10 +24,20 @@ ...@@ -24,10 +24,20 @@
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>
30 <dependency> 35 <dependency>
36 + <groupId>org.onlab.onos</groupId>
37 + <artifactId>onlab-netty</artifactId>
38 + <version>${project.version}</version>
39 + </dependency>
40 + <dependency>
31 <groupId>org.apache.karaf.shell</groupId> 41 <groupId>org.apache.karaf.shell</groupId>
32 <artifactId>org.apache.karaf.shell.console</artifactId> 42 <artifactId>org.apache.karaf.shell.console</artifactId>
33 </dependency> 43 </dependency>
......
...@@ -11,6 +11,9 @@ import org.onlab.onos.cluster.ClusterService; ...@@ -11,6 +11,9 @@ import org.onlab.onos.cluster.ClusterService;
11 import org.onlab.onos.net.device.DeviceEvent; 11 import org.onlab.onos.net.device.DeviceEvent;
12 import org.onlab.onos.net.device.DeviceListener; 12 import org.onlab.onos.net.device.DeviceListener;
13 import org.onlab.onos.net.device.DeviceService; 13 import org.onlab.onos.net.device.DeviceService;
14 +import org.onlab.onos.net.intent.IntentEvent;
15 +import org.onlab.onos.net.intent.IntentListener;
16 +import org.onlab.onos.net.intent.IntentService;
14 import org.slf4j.Logger; 17 import org.slf4j.Logger;
15 18
16 import static org.slf4j.LoggerFactory.getLogger; 19 import static org.slf4j.LoggerFactory.getLogger;
...@@ -29,13 +32,18 @@ public class FooComponent { ...@@ -29,13 +32,18 @@ public class FooComponent {
29 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) 32 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
30 protected DeviceService deviceService; 33 protected DeviceService deviceService;
31 34
35 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
36 + protected IntentService intentService;
37 +
32 private final ClusterEventListener clusterListener = new InnerClusterListener(); 38 private final ClusterEventListener clusterListener = new InnerClusterListener();
33 private final DeviceListener deviceListener = new InnerDeviceListener(); 39 private final DeviceListener deviceListener = new InnerDeviceListener();
40 + private final IntentListener intentListener = new InnerIntentListener();
34 41
35 @Activate 42 @Activate
36 public void activate() { 43 public void activate() {
37 clusterService.addListener(clusterListener); 44 clusterService.addListener(clusterListener);
38 deviceService.addListener(deviceListener); 45 deviceService.addListener(deviceListener);
46 + intentService.addListener(intentListener);
39 log.info("Started"); 47 log.info("Started");
40 } 48 }
41 49
...@@ -43,6 +51,7 @@ public class FooComponent { ...@@ -43,6 +51,7 @@ public class FooComponent {
43 public void deactivate() { 51 public void deactivate() {
44 clusterService.removeListener(clusterListener); 52 clusterService.removeListener(clusterListener);
45 deviceService.removeListener(deviceListener); 53 deviceService.removeListener(deviceListener);
54 + intentService.removeListener(intentListener);
46 log.info("Stopped"); 55 log.info("Stopped");
47 } 56 }
48 57
...@@ -59,6 +68,23 @@ public class FooComponent { ...@@ -59,6 +68,23 @@ public class FooComponent {
59 log.info("YEEEEHAAAAW! {}", event); 68 log.info("YEEEEHAAAAW! {}", event);
60 } 69 }
61 } 70 }
71 +
72 + private class InnerIntentListener implements IntentListener {
73 + @Override
74 + public void event(IntentEvent event) {
75 + String message;
76 + if (event.type() == IntentEvent.Type.SUBMITTED) {
77 + message = "WOW! It looks like someone has some intentions: {}";
78 + } else if (event.type() == IntentEvent.Type.INSTALLED) {
79 + message = "AWESOME! So far things are going great: {}";
80 + } else if (event.type() == IntentEvent.Type.WITHDRAWN) {
81 + message = "HMMM! Ambitions are fading apparently: {}";
82 + } else {
83 + message = "CRAP!!! Things are not turning out as intended: {}";
84 + }
85 + log.info(message, event.subject());
86 + }
87 + }
62 } 88 }
63 89
64 90
......
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 +}
1 +package org.onlab.onos.foo;
2 +
3 +import java.io.IOException;
4 +import java.util.concurrent.ExecutionException;
5 +import java.util.concurrent.TimeoutException;
6 +
7 +import org.onlab.metrics.MetricsComponent;
8 +import org.onlab.metrics.MetricsFeature;
9 +import org.onlab.metrics.MetricsManager;
10 +import org.onlab.netty.Endpoint;
11 +import org.onlab.netty.NettyMessagingService;
12 +import org.onlab.netty.Response;
13 +import org.slf4j.Logger;
14 +import org.slf4j.LoggerFactory;
15 +
16 +import com.codahale.metrics.Timer;
17 +
18 +// FIXME: Should be move out to test or app
19 +public final class SimpleNettyClient {
20 +
21 +private static Logger log = LoggerFactory.getLogger(SimpleNettyClient.class);
22 +
23 + private SimpleNettyClient() {
24 + }
25 +
26 + public static void main(String[] args)
27 + throws IOException, InterruptedException, ExecutionException,
28 + TimeoutException {
29 + try {
30 + startStandalone(args);
31 + } catch (Exception e) {
32 + e.printStackTrace();
33 + }
34 +
35 + System.exit(0);
36 + }
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;
42 + NettyMessagingService messaging = new TestNettyMessagingService(9081);
43 + MetricsManager metrics = new MetricsManager();
44 + messaging.activate();
45 + metrics.activate();
46 + MetricsFeature feature = new MetricsFeature("latency");
47 + MetricsComponent component = metrics.registerComponent("NettyMessaging");
48 + log.info("warmup....");
49 +
50 + for (int i = 0; i < warmup; i++) {
51 + messaging.sendAsync(new Endpoint(host, port), "simple", "Hello World".getBytes());
52 + Response response = messaging
53 + .sendAndReceive(new Endpoint(host, port), "echo",
54 + "Hello World".getBytes());
55 + }
56 +
57 + log.info("measuring async sender");
58 + Timer sendAsyncTimer = metrics.createTimer(component, feature, "AsyncSender");
59 +
60 + for (int i = 0; i < iterations; i++) {
61 + Timer.Context context = sendAsyncTimer.time();
62 + messaging.sendAsync(new Endpoint(host, port), "simple", "Hello World".getBytes());
63 + context.stop();
64 + }
65 +
66 + Timer sendAndReceiveTimer = metrics.createTimer(component, feature, "SendAndReceive");
67 + for (int i = 0; i < iterations; i++) {
68 + Timer.Context context = sendAndReceiveTimer.time();
69 + Response response = messaging
70 + .sendAndReceive(new Endpoint(host, port), "echo",
71 + "Hello World".getBytes());
72 + // System.out.println("Got back:" + new String(response.get(2, TimeUnit.SECONDS)));
73 + context.stop();
74 + }
75 + }
76 +
77 + public static class TestNettyMessagingService extends NettyMessagingService {
78 + public TestNettyMessagingService(int port) throws Exception {
79 + super(port);
80 + }
81 + }
82 +}
1 +package org.onlab.onos.foo;
2 +
3 +import static org.onlab.onos.foo.SimpleNettyClient.startStandalone;
4 +
5 +import org.apache.karaf.shell.commands.Argument;
6 +import org.apache.karaf.shell.commands.Command;
7 +import org.onlab.onos.cli.AbstractShellCommand;
8 +
9 +/**
10 + * Test Netty client performance.
11 + */
12 +@Command(scope = "onos", name = "simple-netty-client",
13 + description = "Starts the simple Netty client")
14 +public class SimpleNettyClientCommand extends AbstractShellCommand {
15 +
16 + //FIXME: replace these arguments with proper ones needed for the test.
17 + @Argument(index = 0, name = "hostname", description = "Server Hostname",
18 + required = false, multiValued = false)
19 + String host = "localhost";
20 +
21 + @Argument(index = 3, name = "port", description = "Port",
22 + required = false, multiValued = false)
23 + String port = "8081";
24 +
25 + @Argument(index = 1, name = "warmupCount", description = "Warm-up count",
26 + required = false, multiValued = false)
27 + String warmup = "1000";
28 +
29 + @Argument(index = 2, name = "messageCount", description = "Message count",
30 + required = false, multiValued = false)
31 + String messageCount = "100000";
32 +
33 + @Override
34 + protected void execute() {
35 + try {
36 + startStandalone(new String[]{host, port, warmup, messageCount});
37 + } catch (Exception e) {
38 + error("Unable to start client %s", e);
39 + }
40 + }
41 +
42 +}
1 +package org.onlab.onos.foo;
2 +
3 +import org.onlab.netty.NettyMessagingService;
4 +import org.slf4j.Logger;
5 +import org.slf4j.LoggerFactory;
6 +
7 +/**
8 + * Test to measure Messaging performance.
9 + */
10 + public final class SimpleNettyServer {
11 + private static Logger log = LoggerFactory.getLogger(SimpleNettyServer.class);
12 +
13 + private SimpleNettyServer() {}
14 +
15 + public static void main(String... args) throws Exception {
16 + startStandalone(args);
17 + System.exit(0);
18 + }
19 +
20 + public static void startStandalone(String[] args) throws Exception {
21 + NettyMessagingService server = new NettyMessagingService(8081);
22 + server.activate();
23 + server.registerHandler("simple", new NettyLoggingHandler());
24 + server.registerHandler("echo", new NettyEchoHandler());
25 + }
26 + }
27 +
1 +package org.onlab.onos.foo;
2 +
3 +import static org.onlab.onos.foo.SimpleNettyServer.startStandalone;
4 +
5 +import org.apache.karaf.shell.commands.Argument;
6 +import org.apache.karaf.shell.commands.Command;
7 +import org.onlab.onos.cli.AbstractShellCommand;
8 +
9 +/**
10 + * Starts the Simple Netty server.
11 + */
12 +@Command(scope = "onos", name = "simple-netty-server",
13 + description = "Starts the simple netty server")
14 +public class SimpleNettyServerCommand extends AbstractShellCommand {
15 +
16 + //FIXME: Replace these with parameters for
17 + @Argument(index = 0, name = "serverIp", description = "Server IP address",
18 + required = false, multiValued = false)
19 + String serverIp = "127.0.0.1";
20 +
21 + @Argument(index = 1, name = "workers", description = "IO workers",
22 + required = false, multiValued = false)
23 + String workers = "6";
24 +
25 + @Argument(index = 2, name = "messageLength", description = "Message length (bytes)",
26 + required = false, multiValued = false)
27 + String messageLength = "128";
28 +
29 + @Override
30 + protected void execute() {
31 + try {
32 + startStandalone(new String[]{serverIp, workers, messageLength});
33 + } catch (Exception e) {
34 + error("Unable to start server %s", e);
35 + }
36 + }
37 +
38 +}
...@@ -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>
......
...@@ -26,9 +26,7 @@ import org.onlab.onos.net.packet.InboundPacket; ...@@ -26,9 +26,7 @@ import org.onlab.onos.net.packet.InboundPacket;
26 import org.onlab.onos.net.packet.PacketContext; 26 import org.onlab.onos.net.packet.PacketContext;
27 import org.onlab.onos.net.packet.PacketProcessor; 27 import org.onlab.onos.net.packet.PacketProcessor;
28 import org.onlab.onos.net.packet.PacketService; 28 import org.onlab.onos.net.packet.PacketService;
29 -import org.onlab.onos.net.proxyarp.ProxyArpService;
30 import org.onlab.onos.net.topology.TopologyService; 29 import org.onlab.onos.net.topology.TopologyService;
31 -import org.onlab.packet.ARP;
32 import org.onlab.packet.Ethernet; 30 import org.onlab.packet.Ethernet;
33 import org.slf4j.Logger; 31 import org.slf4j.Logger;
34 32
...@@ -39,6 +37,7 @@ import org.slf4j.Logger; ...@@ -39,6 +37,7 @@ import org.slf4j.Logger;
39 public class ReactiveForwarding { 37 public class ReactiveForwarding {
40 38
41 private static final int TIMEOUT = 10; 39 private static final int TIMEOUT = 10;
40 + private static final int PRIORITY = 10;
42 41
43 private final Logger log = getLogger(getClass()); 42 private final Logger log = getLogger(getClass());
44 43
...@@ -54,9 +53,6 @@ public class ReactiveForwarding { ...@@ -54,9 +53,6 @@ public class ReactiveForwarding {
54 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) 53 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
55 protected FlowRuleService flowRuleService; 54 protected FlowRuleService flowRuleService;
56 55
57 - @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
58 - protected ProxyArpService proxyArpService;
59 -
60 private ReactivePacketProcessor processor = new ReactivePacketProcessor(); 56 private ReactivePacketProcessor processor = new ReactivePacketProcessor();
61 57
62 private ApplicationId appId; 58 private ApplicationId appId;
...@@ -64,7 +60,7 @@ public class ReactiveForwarding { ...@@ -64,7 +60,7 @@ public class ReactiveForwarding {
64 @Activate 60 @Activate
65 public void activate() { 61 public void activate() {
66 appId = ApplicationId.getAppId(); 62 appId = ApplicationId.getAppId();
67 - packetService.addProcessor(processor, PacketProcessor.ADVISOR_MAX + 1); 63 + packetService.addProcessor(processor, PacketProcessor.ADVISOR_MAX + 2);
68 log.info("Started with Application ID {}", appId.id()); 64 log.info("Started with Application ID {}", appId.id());
69 } 65 }
70 66
...@@ -92,16 +88,6 @@ public class ReactiveForwarding { ...@@ -92,16 +88,6 @@ public class ReactiveForwarding {
92 88
93 InboundPacket pkt = context.inPacket(); 89 InboundPacket pkt = context.inPacket();
94 Ethernet ethPkt = pkt.parsed(); 90 Ethernet ethPkt = pkt.parsed();
95 - if (ethPkt.getEtherType() == Ethernet.TYPE_ARP) {
96 - ARP arp = (ARP) ethPkt.getPayload();
97 - if (arp.getOpCode() == ARP.OP_REPLY) {
98 - proxyArpService.forward(ethPkt);
99 - } else if (arp.getOpCode() == ARP.OP_REQUEST) {
100 - proxyArpService.reply(ethPkt);
101 - }
102 - context.block();
103 - return;
104 - }
105 91
106 HostId id = HostId.hostId(ethPkt.getDestinationMAC()); 92 HostId id = HostId.hostId(ethPkt.getDestinationMAC());
107 93
...@@ -180,24 +166,24 @@ public class ReactiveForwarding { ...@@ -180,24 +166,24 @@ public class ReactiveForwarding {
180 // We don't yet support bufferids in the flowservice so packet out first. 166 // We don't yet support bufferids in the flowservice so packet out first.
181 packetOut(context, portNumber); 167 packetOut(context, portNumber);
182 168
183 - if (context.inPacket().parsed().getEtherType() == Ethernet.TYPE_IPV4) { 169 +
184 170
185 // Install the flow rule to handle this type of message from now on. 171 // Install the flow rule to handle this type of message from now on.
186 Ethernet inPkt = context.inPacket().parsed(); 172 Ethernet inPkt = context.inPacket().parsed();
187 - TrafficSelector.Builder builder = new DefaultTrafficSelector.Builder(); 173 + TrafficSelector.Builder builder = DefaultTrafficSelector.builder();
188 builder.matchEthType(inPkt.getEtherType()) 174 builder.matchEthType(inPkt.getEtherType())
189 .matchEthSrc(inPkt.getSourceMAC()) 175 .matchEthSrc(inPkt.getSourceMAC())
190 .matchEthDst(inPkt.getDestinationMAC()) 176 .matchEthDst(inPkt.getDestinationMAC())
191 .matchInport(context.inPacket().receivedFrom().port()); 177 .matchInport(context.inPacket().receivedFrom().port());
192 178
193 - TrafficTreatment.Builder treat = new DefaultTrafficTreatment.Builder(); 179 + TrafficTreatment.Builder treat = DefaultTrafficTreatment.builder();
194 treat.setOutput(portNumber); 180 treat.setOutput(portNumber);
195 181
196 FlowRule f = new DefaultFlowRule(context.inPacket().receivedFrom().deviceId(), 182 FlowRule f = new DefaultFlowRule(context.inPacket().receivedFrom().deviceId(),
197 - builder.build(), treat.build(), 0, appId, TIMEOUT); 183 + builder.build(), treat.build(), PRIORITY, appId, TIMEOUT);
198 184
199 flowRuleService.applyFlowRules(f); 185 flowRuleService.applyFlowRules(f);
200 - } 186 +
201 } 187 }
202 188
203 } 189 }
......
1 +<?xml version="1.0" encoding="UTF-8"?>
2 +<project xmlns="http://maven.apache.org/POM/4.0.0"
3 + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4 + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
5 + <modelVersion>4.0.0</modelVersion>
6 +
7 + <parent>
8 + <groupId>org.onlab.onos</groupId>
9 + <artifactId>onos-apps</artifactId>
10 + <version>1.0.0-SNAPSHOT</version>
11 + <relativePath>../pom.xml</relativePath>
12 + </parent>
13 +
14 + <artifactId>onos-app-ifwd</artifactId>
15 + <packaging>bundle</packaging>
16 +
17 + <description>ONOS simple reactive forwarding app that uses intent service</description>
18 +
19 +</project>
1 +package org.onlab.onos.ifwd;
2 +
3 +import static org.slf4j.LoggerFactory.getLogger;
4 +
5 +import org.apache.felix.scr.annotations.Activate;
6 +import org.apache.felix.scr.annotations.Component;
7 +import org.apache.felix.scr.annotations.Deactivate;
8 +import org.apache.felix.scr.annotations.Reference;
9 +import org.apache.felix.scr.annotations.ReferenceCardinality;
10 +import org.onlab.onos.net.Host;
11 +import org.onlab.onos.net.HostId;
12 +import org.onlab.onos.net.PortNumber;
13 +import org.onlab.onos.net.flow.DefaultTrafficSelector;
14 +import org.onlab.onos.net.flow.DefaultTrafficTreatment;
15 +import org.onlab.onos.net.flow.TrafficSelector;
16 +import org.onlab.onos.net.flow.TrafficTreatment;
17 +import org.onlab.onos.net.host.HostService;
18 +import org.onlab.onos.net.intent.HostToHostIntent;
19 +import org.onlab.onos.net.intent.IntentId;
20 +import org.onlab.onos.net.intent.IntentService;
21 +import org.onlab.onos.net.packet.DefaultOutboundPacket;
22 +import org.onlab.onos.net.packet.InboundPacket;
23 +import org.onlab.onos.net.packet.OutboundPacket;
24 +import org.onlab.onos.net.packet.PacketContext;
25 +import org.onlab.onos.net.packet.PacketProcessor;
26 +import org.onlab.onos.net.packet.PacketService;
27 +import org.onlab.onos.net.topology.TopologyService;
28 +import org.onlab.packet.Ethernet;
29 +import org.slf4j.Logger;
30 +
31 +/**
32 + * WORK-IN-PROGRESS: Sample reactive forwarding application using intent framework.
33 + */
34 +@Component(immediate = true)
35 +public class IntentReactiveForwarding {
36 +
37 + private final Logger log = getLogger(getClass());
38 +
39 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
40 + protected TopologyService topologyService;
41 +
42 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
43 + protected PacketService packetService;
44 +
45 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
46 + protected IntentService intentService;
47 +
48 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
49 + protected HostService hostService;
50 +
51 + private ReactivePacketProcessor processor = new ReactivePacketProcessor();
52 +
53 + private static long intentId = 0x123000;
54 +
55 + @Activate
56 + public void activate() {
57 + packetService.addProcessor(processor, PacketProcessor.ADVISOR_MAX + 2);
58 + log.info("Started");
59 + }
60 +
61 + @Deactivate
62 + public void deactivate() {
63 + packetService.removeProcessor(processor);
64 + processor = null;
65 + log.info("Stopped");
66 + }
67 +
68 + /**
69 + * Packet processor responsible for forwarding packets along their paths.
70 + */
71 + private class ReactivePacketProcessor implements PacketProcessor {
72 +
73 + @Override
74 + public void process(PacketContext context) {
75 + // Stop processing if the packet has been handled, since we
76 + // can't do any more to it.
77 + if (context.isHandled()) {
78 + return;
79 + }
80 +
81 + InboundPacket pkt = context.inPacket();
82 + Ethernet ethPkt = pkt.parsed();
83 +
84 + HostId srcId = HostId.hostId(ethPkt.getSourceMAC());
85 + HostId dstId = HostId.hostId(ethPkt.getDestinationMAC());
86 +
87 + // Do we know who this is for? If not, flood and bail.
88 + Host dst = hostService.getHost(dstId);
89 + if (dst == null) {
90 + flood(context);
91 + return;
92 + }
93 +
94 + // Otherwise forward and be done with it.
95 + setUpConnectivity(context, srcId, dstId);
96 + forwardPacketToDst(context, dst);
97 + }
98 + }
99 +
100 + // Floods the specified packet if permissible.
101 + private void flood(PacketContext context) {
102 + if (topologyService.isBroadcastPoint(topologyService.currentTopology(),
103 + context.inPacket().receivedFrom())) {
104 + packetOut(context, PortNumber.FLOOD);
105 + } else {
106 + context.block();
107 + }
108 + }
109 +
110 + // Sends a packet out the specified port.
111 + private void packetOut(PacketContext context, PortNumber portNumber) {
112 + context.treatmentBuilder().setOutput(portNumber);
113 + context.send();
114 + }
115 +
116 + private void forwardPacketToDst(PacketContext context, Host dst) {
117 + TrafficTreatment treatment = DefaultTrafficTreatment.builder().setOutput(dst.location().port()).build();
118 + OutboundPacket packet = new DefaultOutboundPacket(dst.location().deviceId(),
119 + treatment, context.inPacket().unparsed());
120 + packetService.emit(packet);
121 + log.info("sending packet: {}", packet);
122 + }
123 +
124 + // Install a rule forwarding the packet to the specified port.
125 + private void setUpConnectivity(PacketContext context, HostId srcId, HostId dstId) {
126 + TrafficSelector selector = DefaultTrafficSelector.builder().build();
127 + TrafficTreatment treatment = DefaultTrafficTreatment.builder().build();
128 +
129 + HostToHostIntent intent =
130 + new HostToHostIntent(new IntentId(intentId++), srcId, dstId,
131 + selector, treatment);
132 +
133 + intentService.submit(intent);
134 + }
135 +
136 +}
1 +/**
2 + * Trivial application that provides simple form of reactive forwarding
3 + * using the intent service.
4 + */
5 +package org.onlab.onos.ifwd;
...@@ -19,8 +19,10 @@ ...@@ -19,8 +19,10 @@
19 <modules> 19 <modules>
20 <module>tvue</module> 20 <module>tvue</module>
21 <module>fwd</module> 21 <module>fwd</module>
22 + <module>ifwd</module>
22 <module>foo</module> 23 <module>foo</module>
23 <module>mobility</module> 24 <module>mobility</module>
25 + <module>proxyarp</module>
24 <module>config</module> 26 <module>config</module>
25 </modules> 27 </modules>
26 28
......
1 +<?xml version="1.0" encoding="UTF-8"?>
2 +<project xmlns="http://maven.apache.org/POM/4.0.0"
3 + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4 + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
5 + <modelVersion>4.0.0</modelVersion>
6 +
7 + <parent>
8 + <groupId>org.onlab.onos</groupId>
9 + <artifactId>onos-apps</artifactId>
10 + <version>1.0.0-SNAPSHOT</version>
11 + <relativePath>../pom.xml</relativePath>
12 + </parent>
13 +
14 + <artifactId>onos-app-proxyarp</artifactId>
15 + <packaging>bundle</packaging>
16 +
17 + <description>ONOS simple proxy arp module</description>
18 +
19 +</project>
1 +package org.onlab.onos.proxyarp;
2 +
3 +import static org.slf4j.LoggerFactory.getLogger;
4 +
5 +import org.apache.felix.scr.annotations.Activate;
6 +import org.apache.felix.scr.annotations.Component;
7 +import org.apache.felix.scr.annotations.Deactivate;
8 +import org.apache.felix.scr.annotations.Reference;
9 +import org.apache.felix.scr.annotations.ReferenceCardinality;
10 +import org.onlab.onos.ApplicationId;
11 +import org.onlab.onos.net.packet.PacketContext;
12 +import org.onlab.onos.net.packet.PacketProcessor;
13 +import org.onlab.onos.net.packet.PacketService;
14 +import org.onlab.onos.net.proxyarp.ProxyArpService;
15 +import org.slf4j.Logger;
16 +
17 +/**
18 + * Sample reactive proxy arp application.
19 + */
20 +@Component(immediate = true)
21 +public class ProxyArp {
22 +
23 +
24 + private final Logger log = getLogger(getClass());
25 +
26 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
27 + protected PacketService packetService;
28 +
29 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
30 + protected ProxyArpService proxyArpService;
31 +
32 + private ProxyArpProcessor processor = new ProxyArpProcessor();
33 +
34 + private ApplicationId appId;
35 +
36 + @Activate
37 + public void activate() {
38 + appId = ApplicationId.getAppId();
39 + packetService.addProcessor(processor, PacketProcessor.ADVISOR_MAX + 1);
40 + log.info("Started with Application ID {}", appId.id());
41 + }
42 +
43 + @Deactivate
44 + public void deactivate() {
45 + packetService.removeProcessor(processor);
46 + processor = null;
47 + log.info("Stopped");
48 + }
49 +
50 +
51 + /**
52 + * Packet processor responsible for forwarding packets along their paths.
53 + */
54 + private class ProxyArpProcessor implements PacketProcessor {
55 +
56 + @Override
57 + public void process(PacketContext context) {
58 + // Stop processing if the packet has been handled, since we
59 + // can't do any more to it.
60 + if (context.isHandled()) {
61 + return;
62 + }
63 +
64 + //handle the arp packet.
65 + proxyArpService.handleArp(context);
66 + }
67 + }
68 +}
69 +
70 +
1 +/**
2 + * Proxy Arp application that handles arp resolution for you.
3 + */
4 +package org.onlab.onos.proxyarp;
1 +package org.onlab.onos.cli;
2 +
3 +import org.apache.karaf.shell.commands.Command;
4 +import org.onlab.onos.CoreService;
5 +import org.onlab.onos.cluster.ClusterService;
6 +import org.onlab.onos.net.device.DeviceService;
7 +import org.onlab.onos.net.flow.FlowRuleService;
8 +import org.onlab.onos.net.host.HostService;
9 +import org.onlab.onos.net.intent.IntentService;
10 +import org.onlab.onos.net.link.LinkService;
11 +import org.onlab.onos.net.topology.Topology;
12 +import org.onlab.onos.net.topology.TopologyService;
13 +
14 +/**
15 + * Provides summary of ONOS model.
16 + */
17 +@Command(scope = "onos", name = "summary",
18 + description = "Provides summary of ONOS model")
19 +public class SummaryCommand extends AbstractShellCommand {
20 +
21 + @Override
22 + protected void execute() {
23 + TopologyService topologyService = get(TopologyService.class);
24 + Topology topology = topologyService.currentTopology();
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(),
27 + get(ClusterService.class).getNodes().size(),
28 + get(DeviceService.class).getDeviceCount(),
29 + get(LinkService.class).getLinkCount(),
30 + get(HostService.class).getHostCount(),
31 + topologyService.getClusters(topology).size(),
32 + topology.pathCount(),
33 + get(FlowRuleService.class).getFlowRuleCount(),
34 + get(IntentService.class).getIntentCount());
35 + }
36 +
37 +}
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.HostId;
7 +import org.onlab.onos.net.flow.DefaultTrafficSelector;
8 +import org.onlab.onos.net.flow.DefaultTrafficTreatment;
9 +import org.onlab.onos.net.flow.TrafficSelector;
10 +import org.onlab.onos.net.flow.TrafficTreatment;
11 +import org.onlab.onos.net.intent.HostToHostIntent;
12 +import org.onlab.onos.net.intent.IntentId;
13 +import org.onlab.onos.net.intent.IntentService;
14 +
15 +/**
16 + * Installs host-to-host connectivity intent.
17 + */
18 +@Command(scope = "onos", name = "add-host-intent",
19 + description = "Installs host-to-host connectivity intent")
20 +public class AddHostToHostIntentCommand extends AbstractShellCommand {
21 +
22 + @Argument(index = 0, name = "one", description = "One host ID",
23 + required = true, multiValued = false)
24 + String one = null;
25 +
26 + @Argument(index = 1, name = "two", description = "Another host ID",
27 + required = true, multiValued = false)
28 + String two = null;
29 +
30 + private static long id = 0x7870001;
31 +
32 + @Override
33 + protected void execute() {
34 + IntentService service = get(IntentService.class);
35 +
36 + HostId oneId = HostId.hostId(one);
37 + HostId twoId = HostId.hostId(two);
38 +
39 + TrafficSelector selector = DefaultTrafficSelector.builder().build();
40 + TrafficTreatment treatment = DefaultTrafficTreatment.builder().build();
41 +
42 + HostToHostIntent intent =
43 + new HostToHostIntent(new IntentId(id++), oneId, twoId,
44 + selector, treatment);
45 + service.submit(intent);
46 + }
47 +
48 +}
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 +}
...@@ -35,7 +35,7 @@ public class DevicesListCommand extends AbstractShellCommand { ...@@ -35,7 +35,7 @@ public class DevicesListCommand extends AbstractShellCommand {
35 * @param service device service 35 * @param service device service
36 * @return sorted device list 36 * @return sorted device list
37 */ 37 */
38 - protected List<Device> getSortedDevices(DeviceService service) { 38 + protected static List<Device> getSortedDevices(DeviceService service) {
39 List<Device> devices = newArrayList(service.getDevices()); 39 List<Device> devices = newArrayList(service.getDevices());
40 Collections.sort(devices, Comparators.ELEMENT_COMPARATOR); 40 Collections.sort(devices, Comparators.ELEMENT_COMPARATOR);
41 return devices; 41 return devices;
......
...@@ -5,7 +5,7 @@ import java.util.SortedSet; ...@@ -5,7 +5,7 @@ import java.util.SortedSet;
5 5
6 import org.apache.karaf.shell.console.Completer; 6 import org.apache.karaf.shell.console.Completer;
7 import org.apache.karaf.shell.console.completer.StringsCompleter; 7 import org.apache.karaf.shell.console.completer.StringsCompleter;
8 -import org.onlab.onos.net.flow.FlowRule.FlowRuleState; 8 +import org.onlab.onos.net.flow.FlowEntry.FlowEntryState;
9 9
10 /** 10 /**
11 * Device ID completer. 11 * Device ID completer.
...@@ -16,7 +16,7 @@ public class FlowRuleStatusCompleter implements Completer { ...@@ -16,7 +16,7 @@ public class FlowRuleStatusCompleter implements Completer {
16 // Delegate string completer 16 // Delegate string completer
17 StringsCompleter delegate = new StringsCompleter(); 17 StringsCompleter delegate = new StringsCompleter();
18 18
19 - FlowRuleState[] states = FlowRuleState.values(); 19 + FlowEntryState[] states = FlowEntryState.values();
20 SortedSet<String> strings = delegate.getStrings(); 20 SortedSet<String> strings = delegate.getStrings();
21 for (int i = 0; i < states.length; i++) { 21 for (int i = 0; i < states.length; i++) {
22 strings.add(states[i].toString().toLowerCase()); 22 strings.add(states[i].toString().toLowerCase());
......
1 package org.onlab.onos.cli.net; 1 package org.onlab.onos.cli.net;
2 2
3 import static com.google.common.collect.Lists.newArrayList; 3 import static com.google.common.collect.Lists.newArrayList;
4 +import static org.onlab.onos.cli.net.DevicesListCommand.getSortedDevices;
4 5
5 import java.util.Collections; 6 import java.util.Collections;
6 import java.util.List; 7 import java.util.List;
...@@ -13,8 +14,8 @@ import org.onlab.onos.cli.Comparators; ...@@ -13,8 +14,8 @@ import org.onlab.onos.cli.Comparators;
13 import org.onlab.onos.net.Device; 14 import org.onlab.onos.net.Device;
14 import org.onlab.onos.net.DeviceId; 15 import org.onlab.onos.net.DeviceId;
15 import org.onlab.onos.net.device.DeviceService; 16 import org.onlab.onos.net.device.DeviceService;
16 -import org.onlab.onos.net.flow.FlowRule; 17 +import org.onlab.onos.net.flow.FlowEntry;
17 -import org.onlab.onos.net.flow.FlowRule.FlowRuleState; 18 +import org.onlab.onos.net.flow.FlowEntry.FlowEntryState;
18 import org.onlab.onos.net.flow.FlowRuleService; 19 import org.onlab.onos.net.flow.FlowRuleService;
19 20
20 import com.google.common.collect.Maps; 21 import com.google.common.collect.Maps;
...@@ -45,8 +46,8 @@ public class FlowsListCommand extends AbstractShellCommand { ...@@ -45,8 +46,8 @@ public class FlowsListCommand extends AbstractShellCommand {
45 protected void execute() { 46 protected void execute() {
46 DeviceService deviceService = get(DeviceService.class); 47 DeviceService deviceService = get(DeviceService.class);
47 FlowRuleService service = get(FlowRuleService.class); 48 FlowRuleService service = get(FlowRuleService.class);
48 - Map<Device, List<FlowRule>> flows = getSortedFlows(deviceService, service); 49 + Map<Device, List<FlowEntry>> flows = getSortedFlows(deviceService, service);
49 - for (Device d : flows.keySet()) { 50 + for (Device d : getSortedDevices(deviceService)) {
50 printFlows(d, flows.get(d)); 51 printFlows(d, flows.get(d));
51 } 52 }
52 } 53 }
...@@ -57,12 +58,13 @@ public class FlowsListCommand extends AbstractShellCommand { ...@@ -57,12 +58,13 @@ public class FlowsListCommand extends AbstractShellCommand {
57 * @param service device service 58 * @param service device service
58 * @return sorted device list 59 * @return sorted device list
59 */ 60 */
60 - protected Map<Device, List<FlowRule>> getSortedFlows(DeviceService deviceService, FlowRuleService service) { 61 + protected Map<Device, List<FlowEntry>> getSortedFlows(DeviceService deviceService,
61 - Map<Device, List<FlowRule>> flows = Maps.newHashMap(); 62 + FlowRuleService service) {
62 - List<FlowRule> rules; 63 + Map<Device, List<FlowEntry>> flows = Maps.newHashMap();
63 - FlowRuleState s = null; 64 + List<FlowEntry> rules;
65 + FlowEntryState s = null;
64 if (state != null && !state.equals("any")) { 66 if (state != null && !state.equals("any")) {
65 - s = FlowRuleState.valueOf(state.toUpperCase()); 67 + s = FlowEntryState.valueOf(state.toUpperCase());
66 } 68 }
67 Iterable<Device> devices = uri == null ? deviceService.getDevices() : 69 Iterable<Device> devices = uri == null ? deviceService.getDevices() :
68 Collections.singletonList(deviceService.getDevice(DeviceId.deviceId(uri))); 70 Collections.singletonList(deviceService.getDevice(DeviceId.deviceId(uri)));
...@@ -71,7 +73,7 @@ public class FlowsListCommand extends AbstractShellCommand { ...@@ -71,7 +73,7 @@ public class FlowsListCommand extends AbstractShellCommand {
71 rules = newArrayList(service.getFlowEntries(d.id())); 73 rules = newArrayList(service.getFlowEntries(d.id()));
72 } else { 74 } else {
73 rules = newArrayList(); 75 rules = newArrayList();
74 - for (FlowRule f : service.getFlowEntries(d.id())) { 76 + for (FlowEntry f : service.getFlowEntries(d.id())) {
75 if (f.state().equals(s)) { 77 if (f.state().equals(s)) {
76 rules.add(f); 78 rules.add(f);
77 } 79 }
...@@ -88,19 +90,17 @@ public class FlowsListCommand extends AbstractShellCommand { ...@@ -88,19 +90,17 @@ public class FlowsListCommand extends AbstractShellCommand {
88 * @param d the device 90 * @param d the device
89 * @param flows the set of flows for that device. 91 * @param flows the set of flows for that device.
90 */ 92 */
91 - protected void printFlows(Device d, List<FlowRule> flows) { 93 + protected void printFlows(Device d, List<FlowEntry> flows) {
92 - print("Device: " + d.id()); 94 + boolean empty = flows == null || flows.isEmpty();
93 - if (flows == null | flows.isEmpty()) { 95 + print("deviceId=%s, flowRuleCount=%d", d.id(), empty ? 0 : flows.size());
94 - print(" %s", "No flows."); 96 + if (!empty) {
95 - return; 97 + for (FlowEntry f : flows) {
96 - }
97 - for (FlowRule f : flows) {
98 print(FMT, Long.toHexString(f.id().value()), f.state(), f.bytes(), 98 print(FMT, Long.toHexString(f.id().value()), f.state(), f.bytes(),
99 - f.packets(), f.lifeMillis(), f.priority()); 99 + f.packets(), f.life(), f.priority());
100 print(SFMT, f.selector().criteria()); 100 print(SFMT, f.selector().criteria());
101 print(TFMT, f.treatment().instructions()); 101 print(TFMT, f.treatment().instructions());
102 } 102 }
103 - 103 + }
104 } 104 }
105 105
106 } 106 }
......
1 +package org.onlab.onos.cli.net;
2 +
3 +import org.apache.karaf.shell.console.Completer;
4 +import org.apache.karaf.shell.console.completer.StringsCompleter;
5 +import org.onlab.onos.cli.AbstractShellCommand;
6 +import org.onlab.onos.net.intent.Intent;
7 +import org.onlab.onos.net.intent.IntentService;
8 +
9 +import java.util.Iterator;
10 +import java.util.List;
11 +import java.util.SortedSet;
12 +
13 +/**
14 + * Intent ID completer.
15 + */
16 +public class IntentIdCompleter 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 + IntentService service = AbstractShellCommand.get(IntentService.class);
24 + Iterator<Intent> it = service.getIntents().iterator();
25 + SortedSet<String> strings = delegate.getStrings();
26 + while (it.hasNext()) {
27 + strings.add(it.next().id().toString());
28 + }
29 +
30 + // Now let the completer do the work for figuring out what to offer.
31 + return delegate.complete(buffer, cursor, candidates);
32 + }
33 +
34 +}
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.intent.Intent;
7 +import org.onlab.onos.net.intent.IntentId;
8 +import org.onlab.onos.net.intent.IntentService;
9 +
10 +/**
11 + * Removes host-to-host connectivity intent.
12 + */
13 +@Command(scope = "onos", name = "remove-intent",
14 + description = "Removes the specified intent")
15 +public class IntentRemoveCommand extends AbstractShellCommand {
16 +
17 + @Argument(index = 0, name = "id", description = "Intent ID",
18 + required = true, multiValued = false)
19 + String id = null;
20 +
21 + @Override
22 + protected void execute() {
23 + IntentService service = get(IntentService.class);
24 +
25 + int radix = id.startsWith("0x") ? 16 : 10;
26 + if (radix == 16) {
27 + id = id.replaceFirst("0x", "");
28 + }
29 + IntentId intentId = new IntentId(Long.parseLong(id, radix));
30 +
31 +
32 + Intent intent = service.getIntent(intentId);
33 + if (intent != null) {
34 + service.withdraw(intent);
35 + }
36 + }
37 +}
1 +package org.onlab.onos.cli.net;
2 +
3 +import org.apache.karaf.shell.commands.Command;
4 +import org.onlab.onos.cli.AbstractShellCommand;
5 +import org.onlab.onos.net.intent.Intent;
6 +import org.onlab.onos.net.intent.IntentService;
7 +import org.onlab.onos.net.intent.IntentState;
8 +
9 +/**
10 + * Lists the inventory of intents and their states.
11 + */
12 +@Command(scope = "onos", name = "intents",
13 + description = "Lists the inventory of intents and their states")
14 +public class IntentsListCommand extends AbstractShellCommand {
15 +
16 + @Override
17 + protected void execute() {
18 + IntentService service = get(IntentService.class);
19 + for (Intent intent : service.getIntents()) {
20 + IntentState state = service.getIntentState(intent.id());
21 + print("%s %s %s", intent.id(), state, intent);
22 + }
23 + }
24 +
25 +}
1 package org.onlab.onos.cli.net; 1 package org.onlab.onos.cli.net;
2 2
3 +import org.apache.karaf.shell.commands.Argument;
3 import org.apache.karaf.shell.commands.Command; 4 import org.apache.karaf.shell.commands.Command;
4 import org.onlab.onos.net.Device; 5 import org.onlab.onos.net.Device;
5 import org.onlab.onos.net.Host; 6 import org.onlab.onos.net.Host;
...@@ -7,28 +8,51 @@ import org.onlab.onos.net.device.DeviceAdminService; ...@@ -7,28 +8,51 @@ import org.onlab.onos.net.device.DeviceAdminService;
7 import org.onlab.onos.net.device.DeviceService; 8 import org.onlab.onos.net.device.DeviceService;
8 import org.onlab.onos.net.host.HostAdminService; 9 import org.onlab.onos.net.host.HostAdminService;
9 import org.onlab.onos.net.host.HostService; 10 import org.onlab.onos.net.host.HostService;
11 +import org.onlab.onos.net.intent.Intent;
12 +import org.onlab.onos.net.intent.IntentService;
13 +import org.onlab.onos.net.intent.IntentState;
10 14
11 /** 15 /**
12 - * 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.
13 */ 17 */
14 @Command(scope = "onos", name = "wipe-out", 18 @Command(scope = "onos", name = "wipe-out",
15 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")
16 public class WipeOutCommand extends ClustersListCommand { 20 public class WipeOutCommand extends ClustersListCommand {
17 21
22 + private static final String DISCLAIMER = "Delete everything please.";
23 +
24 + @Argument(index = 0, name = "disclaimer", description = "Device ID",
25 + required = false, multiValued = false)
26 + String disclaimer = null;
27 +
18 @Override 28 @Override
19 protected void execute() { 29 protected void execute() {
30 + if (disclaimer == null || !disclaimer.equals(DISCLAIMER)) {
31 + print("I'm afraid I can't do that!\nPlease acknowledge with phrase: '%s'",
32 + DISCLAIMER);
33 + return;
34 + }
35 +
36 + print("Wiping devices");
20 DeviceAdminService deviceAdminService = get(DeviceAdminService.class); 37 DeviceAdminService deviceAdminService = get(DeviceAdminService.class);
21 DeviceService deviceService = get(DeviceService.class); 38 DeviceService deviceService = get(DeviceService.class);
22 for (Device device : deviceService.getDevices()) { 39 for (Device device : deviceService.getDevices()) {
23 deviceAdminService.removeDevice(device.id()); 40 deviceAdminService.removeDevice(device.id());
24 } 41 }
25 42
43 + print("Wiping hosts");
26 HostAdminService hostAdminService = get(HostAdminService.class); 44 HostAdminService hostAdminService = get(HostAdminService.class);
27 HostService hostService = get(HostService.class); 45 HostService hostService = get(HostService.class);
28 for (Host host : hostService.getHosts()) { 46 for (Host host : hostService.getHosts()) {
29 hostAdminService.removeHost(host.id()); 47 hostAdminService.removeHost(host.id());
30 } 48 }
31 - }
32 -
33 49
50 + print("Wiping intents");
51 + IntentService intentService = get(IntentService.class);
52 + for (Intent intent : intentService.getIntents()) {
53 + if (intentService.getIntentState(intent.id()) == IntentState.INSTALLED) {
54 + intentService.withdraw(intent);
55 + }
56 + }
57 + }
34 } 58 }
......
...@@ -2,6 +2,9 @@ ...@@ -2,6 +2,9 @@
2 2
3 <command-bundle xmlns="http://karaf.apache.org/xmlns/shell/v1.1.0"> 3 <command-bundle xmlns="http://karaf.apache.org/xmlns/shell/v1.1.0">
4 <command> 4 <command>
5 + <action class="org.onlab.onos.cli.SummaryCommand"/>
6 + </command>
7 + <command>
5 <action class="org.onlab.onos.cli.NodesListCommand"/> 8 <action class="org.onlab.onos.cli.NodesListCommand"/>
6 </command> 9 </command>
7 <command> 10 <command>
...@@ -56,6 +59,30 @@ ...@@ -56,6 +59,30 @@
56 <ref component-id="deviceIdCompleter"/> 59 <ref component-id="deviceIdCompleter"/>
57 </completers> 60 </completers>
58 </command> 61 </command>
62 +
63 + <command>
64 + <action class="org.onlab.onos.cli.net.IntentsListCommand"/>
65 + </command>
66 + <command>
67 + <action class="org.onlab.onos.cli.net.IntentRemoveCommand"/>
68 + <completers>
69 + <ref component-id="intentIdCompleter"/>
70 + </completers>
71 + </command>
72 + <command>
73 + <action class="org.onlab.onos.cli.net.AddHostToHostIntentCommand"/>
74 + <completers>
75 + <ref component-id="hostIdCompleter"/>
76 + </completers>
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>
85 +
59 <command> 86 <command>
60 <action class="org.onlab.onos.cli.net.ClustersListCommand"/> 87 <action class="org.onlab.onos.cli.net.ClustersListCommand"/>
61 </command> 88 </command>
...@@ -94,6 +121,8 @@ ...@@ -94,6 +121,8 @@
94 <bean id="clusterIdCompleter" class="org.onlab.onos.cli.net.ClusterIdCompleter"/> 121 <bean id="clusterIdCompleter" class="org.onlab.onos.cli.net.ClusterIdCompleter"/>
95 <bean id="roleCompleter" class="org.onlab.onos.cli.net.RoleCompleter"/> 122 <bean id="roleCompleter" class="org.onlab.onos.cli.net.RoleCompleter"/>
96 <bean id="hostIdCompleter" class="org.onlab.onos.cli.net.HostIdCompleter"/> 123 <bean id="hostIdCompleter" class="org.onlab.onos.cli.net.HostIdCompleter"/>
124 + <bean id="intentIdCompleter" class="org.onlab.onos.cli.net.IntentIdCompleter"/>
97 <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"/>
98 127
99 </blueprint> 128 </blueprint>
......
...@@ -8,7 +8,7 @@ import java.util.concurrent.atomic.AtomicInteger; ...@@ -8,7 +8,7 @@ import java.util.concurrent.atomic.AtomicInteger;
8 */ 8 */
9 public final class ApplicationId { 9 public final class ApplicationId {
10 10
11 - private static AtomicInteger idDispenser; 11 + private static final AtomicInteger ID_DISPENCER = new AtomicInteger(1);
12 private final Integer id; 12 private final Integer id;
13 13
14 // Ban public construction 14 // Ban public construction
...@@ -50,10 +50,7 @@ public final class ApplicationId { ...@@ -50,10 +50,7 @@ public final class ApplicationId {
50 * @return app id 50 * @return app id
51 */ 51 */
52 public static ApplicationId getAppId() { 52 public static ApplicationId getAppId() {
53 - if (ApplicationId.idDispenser == null) { 53 + return new ApplicationId(ApplicationId.ID_DISPENCER.getAndIncrement());
54 - ApplicationId.idDispenser = new AtomicInteger(1);
55 - }
56 - return new ApplicationId(ApplicationId.idDispenser.getAndIncrement());
57 } 54 }
58 55
59 } 56 }
......
1 +package org.onlab.onos;
2 +
3 +/**
4 + * Service for interacting with the core system of the controller.
5 + */
6 +public interface CoreService {
7 +
8 + /**
9 + * Returns the product version.
10 + *
11 + * @return product version
12 + */
13 + Version version();
14 +
15 +}
1 +package org.onlab.onos;
2 +
3 +import java.util.Objects;
4 +
5 +import static java.lang.Integer.parseInt;
6 +
7 +/**
8 + * Representation of the product version.
9 + */
10 +public final class Version {
11 +
12 + public static final String FORMAT = "%d.%d.%d.%s";
13 +
14 + private final int major;
15 + private final int minor;
16 + private final int patch;
17 + private final String build;
18 +
19 + private final String format;
20 +
21 + // Creates a new version descriptor
22 + private Version(int major, int minor, int patch, String build) {
23 + this.major = major;
24 + this.minor = minor;
25 + this.patch = patch;
26 + this.build = build;
27 + this.format = String.format(FORMAT, major, minor, patch, build);
28 + }
29 +
30 +
31 + /**
32 + * Creates a new version from the specified constituent numbers.
33 + *
34 + * @param major major version number
35 + * @param minor minod version number
36 + * @param patch version patch number
37 + * @param build build string
38 + * @return version descriptor
39 + */
40 + public static Version version(int major, int minor, int patch, String build) {
41 + return new Version(major, minor, patch, build);
42 + }
43 +
44 + /**
45 + * Creates a new version by parsing the specified string.
46 + *
47 + * @param string version string
48 + * @return version descriptor
49 + */
50 + public static Version version(String string) {
51 + String[] fields = string.split("[.-]");
52 + return new Version(parseInt(fields[0]), parseInt(fields[1]),
53 + parseInt(fields[2]), fields[3]);
54 + }
55 +
56 + /**
57 + * Returns the major version number.
58 + *
59 + * @return major version number
60 + */
61 + public int major() {
62 + return major;
63 + }
64 +
65 + /**
66 + * Returns the minor version number.
67 + *
68 + * @return minor version number
69 + */
70 + public int minor() {
71 + return minor;
72 + }
73 +
74 + /**
75 + * Returns the version patch number.
76 + *
77 + * @return patch number
78 + */
79 + public int patch() {
80 + return patch;
81 + }
82 +
83 + /**
84 + * Returns the version build string.
85 + *
86 + * @return build string
87 + */
88 + public String build() {
89 + return build;
90 + }
91 +
92 + @Override
93 + public String toString() {
94 + return format;
95 + }
96 +
97 + @Override
98 + public int hashCode() {
99 + return Objects.hash(format);
100 + }
101 +
102 + @Override
103 + public boolean equals(Object obj) {
104 + if (this == obj) {
105 + return true;
106 + }
107 + if (obj instanceof Version) {
108 + final Version other = (Version) obj;
109 + return Objects.equals(this.format, other.format);
110 + }
111 + return false;
112 + }
113 +}
...@@ -17,6 +17,7 @@ public interface MastershipService { ...@@ -17,6 +17,7 @@ public interface MastershipService {
17 * Returns the role of the local node for the specified device, without 17 * Returns the role of the local node for the specified device, without
18 * triggering master selection. 18 * triggering master selection.
19 * 19 *
20 + * @param deviceId the the identifier of the device
20 * @return role of the current node 21 * @return role of the current node
21 */ 22 */
22 MastershipRole getLocalRole(DeviceId deviceId); 23 MastershipRole getLocalRole(DeviceId deviceId);
......
1 +package org.onlab.onos.net;
2 +
3 +public final class AnnotationsUtil {
4 +
5 + public static boolean isEqual(Annotations lhs, Annotations rhs) {
6 + if (lhs == rhs) {
7 + return true;
8 + }
9 + if (lhs == null || rhs == null) {
10 + return false;
11 + }
12 +
13 + if (!lhs.keys().equals(rhs.keys())) {
14 + return false;
15 + }
16 +
17 + for (String key : lhs.keys()) {
18 + if (!lhs.value(key).equals(rhs.value(key))) {
19 + return false;
20 + }
21 + }
22 + return true;
23 + }
24 +
25 + // not to be instantiated
26 + private AnnotationsUtil() {}
27 +}
...@@ -51,6 +51,22 @@ public class ConnectPoint { ...@@ -51,6 +51,22 @@ public class ConnectPoint {
51 } 51 }
52 52
53 /** 53 /**
54 + * Returns the identifier of the infrastructure device if the connection
55 + * point belongs to a network element which is indeed an end-station host.
56 + *
57 + * @return network element identifier as a host identifier
58 + * @throws java.lang.IllegalStateException if connection point is not
59 + * associated with a host
60 + */
61 + public HostId hostId() {
62 + if (elementId instanceof HostId) {
63 + return (HostId) elementId;
64 + }
65 + throw new IllegalStateException("Connection point not associated " +
66 + "with an end-station host");
67 + }
68 +
69 + /**
54 * Returns the connection port number. 70 * Returns the connection port number.
55 * 71 *
56 * @return port number 72 * @return port number
......
...@@ -73,31 +73,63 @@ public final class DefaultAnnotations implements SparseAnnotations { ...@@ -73,31 +73,63 @@ public final class DefaultAnnotations implements SparseAnnotations {
73 } 73 }
74 74
75 /** 75 /**
76 - * Convert Annotations to DefaultAnnotations if needed and merges. 76 + * Creates the union of two given SparseAnnotations.
77 + * Unlike the {@link #merge(DefaultAnnotations, SparseAnnotations)} method,
78 + * result will be {@link SparseAnnotations} instead of {@link Annotations}.
77 * 79 *
78 - * @see #merge(DefaultAnnotations, SparseAnnotations) 80 + * A key tagged for removal will remain in the output SparseAnnotations,
81 + * if the counterpart of the input does not contain the same key.
79 * 82 *
80 * @param annotations base annotations 83 * @param annotations base annotations
81 * @param sparseAnnotations additional sparse annotations 84 * @param sparseAnnotations additional sparse annotations
82 * @return combined annotations or the original base annotations if there 85 * @return combined annotations or the original base annotations if there
83 * are not additional annotations 86 * are not additional annotations
84 */ 87 */
85 - public static DefaultAnnotations merge(Annotations annotations, 88 + public static SparseAnnotations union(SparseAnnotations annotations,
86 SparseAnnotations sparseAnnotations) { 89 SparseAnnotations sparseAnnotations) {
90 +
91 + if (sparseAnnotations == null || sparseAnnotations.keys().isEmpty()) {
92 + return annotations;
93 + }
94 +
95 + final HashMap<String, String> newMap;
87 if (annotations instanceof DefaultAnnotations) { 96 if (annotations instanceof DefaultAnnotations) {
88 - return merge((DefaultAnnotations) annotations, sparseAnnotations); 97 + newMap = copy(((DefaultAnnotations) annotations).map);
98 + } else {
99 + newMap = new HashMap<>(annotations.keys().size() +
100 + sparseAnnotations.keys().size());
101 + putAllSparseAnnotations(newMap, annotations);
102 + }
103 +
104 + putAllSparseAnnotations(newMap, sparseAnnotations);
105 + return new DefaultAnnotations(newMap);
89 } 106 }
90 107
91 - DefaultAnnotations.Builder builder = DefaultAnnotations.builder(); 108 + // adds the key-values contained in sparseAnnotations to
92 - for (String key : annotations.keys()) { 109 + // newMap, if sparseAnnotations had a key tagged for removal,
93 - builder.set(key, annotations.value(key)); 110 + // and corresponding key exist in newMap, entry will be removed.
111 + // if corresponding key does not exist, removal tag will be added to
112 + // the newMap.
113 + private static void putAllSparseAnnotations(
114 + final HashMap<String, String> newMap,
115 + SparseAnnotations sparseAnnotations) {
116 +
117 + for (String key : sparseAnnotations.keys()) {
118 + if (sparseAnnotations.isRemoved(key)) {
119 + if (newMap.containsKey(key)) {
120 + newMap.remove(key);
121 + } else {
122 + newMap.put(key, Builder.REMOVED);
123 + }
124 + } else {
125 + String value = sparseAnnotations.value(key);
126 + newMap.put(key, value);
127 + }
94 } 128 }
95 - return merge(builder.build(), sparseAnnotations);
96 } 129 }
97 130
98 @Override 131 @Override
99 public Set<String> keys() { 132 public Set<String> keys() {
100 - // TODO: unmodifiable to be removed after switching to ImmutableMap;
101 return Collections.unmodifiableSet(map.keySet()); 133 return Collections.unmodifiableSet(map.keySet());
102 } 134 }
103 135
...@@ -115,7 +147,7 @@ public final class DefaultAnnotations implements SparseAnnotations { ...@@ -115,7 +147,7 @@ public final class DefaultAnnotations implements SparseAnnotations {
115 @SuppressWarnings("unchecked") 147 @SuppressWarnings("unchecked")
116 private static HashMap<String, String> copy(Map<String, String> original) { 148 private static HashMap<String, String> copy(Map<String, String> original) {
117 if (original instanceof HashMap) { 149 if (original instanceof HashMap) {
118 - return (HashMap) ((HashMap) original).clone(); 150 + return (HashMap<String, String>) ((HashMap<?, ?>) original).clone();
119 } 151 }
120 throw new IllegalArgumentException("Expecting HashMap instance"); 152 throw new IllegalArgumentException("Expecting HashMap instance");
121 } 153 }
......
...@@ -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.
...@@ -18,7 +19,7 @@ public class DefaultEdgeLink extends DefaultLink implements EdgeLink { ...@@ -18,7 +19,7 @@ public class DefaultEdgeLink extends DefaultLink implements EdgeLink {
18 * @param providerId provider identity 19 * @param providerId provider identity
19 * @param hostPoint host-side connection point 20 * @param hostPoint host-side connection point
20 * @param hostLocation location where host attaches to the network 21 * @param hostLocation location where host attaches to the network
21 - * @param isIngress true to indicated host-to-network direction; false 22 + * @param isIngress true to indicate host-to-network direction; false
22 * for network-to-host direction 23 * for network-to-host direction
23 * @param annotations optional key/value annotations 24 * @param annotations optional key/value annotations
24 */ 25 */
...@@ -42,4 +43,24 @@ public class DefaultEdgeLink extends DefaultLink implements EdgeLink { ...@@ -42,4 +43,24 @@ public class DefaultEdgeLink extends DefaultLink implements EdgeLink {
42 public HostLocation hostLocation() { 43 public HostLocation hostLocation() {
43 return hostLocation; 44 return hostLocation;
44 } 45 }
46 +
47 + /**
48 + * Creates a phantom edge link, to an unspecified end-station. This link
49 + * does not represent any actually discovered link stored in the system.
50 + *
51 + * @param edgePort network edge port
52 + * @param isIngress true to indicate host-to-network direction; false
53 + * for network-to-host direction
54 + * @return new phantom edge link
55 + */
56 + public static DefaultEdgeLink createEdgeLink(ConnectPoint edgePort,
57 + boolean isIngress) {
58 + checkNotNull(edgePort, "Edge port cannot be null");
59 + HostLocation location = (edgePort instanceof HostLocation) ?
60 + (HostLocation) edgePort : new HostLocation(edgePort, 0);
61 + return new DefaultEdgeLink(ProviderId.NONE,
62 + new ConnectPoint(HostId.NONE, PortNumber.P0),
63 + location, isIngress);
64 + }
65 +
45 } 66 }
......
...@@ -10,6 +10,14 @@ import java.net.URI; ...@@ -10,6 +10,14 @@ import java.net.URI;
10 */ 10 */
11 public final class HostId extends ElementId { 11 public final class HostId extends ElementId {
12 12
13 + private static final String NIC = "nic";
14 +
15 + /**
16 + * Represents either no host, or an unspecified host; used for creating
17 + * open ingress/egress edge links.
18 + */
19 + public static final HostId NONE = hostId(NIC + ":none-0");
20 +
13 // Public construction is prohibited 21 // Public construction is prohibited
14 private HostId(URI uri) { 22 private HostId(URI uri) {
15 super(uri); 23 super(uri);
...@@ -43,8 +51,7 @@ public final class HostId extends ElementId { ...@@ -43,8 +51,7 @@ public final class HostId extends ElementId {
43 * @return host identifier 51 * @return host identifier
44 */ 52 */
45 public static HostId hostId(MacAddress mac, VlanId vlanId) { 53 public static HostId hostId(MacAddress mac, VlanId vlanId) {
46 - // FIXME: use more efficient means of encoding 54 + return hostId(NIC + ":" + mac + "-" + vlanId);
47 - return hostId("nic" + ":" + mac + "-" + vlanId);
48 } 55 }
49 56
50 /** 57 /**
......
...@@ -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 *
......
...@@ -6,6 +6,7 @@ import com.google.common.base.MoreObjects; ...@@ -6,6 +6,7 @@ import com.google.common.base.MoreObjects;
6 6
7 // TODO Consider renaming. 7 // TODO Consider renaming.
8 // it's an identifier for a Link, but it's not ElementId, so not using LinkId. 8 // it's an identifier for a Link, but it's not ElementId, so not using LinkId.
9 +
9 /** 10 /**
10 * Immutable representation of a link identity. 11 * Immutable representation of a link identity.
11 */ 12 */
...@@ -43,6 +44,15 @@ public class LinkKey { ...@@ -43,6 +44,15 @@ public class LinkKey {
43 this.dst = dst; 44 this.dst = dst;
44 } 45 }
45 46
47 + /**
48 + * Creates a link identifier for the specified link.
49 + *
50 + * @param link link descriptor
51 + */
52 + public LinkKey(Link link) {
53 + this(link.src(), link.dst());
54 + }
55 +
46 @Override 56 @Override
47 public int hashCode() { 57 public int hashCode() {
48 return Objects.hash(src(), dst); 58 return Objects.hash(src(), dst);
......
...@@ -9,6 +9,8 @@ import com.google.common.primitives.UnsignedLongs; ...@@ -9,6 +9,8 @@ import com.google.common.primitives.UnsignedLongs;
9 */ 9 */
10 public final class PortNumber { 10 public final class PortNumber {
11 11
12 + public static final PortNumber P0 = portNumber(0);
13 +
12 // TODO: revisit the max and the logical port value assignments 14 // TODO: revisit the max and the logical port value assignments
13 15
14 private static final long MAX_NUMBER = (2L * Integer.MAX_VALUE) + 1; 16 private static final long MAX_NUMBER = (2L * Integer.MAX_VALUE) + 1;
......
...@@ -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 }
......
1 +package org.onlab.onos.net.flow;
2 +
3 +public class CompletedBatchOperation {
4 +
5 +
6 +}
1 +package org.onlab.onos.net.flow;
2 +
3 +import static com.google.common.base.MoreObjects.toStringHelper;
4 +import static org.slf4j.LoggerFactory.getLogger;
5 +
6 +import org.onlab.onos.net.DeviceId;
7 +import org.slf4j.Logger;
8 +
9 +public class DefaultFlowEntry extends DefaultFlowRule implements FlowEntry {
10 +
11 + private final Logger log = getLogger(getClass());
12 +
13 + private long life;
14 + private long packets;
15 + private long bytes;
16 + private FlowEntryState state;
17 +
18 + private long lastSeen = -1;
19 +
20 +
21 + public DefaultFlowEntry(DeviceId deviceId, TrafficSelector selector,
22 + TrafficTreatment treatment, int priority, FlowEntryState state,
23 + long life, long packets, long bytes, long flowId,
24 + int timeout) {
25 + super(deviceId, selector, treatment, priority, flowId, timeout);
26 + this.state = state;
27 + this.life = life;
28 + this.packets = packets;
29 + this.bytes = bytes;
30 + this.lastSeen = System.currentTimeMillis();
31 + }
32 +
33 + public DefaultFlowEntry(FlowRule rule, FlowEntryState state,
34 + long life, long packets, long bytes) {
35 + super(rule);
36 + this.state = state;
37 + this.life = life;
38 + this.packets = packets;
39 + this.bytes = bytes;
40 + this.lastSeen = System.currentTimeMillis();
41 + }
42 +
43 + public DefaultFlowEntry(FlowRule rule) {
44 + super(rule);
45 + this.state = FlowEntryState.PENDING_ADD;
46 + this.life = 0;
47 + this.packets = 0;
48 + this.bytes = 0;
49 + this.lastSeen = System.currentTimeMillis();
50 + }
51 +
52 + @Override
53 + public long life() {
54 + return life;
55 + }
56 +
57 + @Override
58 + public long packets() {
59 + return packets;
60 + }
61 +
62 + @Override
63 + public long bytes() {
64 + return bytes;
65 + }
66 +
67 + @Override
68 + public FlowEntryState state() {
69 + return this.state;
70 + }
71 +
72 + @Override
73 + public long lastSeen() {
74 + return lastSeen;
75 + }
76 +
77 + @Override
78 + public void setLastSeen() {
79 + this.lastSeen = System.currentTimeMillis();
80 + }
81 +
82 + @Override
83 + public void setState(FlowEntryState newState) {
84 + this.state = newState;
85 + }
86 +
87 + @Override
88 + public void setLife(long life) {
89 + this.life = life;
90 + }
91 +
92 + @Override
93 + public void setPackets(long packets) {
94 + this.packets = packets;
95 + }
96 +
97 + @Override
98 + public void setBytes(long bytes) {
99 + this.bytes = bytes;
100 + }
101 +
102 + @Override
103 + public String toString() {
104 + return toStringHelper(this)
105 + .add("rule", super.toString())
106 + .add("state", state)
107 + .toString();
108 + }
109 +
110 +
111 +}
...@@ -18,10 +18,6 @@ public class DefaultFlowRule implements FlowRule { ...@@ -18,10 +18,6 @@ public class DefaultFlowRule implements FlowRule {
18 private final TrafficSelector selector; 18 private final TrafficSelector selector;
19 private final TrafficTreatment treatment; 19 private final TrafficTreatment treatment;
20 private final long created; 20 private final long created;
21 - private final long life;
22 - private final long packets;
23 - private final long bytes;
24 - private final FlowRuleState state;
25 21
26 private final FlowId id; 22 private final FlowId id;
27 23
...@@ -29,73 +25,50 @@ public class DefaultFlowRule implements FlowRule { ...@@ -29,73 +25,50 @@ public class DefaultFlowRule implements FlowRule {
29 25
30 private final int timeout; 26 private final int timeout;
31 27
28 +
32 public DefaultFlowRule(DeviceId deviceId, TrafficSelector selector, 29 public DefaultFlowRule(DeviceId deviceId, TrafficSelector selector,
33 - TrafficTreatment treatment, int priority, FlowRuleState state, 30 + TrafficTreatment treatment, int priority, long flowId,
34 - long life, long packets, long bytes, long flowId, boolean expired,
35 int timeout) { 31 int timeout) {
36 this.deviceId = deviceId; 32 this.deviceId = deviceId;
37 this.priority = priority; 33 this.priority = priority;
38 this.selector = selector; 34 this.selector = selector;
39 this.treatment = treatment; 35 this.treatment = treatment;
40 - this.state = state; 36 + this.timeout = timeout;
37 + this.created = System.currentTimeMillis();
38 +
41 this.appId = ApplicationId.valueOf((int) (flowId >> 32)); 39 this.appId = ApplicationId.valueOf((int) (flowId >> 32));
42 this.id = FlowId.valueOf(flowId); 40 this.id = FlowId.valueOf(flowId);
43 - this.life = life;
44 - this.packets = packets;
45 - this.bytes = bytes;
46 - this.created = System.currentTimeMillis();
47 - this.timeout = timeout;
48 } 41 }
49 42
50 public DefaultFlowRule(DeviceId deviceId, TrafficSelector selector, 43 public DefaultFlowRule(DeviceId deviceId, TrafficSelector selector,
51 TrafficTreatment treatement, int priority, ApplicationId appId, 44 TrafficTreatment treatement, int priority, ApplicationId appId,
52 int timeout) { 45 int timeout) {
53 - this(deviceId, selector, treatement, priority,
54 - FlowRuleState.CREATED, appId, timeout);
55 - }
56 46
57 - public DefaultFlowRule(FlowRule rule, FlowRuleState state) { 47 + if (priority < FlowRule.MIN_PRIORITY) {
58 - this(rule.deviceId(), rule.selector(), rule.treatment(), 48 + throw new IllegalArgumentException("Priority cannot be less than " + MIN_PRIORITY);
59 - rule.priority(), state, rule.id(), rule.appId(),
60 - rule.timeout());
61 } 49 }
62 50
63 - private DefaultFlowRule(DeviceId deviceId,
64 - TrafficSelector selector, TrafficTreatment treatment,
65 - int priority, FlowRuleState state, ApplicationId appId,
66 - int timeout) {
67 this.deviceId = deviceId; 51 this.deviceId = deviceId;
68 this.priority = priority; 52 this.priority = priority;
69 this.selector = selector; 53 this.selector = selector;
70 - this.treatment = treatment; 54 + this.treatment = treatement;
71 - this.state = state;
72 - this.life = 0;
73 - this.packets = 0;
74 - this.bytes = 0;
75 this.appId = appId; 55 this.appId = appId;
76 -
77 this.timeout = timeout; 56 this.timeout = timeout;
57 + this.created = System.currentTimeMillis();
78 58
79 this.id = FlowId.valueOf((((long) appId().id()) << 32) | (this.hash() & 0xffffffffL)); 59 this.id = FlowId.valueOf((((long) appId().id()) << 32) | (this.hash() & 0xffffffffL));
80 - this.created = System.currentTimeMillis();
81 } 60 }
82 61
83 - private DefaultFlowRule(DeviceId deviceId, 62 + public DefaultFlowRule(FlowRule rule) {
84 - TrafficSelector selector, TrafficTreatment treatment, 63 + this.deviceId = rule.deviceId();
85 - int priority, FlowRuleState state, FlowId flowId, ApplicationId appId, 64 + this.priority = rule.priority();
86 - int timeout) { 65 + this.selector = rule.selector();
87 - this.deviceId = deviceId; 66 + this.treatment = rule.treatment();
88 - this.priority = priority; 67 + this.appId = rule.appId();
89 - this.selector = selector; 68 + this.id = rule.id();
90 - this.treatment = treatment; 69 + this.timeout = rule.timeout();
91 - this.state = state;
92 - this.life = 0;
93 - this.packets = 0;
94 - this.bytes = 0;
95 - this.appId = appId;
96 - this.id = flowId;
97 - this.timeout = timeout;
98 this.created = System.currentTimeMillis(); 70 this.created = System.currentTimeMillis();
71 +
99 } 72 }
100 73
101 74
...@@ -129,26 +102,6 @@ public class DefaultFlowRule implements FlowRule { ...@@ -129,26 +102,6 @@ public class DefaultFlowRule implements FlowRule {
129 return treatment; 102 return treatment;
130 } 103 }
131 104
132 - @Override
133 - public long lifeMillis() {
134 - return life;
135 - }
136 -
137 - @Override
138 - public long packets() {
139 - return packets;
140 - }
141 -
142 - @Override
143 - public long bytes() {
144 - return bytes;
145 - }
146 -
147 - @Override
148 - public FlowRuleState state() {
149 - return this.state;
150 - }
151 -
152 105
153 @Override 106 @Override
154 /* 107 /*
...@@ -162,7 +115,7 @@ public class DefaultFlowRule implements FlowRule { ...@@ -162,7 +115,7 @@ public class DefaultFlowRule implements FlowRule {
162 } 115 }
163 116
164 public int hash() { 117 public int hash() {
165 - return Objects.hash(deviceId, selector, id); 118 + return Objects.hash(deviceId, selector, treatment);
166 } 119 }
167 120
168 @Override 121 @Override
...@@ -179,7 +132,7 @@ public class DefaultFlowRule implements FlowRule { ...@@ -179,7 +132,7 @@ public class DefaultFlowRule implements FlowRule {
179 if (obj instanceof DefaultFlowRule) { 132 if (obj instanceof DefaultFlowRule) {
180 DefaultFlowRule that = (DefaultFlowRule) obj; 133 DefaultFlowRule that = (DefaultFlowRule) obj;
181 return Objects.equals(deviceId, that.deviceId) && 134 return Objects.equals(deviceId, that.deviceId) &&
182 - //Objects.equals(id, that.id) && 135 + Objects.equals(id, that.id) &&
183 Objects.equals(priority, that.priority) && 136 Objects.equals(priority, that.priority) &&
184 Objects.equals(selector, that.selector); 137 Objects.equals(selector, that.selector);
185 138
...@@ -190,19 +143,18 @@ public class DefaultFlowRule implements FlowRule { ...@@ -190,19 +143,18 @@ public class DefaultFlowRule implements FlowRule {
190 @Override 143 @Override
191 public String toString() { 144 public String toString() {
192 return toStringHelper(this) 145 return toStringHelper(this)
193 - .add("id", id) 146 + .add("id", Long.toHexString(id.value()))
194 .add("deviceId", deviceId) 147 .add("deviceId", deviceId)
195 .add("priority", priority) 148 .add("priority", priority)
196 .add("selector", selector.criteria()) 149 .add("selector", selector.criteria())
197 .add("treatment", treatment == null ? "N/A" : treatment.instructions()) 150 .add("treatment", treatment == null ? "N/A" : treatment.instructions())
198 .add("created", created) 151 .add("created", created)
199 - .add("state", state)
200 .toString(); 152 .toString();
201 } 153 }
202 154
203 @Override 155 @Override
204 public int timeout() { 156 public int timeout() {
205 - return timeout > MAX_TIMEOUT ? MAX_TIMEOUT : this.timeout; 157 + return timeout;
206 } 158 }
207 159
208 } 160 }
......
1 package org.onlab.onos.net.flow; 1 package org.onlab.onos.net.flow;
2 2
3 -import static org.slf4j.LoggerFactory.getLogger; 3 +import com.google.common.collect.ImmutableSet;
4 -
5 -import java.util.Collections;
6 -import java.util.HashSet;
7 -import java.util.Objects;
8 -import java.util.Set;
9 -
10 import org.onlab.onos.net.PortNumber; 4 import org.onlab.onos.net.PortNumber;
11 import org.onlab.onos.net.flow.criteria.Criteria; 5 import org.onlab.onos.net.flow.criteria.Criteria;
12 import org.onlab.onos.net.flow.criteria.Criterion; 6 import org.onlab.onos.net.flow.criteria.Criterion;
13 import org.onlab.packet.IpPrefix; 7 import org.onlab.packet.IpPrefix;
14 import org.onlab.packet.MacAddress; 8 import org.onlab.packet.MacAddress;
15 import org.onlab.packet.VlanId; 9 import org.onlab.packet.VlanId;
16 -import org.slf4j.Logger;
17 10
11 +import java.util.Collections;
12 +import java.util.HashMap;
13 +import java.util.Map;
14 +import java.util.Objects;
15 +import java.util.Set;
16 +
17 +/**
18 + * Default traffic selector implementation.
19 + */
18 public final class DefaultTrafficSelector implements TrafficSelector { 20 public final class DefaultTrafficSelector implements TrafficSelector {
19 21
20 - private final Set<Criterion> selector; 22 + private final Set<Criterion> criteria;
21 23
22 - private DefaultTrafficSelector(Set<Criterion> selector) { 24 + /**
23 - this.selector = Collections.unmodifiableSet(selector); 25 + * Creates a new traffic selector with the specified criteria.
26 + *
27 + * @param criteria criteria
28 + */
29 + private DefaultTrafficSelector(Set<Criterion> criteria) {
30 + this.criteria = Collections.unmodifiableSet(criteria);
24 } 31 }
25 32
26 @Override 33 @Override
27 public Set<Criterion> criteria() { 34 public Set<Criterion> criteria() {
28 - return selector; 35 + return criteria;
29 } 36 }
30 37
31 @Override 38 @Override
32 public int hashCode() { 39 public int hashCode() {
33 - return Objects.hash(selector); 40 + return Objects.hash(criteria);
34 } 41 }
35 42
36 @Override 43 @Override
...@@ -40,23 +47,50 @@ public final class DefaultTrafficSelector implements TrafficSelector { ...@@ -40,23 +47,50 @@ public final class DefaultTrafficSelector implements TrafficSelector {
40 } 47 }
41 if (obj instanceof DefaultTrafficSelector) { 48 if (obj instanceof DefaultTrafficSelector) {
42 DefaultTrafficSelector that = (DefaultTrafficSelector) obj; 49 DefaultTrafficSelector that = (DefaultTrafficSelector) obj;
43 - return Objects.equals(selector, that.selector); 50 + return Objects.equals(criteria, that.criteria);
44 51
45 } 52 }
46 return false; 53 return false;
47 } 54 }
48 55
56 + /**
57 + * Returns a new traffic selector builder.
58 + *
59 + * @return traffic selector builder
60 + */
61 + public static TrafficSelector.Builder builder() {
62 + return new Builder();
63 + }
49 64
65 + /**
66 + * Returns a new traffic selector builder primed to produce entities
67 + * patterned after the supplied selector.
68 + *
69 + * @return traffic selector builder
70 + */
71 + public static TrafficSelector.Builder builder(TrafficSelector selector) {
72 + return new Builder(selector);
73 + }
50 74
51 - public static class Builder implements TrafficSelector.Builder { 75 + /**
76 + * Builder of traffic selector entities.
77 + */
78 + public static final class Builder implements TrafficSelector.Builder {
52 79
53 - private final Logger log = getLogger(getClass()); 80 + private final Map<Criterion.Type, Criterion> selector = new HashMap<>();
54 81
55 - private final Set<Criterion> selector = new HashSet<>(); 82 + private Builder() {
83 + }
84 +
85 + private Builder(TrafficSelector selector) {
86 + for (Criterion c : selector.criteria()) {
87 + add(c);
88 + }
89 + }
56 90
57 @Override 91 @Override
58 public Builder add(Criterion criterion) { 92 public Builder add(Criterion criterion) {
59 - selector.add(criterion); 93 + selector.put(criterion.type(), criterion);
60 return this; 94 return this;
61 } 95 }
62 96
...@@ -107,7 +141,7 @@ public final class DefaultTrafficSelector implements TrafficSelector { ...@@ -107,7 +141,7 @@ public final class DefaultTrafficSelector implements TrafficSelector {
107 141
108 @Override 142 @Override
109 public TrafficSelector build() { 143 public TrafficSelector build() {
110 - return new DefaultTrafficSelector(selector); 144 + return new DefaultTrafficSelector(ImmutableSet.copyOf(selector.values()));
111 } 145 }
112 146
113 } 147 }
......
...@@ -5,6 +5,7 @@ import static org.slf4j.LoggerFactory.getLogger; ...@@ -5,6 +5,7 @@ import static org.slf4j.LoggerFactory.getLogger;
5 import java.util.Collections; 5 import java.util.Collections;
6 import java.util.LinkedList; 6 import java.util.LinkedList;
7 import java.util.List; 7 import java.util.List;
8 +import java.util.Objects;
8 9
9 import org.onlab.onos.net.PortNumber; 10 import org.onlab.onos.net.PortNumber;
10 import org.onlab.onos.net.flow.instructions.Instruction; 11 import org.onlab.onos.net.flow.instructions.Instruction;
...@@ -14,10 +15,18 @@ import org.onlab.packet.MacAddress; ...@@ -14,10 +15,18 @@ import org.onlab.packet.MacAddress;
14 import org.onlab.packet.VlanId; 15 import org.onlab.packet.VlanId;
15 import org.slf4j.Logger; 16 import org.slf4j.Logger;
16 17
18 +/**
19 + * Default traffic treatment implementation.
20 + */
17 public final class DefaultTrafficTreatment implements TrafficTreatment { 21 public final class DefaultTrafficTreatment implements TrafficTreatment {
18 22
19 private final List<Instruction> instructions; 23 private final List<Instruction> instructions;
20 24
25 + /**
26 + * Creates a new traffic treatment from the specified list of instructions.
27 + *
28 + * @param instructions treatment instructions
29 + */
21 private DefaultTrafficTreatment(List<Instruction> instructions) { 30 private DefaultTrafficTreatment(List<Instruction> instructions) {
22 this.instructions = Collections.unmodifiableList(instructions); 31 this.instructions = Collections.unmodifiableList(instructions);
23 } 32 }
...@@ -28,12 +37,38 @@ public final class DefaultTrafficTreatment implements TrafficTreatment { ...@@ -28,12 +37,38 @@ public final class DefaultTrafficTreatment implements TrafficTreatment {
28 } 37 }
29 38
30 /** 39 /**
31 - * Builds a list of treatments following the following order. 40 + * Returns a new traffic treatment builder.
32 - * Modifications -> Group -> Output (including drop)
33 * 41 *
42 + * @return traffic treatment builder
34 */ 43 */
44 + public static TrafficTreatment.Builder builder() {
45 + return new Builder();
46 + }
47 +
48 + //FIXME: Order of instructions may affect hashcode
49 + @Override
50 + public int hashCode() {
51 + return Objects.hash(instructions);
52 + }
35 53
36 - public static class Builder implements TrafficTreatment.Builder { 54 + @Override
55 + public boolean equals(Object obj) {
56 + if (this == obj) {
57 + return true;
58 + }
59 + if (obj instanceof DefaultTrafficTreatment) {
60 + DefaultTrafficTreatment that = (DefaultTrafficTreatment) obj;
61 + return Objects.equals(instructions, that.instructions);
62 +
63 + }
64 + return false;
65 + }
66 +
67 + /**
68 + * Builds a list of treatments following the following order.
69 + * Modifications -> Group -> Output (including drop)
70 + */
71 + public static final class Builder implements TrafficTreatment.Builder {
37 72
38 private final Logger log = getLogger(getClass()); 73 private final Logger log = getLogger(getClass());
39 74
...@@ -47,6 +82,11 @@ public final class DefaultTrafficTreatment implements TrafficTreatment { ...@@ -47,6 +82,11 @@ public final class DefaultTrafficTreatment implements TrafficTreatment {
47 // TODO: should be a list of instructions based on modification objects 82 // TODO: should be a list of instructions based on modification objects
48 List<Instruction> modifications = new LinkedList<>(); 83 List<Instruction> modifications = new LinkedList<>();
49 84
85 + // Creates a new builder
86 + private Builder() {
87 + }
88 +
89 + @Override
50 public Builder add(Instruction instruction) { 90 public Builder add(Instruction instruction) {
51 if (drop) { 91 if (drop) {
52 return this; 92 return this;
......
1 +package org.onlab.onos.net.flow;
2 +
3 +
4 +/**
5 + * Represents a generalized match &amp; action pair to be applied to
6 + * an infrastucture device.
7 + */
8 +public interface FlowEntry extends FlowRule {
9 +
10 +
11 + public enum FlowEntryState {
12 +
13 + /**
14 + * Indicates that this rule has been submitted for addition.
15 + * Not necessarily in the flow table.
16 + */
17 + PENDING_ADD,
18 +
19 + /**
20 + * Rule has been added which means it is in the flow table.
21 + */
22 + ADDED,
23 +
24 + /**
25 + * Flow has been marked for removal, might still be in flow table.
26 + */
27 + PENDING_REMOVE,
28 +
29 + /**
30 + * Flow has been removed from flow table and can be purged.
31 + */
32 + REMOVED
33 + }
34 +
35 + /**
36 + * Returns the flow entry state.
37 + *
38 + * @return flow entry state
39 + */
40 + FlowEntryState state();
41 +
42 + /**
43 + * Returns the number of milliseconds this flow rule has been applied.
44 + *
45 + * @return number of millis
46 + */
47 + long life();
48 +
49 + /**
50 + * Returns the number of packets this flow rule has matched.
51 + *
52 + * @return number of packets
53 + */
54 + long packets();
55 +
56 + /**
57 + * Returns the number of bytes this flow rule has matched.
58 + *
59 + * @return number of bytes
60 + */
61 + long bytes();
62 +
63 + /**
64 + * When this flow entry was last deemed active.
65 + * @return epoch time of last activity
66 + */
67 + long lastSeen();
68 +
69 + /**
70 + * Sets the last active epoch time.
71 + */
72 + void setLastSeen();
73 +
74 + /**
75 + * Sets the new state for this entry.
76 + * @param newState new flow entry state.
77 + */
78 + void setState(FlowEntryState newState);
79 +
80 + /**
81 + * Sets how long this entry has been entered in the system.
82 + * @param life epoch time
83 + */
84 + void setLife(long life);
85 +
86 + /**
87 + * Number of packets seen by this entry.
88 + * @param packets a long value
89 + */
90 + void setPackets(long packets);
91 +
92 + /**
93 + * Number of bytes seen by this rule.
94 + * @param bytes a long value
95 + */
96 + void setBytes(long bytes);
97 +
98 +}
...@@ -26,6 +26,9 @@ public final class FlowId { ...@@ -26,6 +26,9 @@ public final class FlowId {
26 if (this == obj) { 26 if (this == obj) {
27 return true; 27 return true;
28 } 28 }
29 + if (obj == null) {
30 + return false;
31 + }
29 if (obj.getClass() == this.getClass()) { 32 if (obj.getClass() == this.getClass()) {
30 FlowId that = (FlowId) obj; 33 FlowId that = (FlowId) obj;
31 return Objects.equal(this.flowid, that.flowid); 34 return Objects.equal(this.flowid, that.flowid);
......
...@@ -2,49 +2,16 @@ package org.onlab.onos.net.flow; ...@@ -2,49 +2,16 @@ package org.onlab.onos.net.flow;
2 2
3 import org.onlab.onos.ApplicationId; 3 import org.onlab.onos.ApplicationId;
4 import org.onlab.onos.net.DeviceId; 4 import org.onlab.onos.net.DeviceId;
5 +import org.onlab.onos.net.intent.BatchOperationTarget;
5 6
6 /** 7 /**
7 * Represents a generalized match &amp; action pair to be applied to 8 * Represents a generalized match &amp; action pair to be applied to
8 * an infrastucture device. 9 * an infrastucture device.
9 */ 10 */
10 -public interface FlowRule { 11 +public interface FlowRule extends BatchOperationTarget {
11 12
12 static final int MAX_TIMEOUT = 60; 13 static final int MAX_TIMEOUT = 60;
13 - 14 + static final int MIN_PRIORITY = 0;
14 - public enum FlowRuleState {
15 - /**
16 - * Indicates that this rule has been created.
17 - */
18 - CREATED,
19 -
20 - /**
21 - * Indicates that this rule has been submitted for addition.
22 - * Not necessarily in the flow table.
23 - */
24 - PENDING_ADD,
25 -
26 - /**
27 - * Rule has been added which means it is in the flow table.
28 - */
29 - ADDED,
30 -
31 - /**
32 - * Flow has been marked for removal, might still be in flow table.
33 - */
34 - PENDING_REMOVE,
35 -
36 - /**
37 - * Flow has been removed from flow table and can be purged.
38 - */
39 - REMOVED
40 - }
41 -
42 - /**
43 - * Returns the flow rule state.
44 - *
45 - * @return flow rule state
46 - */
47 - FlowRuleState state();
48 15
49 //TODO: build cookie value 16 //TODO: build cookie value
50 /** 17 /**
...@@ -92,27 +59,6 @@ public interface FlowRule { ...@@ -92,27 +59,6 @@ public interface FlowRule {
92 TrafficTreatment treatment(); 59 TrafficTreatment treatment();
93 60
94 /** 61 /**
95 - * Returns the number of milliseconds this flow rule has been applied.
96 - *
97 - * @return number of millis
98 - */
99 - long lifeMillis();
100 -
101 - /**
102 - * Returns the number of packets this flow rule has matched.
103 - *
104 - * @return number of packets
105 - */
106 - long packets();
107 -
108 - /**
109 - * Returns the number of bytes this flow rule has matched.
110 - *
111 - * @return number of bytes
112 - */
113 - long bytes();
114 -
115 - /**
116 * Returns the timeout for this flow requested by an application. 62 * Returns the timeout for this flow requested by an application.
117 * @return integer value of the timeout 63 * @return integer value of the timeout
118 */ 64 */
......
1 +package org.onlab.onos.net.flow;
2 +
3 +import org.onlab.onos.net.flow.FlowRuleBatchEntry.FlowRuleOperation;
4 +import org.onlab.onos.net.intent.BatchOperationEntry;
5 +
6 +
7 +public class FlowRuleBatchEntry
8 + extends BatchOperationEntry<FlowRuleOperation, FlowRule> {
9 +
10 + public FlowRuleBatchEntry(FlowRuleOperation operator, FlowRule target) {
11 + super(operator, target);
12 + }
13 +
14 + public enum FlowRuleOperation {
15 + ADD,
16 + REMOVE,
17 + MODIFY
18 + }
19 +
20 +}
1 +package org.onlab.onos.net.flow;
2 +
3 +import java.util.Collection;
4 +
5 +import org.onlab.onos.net.intent.BatchOperation;
6 +
7 +public class FlowRuleBatchOperation
8 + extends BatchOperation<FlowRuleBatchEntry> {
9 +
10 + public FlowRuleBatchOperation(Collection<FlowRuleBatchEntry> operations) {
11 + super(operations);
12 + }
13 +}
1 package org.onlab.onos.net.flow; 1 package org.onlab.onos.net.flow;
2 2
3 +import java.util.concurrent.Future;
4 +
3 import org.onlab.onos.ApplicationId; 5 import org.onlab.onos.ApplicationId;
6 +import org.onlab.onos.net.intent.BatchOperation;
4 import org.onlab.onos.net.provider.Provider; 7 import org.onlab.onos.net.provider.Provider;
5 8
6 /** 9 /**
...@@ -34,4 +37,6 @@ public interface FlowRuleProvider extends Provider { ...@@ -34,4 +37,6 @@ public interface FlowRuleProvider extends Provider {
34 */ 37 */
35 void removeRulesById(ApplicationId id, FlowRule... flowRules); 38 void removeRulesById(ApplicationId id, FlowRule... flowRules);
36 39
40 + Future<Void> executeBatch(BatchOperation<FlowRuleBatchEntry> batch);
41 +
37 } 42 }
......
...@@ -14,7 +14,7 @@ public interface FlowRuleProviderService extends ProviderService<FlowRuleProvide ...@@ -14,7 +14,7 @@ public interface FlowRuleProviderService extends ProviderService<FlowRuleProvide
14 * 14 *
15 * @param flowRule information about the removed flow 15 * @param flowRule information about the removed flow
16 */ 16 */
17 - void flowRemoved(FlowRule flowRule); 17 + void flowRemoved(FlowEntry flowEntry);
18 18
19 /** 19 /**
20 * Pushes the collection of flow entries currently applied on the given 20 * Pushes the collection of flow entries currently applied on the given
...@@ -22,7 +22,7 @@ public interface FlowRuleProviderService extends ProviderService<FlowRuleProvide ...@@ -22,7 +22,7 @@ public interface FlowRuleProviderService extends ProviderService<FlowRuleProvide
22 * 22 *
23 * @param flowRules collection of flow rules 23 * @param flowRules collection of flow rules
24 */ 24 */
25 - void pushFlowMetrics(DeviceId deviceId, Iterable<FlowRule> flowRules); 25 + void pushFlowMetrics(DeviceId deviceId, Iterable<FlowEntry> flowEntries);
26 26
27 27
28 28
......
1 package org.onlab.onos.net.flow; 1 package org.onlab.onos.net.flow;
2 2
3 +import java.util.concurrent.Future;
4 +
3 import org.onlab.onos.ApplicationId; 5 import org.onlab.onos.ApplicationId;
4 import org.onlab.onos.net.DeviceId; 6 import org.onlab.onos.net.DeviceId;
5 7
...@@ -13,6 +15,13 @@ import org.onlab.onos.net.DeviceId; ...@@ -13,6 +15,13 @@ import org.onlab.onos.net.DeviceId;
13 public interface FlowRuleService { 15 public interface FlowRuleService {
14 16
15 /** 17 /**
18 + * Returns the number of flow rules in the system.
19 + *
20 + * @return flow rule count
21 + */
22 + int getFlowRuleCount();
23 +
24 + /**
16 * Returns the collection of flow entries applied on the specified device. 25 * Returns the collection of flow entries applied on the specified device.
17 * This will include flow rules which may not yet have been applied to 26 * This will include flow rules which may not yet have been applied to
18 * the device. 27 * the device.
...@@ -20,7 +29,7 @@ public interface FlowRuleService { ...@@ -20,7 +29,7 @@ public interface FlowRuleService {
20 * @param deviceId device identifier 29 * @param deviceId device identifier
21 * @return collection of flow rules 30 * @return collection of flow rules
22 */ 31 */
23 - Iterable<FlowRule> getFlowEntries(DeviceId deviceId); 32 + Iterable<FlowEntry> getFlowEntries(DeviceId deviceId);
24 33
25 // TODO: add createFlowRule factory method and execute operations method 34 // TODO: add createFlowRule factory method and execute operations method
26 35
...@@ -60,6 +69,13 @@ public interface FlowRuleService { ...@@ -60,6 +69,13 @@ public interface FlowRuleService {
60 Iterable<FlowRule> getFlowRulesById(ApplicationId id); 69 Iterable<FlowRule> getFlowRulesById(ApplicationId id);
61 70
62 /** 71 /**
72 + * Applies a batch operation of FlowRules.
73 + *
74 + * @return future indicating the state of the batch operation
75 + */
76 + Future<CompletedBatchOperation> applyBatch(FlowRuleBatchOperation batch);
77 +
78 + /**
63 * Adds the specified flow rule listener. 79 * Adds the specified flow rule listener.
64 * 80 *
65 * @param listener flow rule listener 81 * @param listener flow rule listener
...@@ -72,7 +88,4 @@ public interface FlowRuleService { ...@@ -72,7 +88,4 @@ public interface FlowRuleService {
72 * @param listener flow rule listener 88 * @param listener flow rule listener
73 */ 89 */
74 void removeListener(FlowRuleListener listener); 90 void removeListener(FlowRuleListener listener);
75 -
76 -
77 -
78 } 91 }
......
...@@ -10,11 +10,19 @@ import org.onlab.onos.store.Store; ...@@ -10,11 +10,19 @@ import org.onlab.onos.store.Store;
10 public interface FlowRuleStore extends Store<FlowRuleEvent, FlowRuleStoreDelegate> { 10 public interface FlowRuleStore extends Store<FlowRuleEvent, FlowRuleStoreDelegate> {
11 11
12 /** 12 /**
13 + * Returns the number of flow rule in the store.
14 + *
15 + * @return number of flow rules
16 + */
17 + int getFlowRuleCount();
18 +
19 + /**
13 * Returns the stored flow. 20 * Returns the stored flow.
21 + *
14 * @param rule the rule to look for 22 * @param rule the rule to look for
15 * @return a flow rule 23 * @return a flow rule
16 */ 24 */
17 - FlowRule getFlowRule(FlowRule rule); 25 + FlowEntry getFlowEntry(FlowRule rule);
18 26
19 /** 27 /**
20 * Returns the flow entries associated with a device. 28 * Returns the flow entries associated with a device.
...@@ -22,7 +30,7 @@ public interface FlowRuleStore extends Store<FlowRuleEvent, FlowRuleStoreDelegat ...@@ -22,7 +30,7 @@ public interface FlowRuleStore extends Store<FlowRuleEvent, FlowRuleStoreDelegat
22 * @param deviceId the device ID 30 * @param deviceId the device ID
23 * @return the flow entries 31 * @return the flow entries
24 */ 32 */
25 - Iterable<FlowRule> getFlowEntries(DeviceId deviceId); 33 + Iterable<FlowEntry> getFlowEntries(DeviceId deviceId);
26 34
27 /** 35 /**
28 * Returns the flow entries associated with an application. 36 * Returns the flow entries associated with an application.
...@@ -30,7 +38,7 @@ public interface FlowRuleStore extends Store<FlowRuleEvent, FlowRuleStoreDelegat ...@@ -30,7 +38,7 @@ public interface FlowRuleStore extends Store<FlowRuleEvent, FlowRuleStoreDelegat
30 * @param appId the application id 38 * @param appId the application id
31 * @return the flow entries 39 * @return the flow entries
32 */ 40 */
33 - Iterable<FlowRule> getFlowEntriesByAppId(ApplicationId appId); 41 + Iterable<FlowRule> getFlowRulesByAppId(ApplicationId appId);
34 42
35 /** 43 /**
36 * Stores a new flow rule without generating events. 44 * Stores a new flow rule without generating events.
...@@ -40,7 +48,8 @@ public interface FlowRuleStore extends Store<FlowRuleEvent, FlowRuleStoreDelegat ...@@ -40,7 +48,8 @@ public interface FlowRuleStore extends Store<FlowRuleEvent, FlowRuleStoreDelegat
40 void storeFlowRule(FlowRule rule); 48 void storeFlowRule(FlowRule rule);
41 49
42 /** 50 /**
43 - * Deletes a flow rule without generating events. 51 + * Marks a flow rule for deletion. Actual deletion will occur
52 + * when the provider indicates that the flow has been removed.
44 * 53 *
45 * @param rule the flow rule to delete 54 * @param rule the flow rule to delete
46 */ 55 */
...@@ -52,12 +61,11 @@ public interface FlowRuleStore extends Store<FlowRuleEvent, FlowRuleStoreDelegat ...@@ -52,12 +61,11 @@ public interface FlowRuleStore extends Store<FlowRuleEvent, FlowRuleStoreDelegat
52 * @param rule the flow rule to add or update 61 * @param rule the flow rule to add or update
53 * @return flow_added event, or null if just an update 62 * @return flow_added event, or null if just an update
54 */ 63 */
55 - FlowRuleEvent addOrUpdateFlowRule(FlowRule rule); 64 + FlowRuleEvent addOrUpdateFlowRule(FlowEntry rule);
56 65
57 /** 66 /**
58 - * @param rule the flow rule to remove 67 + * @param rule the flow entry to remove
59 * @return flow_removed event, or null if nothing removed 68 * @return flow_removed event, or null if nothing removed
60 */ 69 */
61 - FlowRuleEvent removeFlowRule(FlowRule rule); 70 + FlowRuleEvent removeFlowRule(FlowEntry rule);
62 -
63 } 71 }
......
...@@ -3,6 +3,8 @@ package org.onlab.onos.net.flow.instructions; ...@@ -3,6 +3,8 @@ package org.onlab.onos.net.flow.instructions;
3 import static com.google.common.base.MoreObjects.toStringHelper; 3 import static com.google.common.base.MoreObjects.toStringHelper;
4 import static com.google.common.base.Preconditions.checkNotNull; 4 import static com.google.common.base.Preconditions.checkNotNull;
5 5
6 +import java.util.Objects;
7 +
6 import org.onlab.onos.net.PortNumber; 8 import org.onlab.onos.net.PortNumber;
7 import org.onlab.onos.net.flow.instructions.L2ModificationInstruction.L2SubType; 9 import org.onlab.onos.net.flow.instructions.L2ModificationInstruction.L2SubType;
8 import org.onlab.onos.net.flow.instructions.L2ModificationInstruction.ModEtherInstruction; 10 import org.onlab.onos.net.flow.instructions.L2ModificationInstruction.ModEtherInstruction;
...@@ -117,6 +119,24 @@ public final class Instructions { ...@@ -117,6 +119,24 @@ public final class Instructions {
117 return toStringHelper(type()).toString(); 119 return toStringHelper(type()).toString();
118 120
119 } 121 }
122 +
123 + @Override
124 + public int hashCode() {
125 + return Objects.hash(type());
126 + }
127 +
128 + @Override
129 + public boolean equals(Object obj) {
130 + if (this == obj) {
131 + return true;
132 + }
133 + if (obj instanceof DropInstruction) {
134 + DropInstruction that = (DropInstruction) obj;
135 + return Objects.equals(type(), that.type());
136 +
137 + }
138 + return false;
139 + }
120 } 140 }
121 141
122 142
...@@ -140,6 +160,26 @@ public final class Instructions { ...@@ -140,6 +160,26 @@ public final class Instructions {
140 return toStringHelper(type().toString()) 160 return toStringHelper(type().toString())
141 .add("port", port).toString(); 161 .add("port", port).toString();
142 } 162 }
163 +
164 + @Override
165 + public int hashCode() {
166 + return Objects.hash(port, type());
167 + }
168 +
169 + @Override
170 + public boolean equals(Object obj) {
171 + if (this == obj) {
172 + return true;
173 + }
174 + if (obj instanceof OutputInstruction) {
175 + OutputInstruction that = (OutputInstruction) obj;
176 + Objects.equals(port, that.port);
177 +
178 + }
179 + return false;
180 + }
143 } 181 }
144 182
145 } 183 }
184 +
185 +
......
...@@ -2,6 +2,8 @@ package org.onlab.onos.net.flow.instructions; ...@@ -2,6 +2,8 @@ package org.onlab.onos.net.flow.instructions;
2 2
3 import static com.google.common.base.MoreObjects.toStringHelper; 3 import static com.google.common.base.MoreObjects.toStringHelper;
4 4
5 +import java.util.Objects;
6 +
5 import org.onlab.packet.MacAddress; 7 import org.onlab.packet.MacAddress;
6 import org.onlab.packet.VlanId; 8 import org.onlab.packet.VlanId;
7 9
...@@ -74,6 +76,25 @@ public abstract class L2ModificationInstruction implements Instruction { ...@@ -74,6 +76,25 @@ public abstract class L2ModificationInstruction implements Instruction {
74 .add("mac", mac).toString(); 76 .add("mac", mac).toString();
75 } 77 }
76 78
79 + @Override
80 + public int hashCode() {
81 + return Objects.hash(mac, subtype);
82 + }
83 +
84 + @Override
85 + public boolean equals(Object obj) {
86 + if (this == obj) {
87 + return true;
88 + }
89 + if (obj instanceof ModEtherInstruction) {
90 + ModEtherInstruction that = (ModEtherInstruction) obj;
91 + return Objects.equals(mac, that.mac) &&
92 + Objects.equals(subtype, that.subtype);
93 +
94 + }
95 + return false;
96 + }
97 +
77 98
78 } 99 }
79 100
...@@ -103,6 +124,25 @@ public abstract class L2ModificationInstruction implements Instruction { ...@@ -103,6 +124,25 @@ public abstract class L2ModificationInstruction implements Instruction {
103 .add("id", vlanId).toString(); 124 .add("id", vlanId).toString();
104 } 125 }
105 126
127 + @Override
128 + public int hashCode() {
129 + return Objects.hash(vlanId, subtype());
130 + }
131 +
132 + @Override
133 + public boolean equals(Object obj) {
134 + if (this == obj) {
135 + return true;
136 + }
137 + if (obj instanceof ModVlanIdInstruction) {
138 + ModVlanIdInstruction that = (ModVlanIdInstruction) obj;
139 + return Objects.equals(vlanId, that.vlanId);
140 +
141 + }
142 + return false;
143 + }
144 +
145 +
106 } 146 }
107 147
108 /** 148 /**
...@@ -131,6 +171,24 @@ public abstract class L2ModificationInstruction implements Instruction { ...@@ -131,6 +171,24 @@ public abstract class L2ModificationInstruction implements Instruction {
131 .add("pcp", Long.toHexString(vlanPcp)).toString(); 171 .add("pcp", Long.toHexString(vlanPcp)).toString();
132 } 172 }
133 173
174 + @Override
175 + public int hashCode() {
176 + return Objects.hash(vlanPcp, subtype());
177 + }
178 +
179 + @Override
180 + public boolean equals(Object obj) {
181 + if (this == obj) {
182 + return true;
183 + }
184 + if (obj instanceof ModVlanPcpInstruction) {
185 + ModVlanPcpInstruction that = (ModVlanPcpInstruction) obj;
186 + return Objects.equals(vlanPcp, that.vlanPcp);
187 +
188 + }
189 + return false;
190 + }
191 +
134 } 192 }
135 193
136 194
......
...@@ -2,6 +2,8 @@ package org.onlab.onos.net.flow.instructions; ...@@ -2,6 +2,8 @@ package org.onlab.onos.net.flow.instructions;
2 2
3 import static com.google.common.base.MoreObjects.toStringHelper; 3 import static com.google.common.base.MoreObjects.toStringHelper;
4 4
5 +import java.util.Objects;
6 +
5 import org.onlab.packet.IpPrefix; 7 import org.onlab.packet.IpPrefix;
6 8
7 /** 9 /**
...@@ -66,5 +68,23 @@ public abstract class L3ModificationInstruction implements Instruction { ...@@ -66,5 +68,23 @@ public abstract class L3ModificationInstruction implements Instruction {
66 .add("ip", ip).toString(); 68 .add("ip", ip).toString();
67 } 69 }
68 70
71 + @Override
72 + public int hashCode() {
73 + return Objects.hash(ip, subtype());
74 + }
75 +
76 + @Override
77 + public boolean equals(Object obj) {
78 + if (this == obj) {
79 + return true;
80 + }
81 + if (obj instanceof ModIPInstruction) {
82 + ModIPInstruction that = (ModIPInstruction) obj;
83 + return Objects.equals(ip, that.ip);
84 +
85 + }
86 + return false;
87 + }
88 +
69 } 89 }
70 } 90 }
......
...@@ -24,7 +24,7 @@ public abstract class AbstractIntent implements Intent { ...@@ -24,7 +24,7 @@ public abstract class AbstractIntent implements Intent {
24 } 24 }
25 25
26 @Override 26 @Override
27 - public IntentId getId() { 27 + public IntentId id() {
28 return id; 28 return id;
29 } 29 }
30 30
......
...@@ -3,6 +3,7 @@ package org.onlab.onos.net.intent; ...@@ -3,6 +3,7 @@ package org.onlab.onos.net.intent;
3 3
4 import static com.google.common.base.Preconditions.checkNotNull; 4 import static com.google.common.base.Preconditions.checkNotNull;
5 5
6 +import java.util.Collection;
6 import java.util.Collections; 7 import java.util.Collections;
7 import java.util.LinkedList; 8 import java.util.LinkedList;
8 import java.util.List; 9 import java.util.List;
...@@ -12,10 +13,10 @@ import java.util.List; ...@@ -12,10 +13,10 @@ import java.util.List;
12 * 13 *
13 * @param <T> the enum of operators <br> 14 * @param <T> the enum of operators <br>
14 * This enum must be defined in each sub-classes. 15 * This enum must be defined in each sub-classes.
15 - *
16 */ 16 */
17 public abstract class BatchOperation<T extends BatchOperationEntry<?, ?>> { 17 public abstract class BatchOperation<T extends BatchOperationEntry<?, ?>> {
18 - private List<T> ops; 18 +
19 + private final List<T> ops;
19 20
20 /** 21 /**
21 * Creates new {@link BatchOperation} object. 22 * Creates new {@link BatchOperation} object.
...@@ -30,7 +31,7 @@ public abstract class BatchOperation<T extends BatchOperationEntry<?, ?>> { ...@@ -30,7 +31,7 @@ public abstract class BatchOperation<T extends BatchOperationEntry<?, ?>> {
30 * 31 *
31 * @param batchOperations the list of batch operation entries. 32 * @param batchOperations the list of batch operation entries.
32 */ 33 */
33 - public BatchOperation(List<T> batchOperations) { 34 + public BatchOperation(Collection<T> batchOperations) {
34 ops = new LinkedList<>(checkNotNull(batchOperations)); 35 ops = new LinkedList<>(checkNotNull(batchOperations));
35 } 36 }
36 37
...@@ -61,6 +62,10 @@ public abstract class BatchOperation<T extends BatchOperationEntry<?, ?>> { ...@@ -61,6 +62,10 @@ public abstract class BatchOperation<T extends BatchOperationEntry<?, ?>> {
61 62
62 /** 63 /**
63 * Adds an operation. 64 * Adds an operation.
65 + * FIXME: Brian promises that the Intent Framework
66 + * will not modify the batch operation after it has submitted it.
67 + * Ali would prefer immutablity, but trusts brian for better or
68 + * for worse.
64 * 69 *
65 * @param entry the operation to be added 70 * @param entry the operation to be added
66 * @return this object if succeeded, null otherwise 71 * @return this object if succeeded, null otherwise
......
...@@ -15,14 +15,7 @@ public class BatchOperationEntry<T extends Enum<?>, U extends BatchOperationTarg ...@@ -15,14 +15,7 @@ public class BatchOperationEntry<T extends Enum<?>, U extends BatchOperationTarg
15 private final T operator; 15 private final T operator;
16 private final U target; 16 private final U target;
17 17
18 - /** 18 +
19 - * Default constructor for serializer.
20 - */
21 - @Deprecated
22 - protected BatchOperationEntry() {
23 - this.operator = null;
24 - this.target = null;
25 - }
26 19
27 /** 20 /**
28 * Constructs new instance for the entry of the BatchOperation. 21 * Constructs new instance for the entry of the BatchOperation.
......
1 package org.onlab.onos.net.intent; 1 package org.onlab.onos.net.intent;
2 2
3 -import static com.google.common.base.Preconditions.checkNotNull; 3 +import com.google.common.base.Objects;
4 -
5 import org.onlab.onos.net.flow.TrafficSelector; 4 import org.onlab.onos.net.flow.TrafficSelector;
6 import org.onlab.onos.net.flow.TrafficTreatment; 5 import org.onlab.onos.net.flow.TrafficTreatment;
7 6
8 -import com.google.common.base.Objects; 7 +import static com.google.common.base.Preconditions.checkNotNull;
9 8
10 /** 9 /**
11 * Abstraction of connectivity intent for traffic matching some criteria. 10 * Abstraction of connectivity intent for traffic matching some criteria.
...@@ -26,17 +25,18 @@ public abstract class ConnectivityIntent extends AbstractIntent { ...@@ -26,17 +25,18 @@ public abstract class ConnectivityIntent extends AbstractIntent {
26 25
27 /** 26 /**
28 * Creates a connectivity intent that matches on the specified intent 27 * Creates a connectivity intent that matches on the specified intent
29 - * and applies the specified action. 28 + * and applies the specified treatement.
30 * 29 *
31 - * @param id intent identifier 30 + * @param intentId intent identifier
32 - * @param match traffic match 31 + * @param selector traffic selector
33 - * @param action action 32 + * @param treatement treatement
34 - * @throws NullPointerException if the match or action is null 33 + * @throws NullPointerException if the selector or treatement is null
35 */ 34 */
36 - protected ConnectivityIntent(IntentId id, TrafficSelector match, TrafficTreatment action) { 35 + protected ConnectivityIntent(IntentId intentId, TrafficSelector selector,
37 - super(id); 36 + TrafficTreatment treatement) {
38 - this.selector = checkNotNull(match); 37 + super(intentId);
39 - this.treatment = checkNotNull(action); 38 + this.selector = checkNotNull(selector);
39 + this.treatment = checkNotNull(treatement);
40 } 40 }
41 41
42 /** 42 /**
...@@ -53,7 +53,7 @@ public abstract class ConnectivityIntent extends AbstractIntent { ...@@ -53,7 +53,7 @@ public abstract class ConnectivityIntent extends AbstractIntent {
53 * 53 *
54 * @return traffic match 54 * @return traffic match
55 */ 55 */
56 - public TrafficSelector getTrafficSelector() { 56 + public TrafficSelector selector() {
57 return selector; 57 return selector;
58 } 58 }
59 59
...@@ -62,7 +62,7 @@ public abstract class ConnectivityIntent extends AbstractIntent { ...@@ -62,7 +62,7 @@ public abstract class ConnectivityIntent extends AbstractIntent {
62 * 62 *
63 * @return applied action 63 * @return applied action
64 */ 64 */
65 - public TrafficTreatment getTrafficTreatment() { 65 + public TrafficTreatment treatment() {
66 return treatment; 66 return treatment;
67 } 67 }
68 68
......
1 +package org.onlab.onos.net.intent;
2 +
3 +import com.google.common.base.MoreObjects;
4 +import org.onlab.onos.net.HostId;
5 +import org.onlab.onos.net.flow.TrafficSelector;
6 +import org.onlab.onos.net.flow.TrafficTreatment;
7 +
8 +import java.util.Objects;
9 +
10 +import static com.google.common.base.Preconditions.checkNotNull;
11 +
12 +/**
13 + * Abstraction of end-station to end-station bidirectional connectivity.
14 + */
15 +public class HostToHostIntent extends ConnectivityIntent {
16 +
17 + private final HostId one;
18 + private final HostId two;
19 +
20 + /**
21 + * Creates a new point-to-point intent with the supplied ingress/egress
22 + * ports.
23 + *
24 + * @param intentId intent identifier
25 + * @param one first host
26 + * @param two second host
27 + * @param selector action
28 + * @param treatment ingress port
29 + * @throws NullPointerException if {@code ingressPort} or {@code egressPort}
30 + * is null.
31 + */
32 + public HostToHostIntent(IntentId intentId, HostId one, HostId two,
33 + TrafficSelector selector,
34 + TrafficTreatment treatment) {
35 + super(intentId, selector, treatment);
36 + this.one = checkNotNull(one);
37 + this.two = checkNotNull(two);
38 + }
39 +
40 + /**
41 + * Returns identifier of the first host.
42 + *
43 + * @return first host identifier
44 + */
45 + public HostId one() {
46 + return one;
47 + }
48 +
49 + /**
50 + * Returns identifier of the second host.
51 + *
52 + * @return second host identifier
53 + */
54 + public HostId two() {
55 + return two;
56 + }
57 +
58 + @Override
59 + public boolean equals(Object o) {
60 + if (this == o) {
61 + return true;
62 + }
63 + if (o == null || getClass() != o.getClass()) {
64 + return false;
65 + }
66 + if (!super.equals(o)) {
67 + return false;
68 + }
69 +
70 + HostToHostIntent that = (HostToHostIntent) o;
71 + return Objects.equals(this.one, that.one)
72 + && Objects.equals(this.two, that.two);
73 + }
74 +
75 + @Override
76 + public int hashCode() {
77 + return Objects.hash(super.hashCode(), one, two);
78 + }
79 +
80 + @Override
81 + public String toString() {
82 + return MoreObjects.toStringHelper(getClass())
83 + .add("id", id())
84 + .add("selector", selector())
85 + .add("treatment", treatment())
86 + .add("one", one)
87 + .add("two", two)
88 + .toString();
89 + }
90 +
91 +}
1 package org.onlab.onos.net.intent; 1 package org.onlab.onos.net.intent;
2 2
3 +import org.onlab.onos.net.Link;
4 +
5 +import java.util.Collection;
6 +
3 /** 7 /**
4 * Abstraction of an intent that can be installed into 8 * Abstraction of an intent that can be installed into
5 * the underlying system without additional compilation. 9 * the underlying system without additional compilation.
6 */ 10 */
7 public interface InstallableIntent extends Intent { 11 public interface InstallableIntent extends Intent {
12 +
13 + /**
14 + * Returns the collection of links that are required for this installable
15 + * intent to exist.
16 + *
17 + * @return collection of links
18 + */
19 + // FIXME: replace this with 'NetworkResource'
20 + Collection<Link> requiredLinks();
21 +
8 } 22 }
......
...@@ -2,7 +2,7 @@ package org.onlab.onos.net.intent; ...@@ -2,7 +2,7 @@ package org.onlab.onos.net.intent;
2 2
3 /** 3 /**
4 * Abstraction of an application level intent. 4 * Abstraction of an application level intent.
5 - * 5 + * <p/>
6 * Make sure that an Intent should be immutable when a new type is defined. 6 * Make sure that an Intent should be immutable when a new type is defined.
7 */ 7 */
8 public interface Intent extends BatchOperationTarget { 8 public interface Intent extends BatchOperationTarget {
...@@ -11,5 +11,5 @@ public interface Intent extends BatchOperationTarget { ...@@ -11,5 +11,5 @@ public interface Intent extends BatchOperationTarget {
11 * 11 *
12 * @return intent identifier 12 * @return intent identifier
13 */ 13 */
14 - IntentId getId(); 14 + IntentId id();
15 } 15 }
......
1 package org.onlab.onos.net.intent; 1 package org.onlab.onos.net.intent;
2 2
3 -import static com.google.common.base.Preconditions.checkNotNull; 3 +import org.onlab.onos.event.AbstractEvent;
4 -
5 -import java.util.Objects;
6 -
7 -import com.google.common.base.MoreObjects;
8 4
9 /** 5 /**
10 * A class to represent an intent related event. 6 * A class to represent an intent related event.
11 */ 7 */
12 -public class IntentEvent { 8 +public class IntentEvent extends AbstractEvent<IntentEvent.Type, Intent> {
13 -
14 - // TODO: determine a suitable parent class; if one does not exist, consider introducing one
15 -
16 - private final long time;
17 - private final Intent intent;
18 - private final IntentState state;
19 - private final IntentState previous;
20 9
10 + public enum Type {
21 /** 11 /**
22 - * Creates an event describing a state change of an intent. 12 + * Signifies that a new intent has been submitted to the system.
23 - *
24 - * @param intent subject intent
25 - * @param state new intent state
26 - * @param previous previous intent state
27 - * @param time time the event created in milliseconds since start of epoch
28 - * @throws NullPointerException if the intent or state is null
29 */ 13 */
30 - public IntentEvent(Intent intent, IntentState state, IntentState previous, long time) { 14 + SUBMITTED,
31 - this.intent = checkNotNull(intent);
32 - this.state = checkNotNull(state);
33 - this.previous = previous;
34 - this.time = time;
35 - }
36 15
37 /** 16 /**
38 - * Constructor for serializer. 17 + * Signifies that an intent has been successfully installed.
39 */ 18 */
40 - protected IntentEvent() { 19 + INSTALLED,
41 - this.intent = null;
42 - this.state = null;
43 - this.previous = null;
44 - this.time = 0;
45 - }
46 20
47 /** 21 /**
48 - * Returns the state of the intent which caused the event. 22 + * Signifies that an intent has failed compilation or installation.
49 - *
50 - * @return the state of the intent
51 */ 23 */
52 - public IntentState getState() { 24 + FAILED,
53 - return state;
54 - }
55 25
56 /** 26 /**
57 - * Returns the previous state of the intent which caused the event. 27 + * Signifies that an intent has been withdrawn from the system.
58 - *
59 - * @return the previous state of the intent
60 */ 28 */
61 - public IntentState getPreviousState() { 29 + WITHDRAWN
62 - return previous;
63 } 30 }
64 31
65 /** 32 /**
66 - * Returns the intent associated with the event. 33 + * Creates an event of a given type and for the specified intent and the
34 + * current time.
67 * 35 *
68 - * @return the intent 36 + * @param type event type
37 + * @param intent subject intent
38 + * @param time time the event created in milliseconds since start of epoch
69 */ 39 */
70 - public Intent getIntent() { 40 + public IntentEvent(Type type, Intent intent, long time) {
71 - return intent; 41 + super(type, intent, time);
72 } 42 }
73 43
74 /** 44 /**
75 - * Returns the time at which the event was created. 45 + * Creates an event of a given type and for the specified intent and the
46 + * current time.
76 * 47 *
77 - * @return the time in milliseconds since start of epoch 48 + * @param type event type
49 + * @param intent subject intent
78 */ 50 */
79 - public long getTime() { 51 + public IntentEvent(Type type, Intent intent) {
80 - return time; 52 + super(type, intent);
81 - }
82 -
83 - @Override
84 - public boolean equals(Object o) {
85 - if (this == o) {
86 - return true;
87 - }
88 - if (o == null || getClass() != o.getClass()) {
89 - return false;
90 } 53 }
91 54
92 - IntentEvent that = (IntentEvent) o;
93 - return Objects.equals(this.intent, that.intent)
94 - && Objects.equals(this.state, that.state)
95 - && Objects.equals(this.previous, that.previous)
96 - && Objects.equals(this.time, that.time);
97 - }
98 -
99 - @Override
100 - public int hashCode() {
101 - return Objects.hash(intent, state, previous, time);
102 - }
103 -
104 - @Override
105 - public String toString() {
106 - return MoreObjects.toStringHelper(getClass())
107 - .add("intent", intent)
108 - .add("state", state)
109 - .add("previous", previous)
110 - .add("time", time)
111 - .toString();
112 - }
113 } 55 }
......
...@@ -2,7 +2,7 @@ package org.onlab.onos.net.intent; ...@@ -2,7 +2,7 @@ package org.onlab.onos.net.intent;
2 2
3 /** 3 /**
4 * Intent identifier suitable as an external key. 4 * Intent identifier suitable as an external key.
5 - * 5 + * <p/>
6 * This class is immutable. 6 * This class is immutable.
7 */ 7 */
8 public final class IntentId implements BatchOperationTarget { 8 public final class IntentId implements BatchOperationTarget {
......
1 package org.onlab.onos.net.intent; 1 package org.onlab.onos.net.intent;
2 2
3 +import org.onlab.onos.event.EventListener;
4 +
3 /** 5 /**
4 * Listener for {@link IntentEvent intent events}. 6 * Listener for {@link IntentEvent intent events}.
5 */ 7 */
6 -public interface IntentEventListener { 8 +public interface IntentListener extends EventListener<IntentEvent> {
7 - /**
8 - * Processes the specified intent event.
9 - *
10 - * @param event the event to process
11 - */
12 - void event(IntentEvent event);
13 } 9 }
......
1 package org.onlab.onos.net.intent; 1 package org.onlab.onos.net.intent;
2 2
3 -import java.util.Set;
4 3
5 /** 4 /**
6 * Service for application submitting or withdrawing their intents. 5 * Service for application submitting or withdrawing their intents.
...@@ -8,9 +7,9 @@ import java.util.Set; ...@@ -8,9 +7,9 @@ import java.util.Set;
8 public interface IntentService { 7 public interface IntentService {
9 /** 8 /**
10 * Submits an intent into the system. 9 * Submits an intent into the system.
11 - * 10 + * <p/>
12 - * This is an asynchronous request meaning that any compiling 11 + * This is an asynchronous request meaning that any compiling or
13 - * or installation activities may be done at later time. 12 + * installation activities may be done at later time.
14 * 13 *
15 * @param intent intent to be submitted 14 * @param intent intent to be submitted
16 */ 15 */
...@@ -18,9 +17,9 @@ public interface IntentService { ...@@ -18,9 +17,9 @@ public interface IntentService {
18 17
19 /** 18 /**
20 * Withdraws an intent from the system. 19 * Withdraws an intent from the system.
21 - * 20 + * <p/>
22 - * This is an asynchronous request meaning that the environment 21 + * This is an asynchronous request meaning that the environment may be
23 - * may be affected at later time. 22 + * affected at later time.
24 * 23 *
25 * @param intent intent to be withdrawn 24 * @param intent intent to be withdrawn
26 */ 25 */
...@@ -29,20 +28,27 @@ public interface IntentService { ...@@ -29,20 +28,27 @@ public interface IntentService {
29 /** 28 /**
30 * Submits a batch of submit &amp; withdraw operations. Such a batch is 29 * Submits a batch of submit &amp; withdraw operations. Such a batch is
31 * assumed to be processed together. 30 * assumed to be processed together.
32 - * 31 + * <p/>
33 - * This is an asynchronous request meaning that the environment 32 + * This is an asynchronous request meaning that the environment may be
34 - * may be affected at later time. 33 + * affected at later time.
35 * 34 *
36 * @param operations batch of intent operations 35 * @param operations batch of intent operations
37 */ 36 */
38 void execute(IntentOperations operations); 37 void execute(IntentOperations operations);
39 38
40 /** 39 /**
41 - * Returns immutable set of intents currently in the system. 40 + * Returns an iterable of intents currently in the system.
42 * 41 *
43 * @return set of intents 42 * @return set of intents
44 */ 43 */
45 - Set<Intent> getIntents(); 44 + Iterable<Intent> getIntents();
45 +
46 + /**
47 + * Returns the number of intents currently in the system.
48 + *
49 + * @return number of intents
50 + */
51 + long getIntentCount();
46 52
47 /** 53 /**
48 * Retrieves the intent specified by its identifier. 54 * Retrieves the intent specified by its identifier.
...@@ -56,7 +62,8 @@ public interface IntentService { ...@@ -56,7 +62,8 @@ public interface IntentService {
56 * Retrieves the state of an intent by its identifier. 62 * Retrieves the state of an intent by its identifier.
57 * 63 *
58 * @param id intent identifier 64 * @param id intent identifier
59 - * @return the intent state or null if one with the given identifier is not found 65 + * @return the intent state or null if one with the given identifier is not
66 + * found
60 */ 67 */
61 IntentState getIntentState(IntentId id); 68 IntentState getIntentState(IntentId id);
62 69
...@@ -65,12 +72,12 @@ public interface IntentService { ...@@ -65,12 +72,12 @@ public interface IntentService {
65 * 72 *
66 * @param listener listener to be added 73 * @param listener listener to be added
67 */ 74 */
68 - void addListener(IntentEventListener listener); 75 + void addListener(IntentListener listener);
69 76
70 /** 77 /**
71 * Removes the specified listener for intent events. 78 * Removes the specified listener for intent events.
72 * 79 *
73 * @param listener listener to be removed 80 * @param listener listener to be removed
74 */ 81 */
75 - void removeListener(IntentEventListener listener); 82 + void removeListener(IntentListener listener);
76 } 83 }
......
1 package org.onlab.onos.net.intent; 1 package org.onlab.onos.net.intent;
2 2
3 /** 3 /**
4 - * This class represents the states of an intent. 4 + * Representation of the phases an intent may attain during its lifecycle.
5 - *
6 - * <p>
7 - * Note: The state is expressed as enum, but there is possibility
8 - * in the future that we define specific class instead of enum to improve
9 - * the extensibility of state definition.
10 - * </p>
11 */ 5 */
12 public enum IntentState { 6 public enum IntentState {
13 - // FIXME: requires discussion on State vs. EventType and a solid state-transition diagram 7 +
14 - // TODO: consider the impact of conflict detection
15 - // TODO: consider the impact that external events affect an installed intent
16 /** 8 /**
17 - * The beginning state. 9 + * Signifies that the intent has been submitted and will start compiling
18 - * 10 + * shortly. However, this compilation may not necessarily occur on the
11 + * local controller instance.
12 + * <p/>
19 * All intent in the runtime take this state first. 13 * All intent in the runtime take this state first.
20 */ 14 */
21 SUBMITTED, 15 SUBMITTED,
22 16
23 /** 17 /**
24 - * The intent compilation has been completed. 18 + * Signifies that the intent is being compiled into installable intents.
25 - * 19 + * This is a transitional state after which the intent will enter either
26 - * An intent translation graph (tree) is completely created. 20 + * {@link #FAILED} state or {@link #INSTALLING} state.
27 - * Leaves of the graph are installable intent type. 21 + */
22 + COMPILING,
23 +
24 + /**
25 + * Signifies that the resulting installable intents are being installed
26 + * into the network environment. This is a transitional state after which
27 + * the intent will enter either {@link #INSTALLED} state or
28 + * {@link #RECOMPILING} state.
28 */ 29 */
29 - COMPILED, 30 + INSTALLING,
30 31
31 /** 32 /**
32 - * The intent has been successfully installed. 33 + * The intent has been successfully installed. This is a state where the
34 + * intent may remain parked until it is withdrawn by the application or
35 + * until the network environment changes in some way to make the original
36 + * set of installable intents untenable.
33 */ 37 */
34 INSTALLED, 38 INSTALLED,
35 39
36 /** 40 /**
37 - * The intent is being withdrawn. 41 + * Signifies that the intent is being recompiled into installable intents
38 - * 42 + * as an attempt to adapt to an anomaly in the network environment.
39 - * When {@link IntentService#withdraw(Intent)} is called, 43 + * This is a transitional state after which the intent will enter either
40 - * the intent takes this state first. 44 + * {@link #FAILED} state or {@link #INSTALLING} state.
45 + * <p/>
46 + * Exit to the {@link #FAILED} state may be caused by failure to compile
47 + * or by compiling into the same set of installable intents which have
48 + * previously failed to be installed.
49 + */
50 + RECOMPILING,
51 +
52 + /**
53 + * Indicates that the intent is being withdrawn. This is a transitional
54 + * state, triggered by invocation of the
55 + * {@link IntentService#withdraw(Intent)} but one with only one outcome,
56 + * which is the the intent being placed in the {@link #WITHDRAWN} state.
41 */ 57 */
42 WITHDRAWING, 58 WITHDRAWING,
43 59
44 /** 60 /**
45 - * The intent has been successfully withdrawn. 61 + * Indicates that the intent has been successfully withdrawn.
46 */ 62 */
47 WITHDRAWN, 63 WITHDRAWN,
48 64
49 /** 65 /**
50 - * The intent has failed to be compiled, installed, or withdrawn. 66 + * Signifies that the intent has failed compiling, installing or
51 - * 67 + * recompiling states.
52 - * When the intent failed to be withdrawn, it is still, at least partially installed.
53 */ 68 */
54 - FAILED, 69 + FAILED
55 } 70 }
......
1 +package org.onlab.onos.net.intent;
2 +
3 +import org.onlab.onos.store.Store;
4 +
5 +import java.util.List;
6 +
7 +/**
8 + * Manages inventory of end-station intents; not intended for direct use.
9 + */
10 +public interface IntentStore extends Store<IntentEvent, IntentStoreDelegate> {
11 +
12 + /**
13 + * Submits a new intent into the store. If the returned event is not
14 + * null, the manager is expected to dispatch the event and then to kick
15 + * off intent compilation process. Otherwise, another node has been elected
16 + * to perform the compilation process and the node will learn about
17 + * the submittal and results of the intent compilation via the delegate
18 + * mechanism.
19 + *
20 + * @param intent intent to be submitted
21 + * @return event indicating the intent was submitted or null if no
22 + * change resulted, e.g. duplicate intent
23 + */
24 + IntentEvent createIntent(Intent intent);
25 +
26 + /**
27 + * Removes the specified intent from the inventory.
28 + *
29 + * @param intentId intent identification
30 + * @return removed state transition event or null if intent was not found
31 + */
32 + IntentEvent removeIntent(IntentId intentId);
33 +
34 + /**
35 + * Returns the number of intents in the store.
36 + */
37 + long getIntentCount();
38 +
39 + /**
40 + * Returns a collection of all intents in the store.
41 + *
42 + * @return iterable collection of all intents
43 + */
44 + Iterable<Intent> getIntents();
45 +
46 + /**
47 + * Returns the intent with the specified identifer.
48 + *
49 + * @param intentId intent identification
50 + * @return intent or null if not found
51 + */
52 + Intent getIntent(IntentId intentId);
53 +
54 + /**
55 + * Returns the state of the specified intent.
56 + *
57 + * @param intentId intent identification
58 + * @return current intent state
59 + */
60 + IntentState getIntentState(IntentId intentId);
61 +
62 + /**
63 + * Sets the state of the specified intent to the new state.
64 + *
65 + * @param intent intent whose state is to be changed
66 + * @param newState new state
67 + * @return state transition event
68 + */
69 + IntentEvent setState(Intent intent, IntentState newState);
70 +
71 + /**
72 + * Adds the installable intents which resulted from compilation of the
73 + * specified original intent.
74 + *
75 + * @param intentId original intent identifier
76 + * @param installableIntents compiled installable intents
77 + */
78 + void addInstallableIntents(IntentId intentId,
79 + List<InstallableIntent> installableIntents);
80 +
81 + /**
82 + * Returns the list of the installable events associated with the specified
83 + * original intent.
84 + *
85 + * @param intentId original intent identifier
86 + * @return compiled installable intents
87 + */
88 + List<InstallableIntent> getInstallableIntents(IntentId intentId);
89 +
90 + // TODO: this should be triggered from with the store as a result of removeIntent call
91 +
92 + /**
93 + * Removes any installable intents which resulted from compilation of the
94 + * specified original intent.
95 + *
96 + * @param intentId original intent identifier
97 + * @return compiled state transition event
98 + */
99 + void removeInstalledIntents(IntentId intentId);
100 +
101 +}
1 +package org.onlab.onos.net.intent;
2 +
3 +import org.onlab.onos.store.StoreDelegate;
4 +
5 +/**
6 + * Intent store delegate abstraction.
7 + */
8 +public interface IntentStoreDelegate extends StoreDelegate<IntentEvent> {
9 +}
1 package org.onlab.onos.net.intent; 1 package org.onlab.onos.net.intent;
2 2
3 -import static com.google.common.base.Preconditions.checkArgument; 3 +import com.google.common.base.MoreObjects;
4 -import static com.google.common.base.Preconditions.checkNotNull; 4 +import com.google.common.collect.Sets;
5 -
6 -import java.util.Objects;
7 -import java.util.Set;
8 -
9 import org.onlab.onos.net.ConnectPoint; 5 import org.onlab.onos.net.ConnectPoint;
10 import org.onlab.onos.net.flow.TrafficSelector; 6 import org.onlab.onos.net.flow.TrafficSelector;
11 import org.onlab.onos.net.flow.TrafficTreatment; 7 import org.onlab.onos.net.flow.TrafficTreatment;
12 8
13 -import com.google.common.base.MoreObjects; 9 +import java.util.Objects;
14 -import com.google.common.collect.Sets; 10 +import java.util.Set;
11 +
12 +import static com.google.common.base.Preconditions.checkArgument;
13 +import static com.google.common.base.Preconditions.checkNotNull;
15 14
16 /** 15 /**
17 * Abstraction of multiple source to single destination connectivity intent. 16 * Abstraction of multiple source to single destination connectivity intent.
18 */ 17 */
19 public class MultiPointToSinglePointIntent extends ConnectivityIntent { 18 public class MultiPointToSinglePointIntent extends ConnectivityIntent {
20 19
21 - private final Set<ConnectPoint> ingressPorts; 20 + private final Set<ConnectPoint> ingressPoints;
22 - private final ConnectPoint egressPort; 21 + private final ConnectPoint egressPoint;
23 22
24 /** 23 /**
25 * Creates a new multi-to-single point connectivity intent for the specified 24 * Creates a new multi-to-single point connectivity intent for the specified
...@@ -28,23 +27,25 @@ public class MultiPointToSinglePointIntent extends ConnectivityIntent { ...@@ -28,23 +27,25 @@ public class MultiPointToSinglePointIntent extends ConnectivityIntent {
28 * @param id intent identifier 27 * @param id intent identifier
29 * @param match traffic match 28 * @param match traffic match
30 * @param action action 29 * @param action action
31 - * @param ingressPorts set of ports from which ingress traffic originates 30 + * @param ingressPoints set of ports from which ingress traffic originates
32 - * @param egressPort port to which traffic will egress 31 + * @param egressPoint port to which traffic will egress
33 - * @throws NullPointerException if {@code ingressPorts} or 32 + * @throws NullPointerException if {@code ingressPoints} or
34 - * {@code egressPort} is null. 33 + * {@code egressPoint} is null.
35 - * @throws IllegalArgumentException if the size of {@code ingressPorts} is 34 + * @throws IllegalArgumentException if the size of {@code ingressPoints} is
36 * not more than 1 35 * not more than 1
37 */ 36 */
38 - public MultiPointToSinglePointIntent(IntentId id, TrafficSelector match, TrafficTreatment action, 37 + public MultiPointToSinglePointIntent(IntentId id, TrafficSelector match,
39 - Set<ConnectPoint> ingressPorts, ConnectPoint egressPort) { 38 + TrafficTreatment action,
39 + Set<ConnectPoint> ingressPoints,
40 + ConnectPoint egressPoint) {
40 super(id, match, action); 41 super(id, match, action);
41 42
42 - checkNotNull(ingressPorts); 43 + checkNotNull(ingressPoints);
43 - checkArgument(!ingressPorts.isEmpty(), 44 + checkArgument(!ingressPoints.isEmpty(),
44 "there should be at least one ingress port"); 45 "there should be at least one ingress port");
45 46
46 - this.ingressPorts = Sets.newHashSet(ingressPorts); 47 + this.ingressPoints = Sets.newHashSet(ingressPoints);
47 - this.egressPort = checkNotNull(egressPort); 48 + this.egressPoint = checkNotNull(egressPoint);
48 } 49 }
49 50
50 /** 51 /**
...@@ -52,8 +53,8 @@ public class MultiPointToSinglePointIntent extends ConnectivityIntent { ...@@ -52,8 +53,8 @@ public class MultiPointToSinglePointIntent extends ConnectivityIntent {
52 */ 53 */
53 protected MultiPointToSinglePointIntent() { 54 protected MultiPointToSinglePointIntent() {
54 super(); 55 super();
55 - this.ingressPorts = null; 56 + this.ingressPoints = null;
56 - this.egressPort = null; 57 + this.egressPoint = null;
57 } 58 }
58 59
59 /** 60 /**
...@@ -62,8 +63,8 @@ public class MultiPointToSinglePointIntent extends ConnectivityIntent { ...@@ -62,8 +63,8 @@ public class MultiPointToSinglePointIntent extends ConnectivityIntent {
62 * 63 *
63 * @return set of ingress ports 64 * @return set of ingress ports
64 */ 65 */
65 - public Set<ConnectPoint> getIngressPorts() { 66 + public Set<ConnectPoint> ingressPoints() {
66 - return ingressPorts; 67 + return ingressPoints;
67 } 68 }
68 69
69 /** 70 /**
...@@ -71,8 +72,8 @@ public class MultiPointToSinglePointIntent extends ConnectivityIntent { ...@@ -71,8 +72,8 @@ public class MultiPointToSinglePointIntent extends ConnectivityIntent {
71 * 72 *
72 * @return egress port 73 * @return egress port
73 */ 74 */
74 - public ConnectPoint getEgressPort() { 75 + public ConnectPoint egressPoint() {
75 - return egressPort; 76 + return egressPoint;
76 } 77 }
77 78
78 @Override 79 @Override
...@@ -88,23 +89,23 @@ public class MultiPointToSinglePointIntent extends ConnectivityIntent { ...@@ -88,23 +89,23 @@ public class MultiPointToSinglePointIntent extends ConnectivityIntent {
88 } 89 }
89 90
90 MultiPointToSinglePointIntent that = (MultiPointToSinglePointIntent) o; 91 MultiPointToSinglePointIntent that = (MultiPointToSinglePointIntent) o;
91 - return Objects.equals(this.ingressPorts, that.ingressPorts) 92 + return Objects.equals(this.ingressPoints, that.ingressPoints)
92 - && Objects.equals(this.egressPort, that.egressPort); 93 + && Objects.equals(this.egressPoint, that.egressPoint);
93 } 94 }
94 95
95 @Override 96 @Override
96 public int hashCode() { 97 public int hashCode() {
97 - return Objects.hash(super.hashCode(), ingressPorts, egressPort); 98 + return Objects.hash(super.hashCode(), ingressPoints, egressPoint);
98 } 99 }
99 100
100 @Override 101 @Override
101 public String toString() { 102 public String toString() {
102 return MoreObjects.toStringHelper(getClass()) 103 return MoreObjects.toStringHelper(getClass())
103 - .add("id", getId()) 104 + .add("id", id())
104 - .add("match", getTrafficSelector()) 105 + .add("match", selector())
105 - .add("action", getTrafficTreatment()) 106 + .add("action", treatment())
106 - .add("ingressPorts", getIngressPorts()) 107 + .add("ingressPoints", ingressPoints())
107 - .add("egressPort", getEgressPort()) 108 + .add("egressPoint", egressPoint())
108 .toString(); 109 .toString();
109 } 110 }
110 } 111 }
......
...@@ -3,30 +3,30 @@ package org.onlab.onos.net.intent; ...@@ -3,30 +3,30 @@ package org.onlab.onos.net.intent;
3 import org.onlab.onos.net.ConnectPoint; 3 import org.onlab.onos.net.ConnectPoint;
4 4
5 // TODO: consider if this intent should be sub-class of ConnectivityIntent 5 // TODO: consider if this intent should be sub-class of ConnectivityIntent
6 +
6 /** 7 /**
7 * An optical layer Intent for a connectivity from a transponder port to another 8 * An optical layer Intent for a connectivity from a transponder port to another
8 * transponder port. 9 * transponder port.
9 - * <p> 10 + * <p/>
10 * This class doesn't accepts lambda specifier. This class computes path between 11 * This class doesn't accepts lambda specifier. This class computes path between
11 * ports and assign lambda automatically. The lambda can be specified using 12 * ports and assign lambda automatically. The lambda can be specified using
12 * OpticalPathFlow class. 13 * OpticalPathFlow class.
13 */ 14 */
14 public class OpticalConnectivityIntent extends AbstractIntent { 15 public class OpticalConnectivityIntent extends AbstractIntent {
15 - protected ConnectPoint srcConnectPoint; 16 + protected ConnectPoint src;
16 - protected ConnectPoint dstConnectPoint; 17 + protected ConnectPoint dst;
17 18
18 /** 19 /**
19 * Constructor. 20 * Constructor.
20 * 21 *
21 * @param id ID for this new Intent object. 22 * @param id ID for this new Intent object.
22 - * @param srcConnectPoint The source transponder port. 23 + * @param src The source transponder port.
23 - * @param dstConnectPoint The destination transponder port. 24 + * @param dst The destination transponder port.
24 */ 25 */
25 - public OpticalConnectivityIntent(IntentId id, 26 + public OpticalConnectivityIntent(IntentId id, ConnectPoint src, ConnectPoint dst) {
26 - ConnectPoint srcConnectPoint, ConnectPoint dstConnectPoint) {
27 super(id); 27 super(id);
28 - this.srcConnectPoint = srcConnectPoint; 28 + this.src = src;
29 - this.dstConnectPoint = dstConnectPoint; 29 + this.dst = dst;
30 } 30 }
31 31
32 /** 32 /**
...@@ -34,8 +34,8 @@ public class OpticalConnectivityIntent extends AbstractIntent { ...@@ -34,8 +34,8 @@ public class OpticalConnectivityIntent extends AbstractIntent {
34 */ 34 */
35 protected OpticalConnectivityIntent() { 35 protected OpticalConnectivityIntent() {
36 super(); 36 super();
37 - this.srcConnectPoint = null; 37 + this.src = null;
38 - this.dstConnectPoint = null; 38 + this.dst = null;
39 } 39 }
40 40
41 /** 41 /**
...@@ -44,7 +44,7 @@ public class OpticalConnectivityIntent extends AbstractIntent { ...@@ -44,7 +44,7 @@ public class OpticalConnectivityIntent extends AbstractIntent {
44 * @return The source transponder port. 44 * @return The source transponder port.
45 */ 45 */
46 public ConnectPoint getSrcConnectPoint() { 46 public ConnectPoint getSrcConnectPoint() {
47 - return srcConnectPoint; 47 + return src;
48 } 48 }
49 49
50 /** 50 /**
...@@ -52,7 +52,7 @@ public class OpticalConnectivityIntent extends AbstractIntent { ...@@ -52,7 +52,7 @@ public class OpticalConnectivityIntent extends AbstractIntent {
52 * 52 *
53 * @return The source transponder port. 53 * @return The source transponder port.
54 */ 54 */
55 - public ConnectPoint getDstConnectPoint() { 55 + public ConnectPoint getDst() {
56 - return dstConnectPoint; 56 + return dst;
57 } 57 }
58 } 58 }
......
1 package org.onlab.onos.net.intent; 1 package org.onlab.onos.net.intent;
2 2
3 -import java.util.Objects; 3 +import com.google.common.base.MoreObjects;
4 -
5 import org.onlab.onos.net.ConnectPoint; 4 import org.onlab.onos.net.ConnectPoint;
5 +import org.onlab.onos.net.Link;
6 import org.onlab.onos.net.Path; 6 import org.onlab.onos.net.Path;
7 import org.onlab.onos.net.flow.TrafficSelector; 7 import org.onlab.onos.net.flow.TrafficSelector;
8 import org.onlab.onos.net.flow.TrafficTreatment; 8 import org.onlab.onos.net.flow.TrafficTreatment;
9 9
10 -import com.google.common.base.MoreObjects; 10 +import java.util.Collection;
11 +import java.util.Objects;
11 12
12 /** 13 /**
13 * Abstraction of explicitly path specified connectivity intent. 14 * Abstraction of explicitly path specified connectivity intent.
14 */ 15 */
15 -public class PathIntent extends PointToPointIntent { 16 +public class PathIntent extends PointToPointIntent implements InstallableIntent {
16 17
17 private final Path path; 18 private final Path path;
18 19
...@@ -45,7 +46,7 @@ public class PathIntent extends PointToPointIntent { ...@@ -45,7 +46,7 @@ public class PathIntent extends PointToPointIntent {
45 * 46 *
46 * @return traversed links 47 * @return traversed links
47 */ 48 */
48 - public Path getPath() { 49 + public Path path() {
49 return path; 50 return path;
50 } 51 }
51 52
...@@ -78,12 +79,18 @@ public class PathIntent extends PointToPointIntent { ...@@ -78,12 +79,18 @@ public class PathIntent extends PointToPointIntent {
78 @Override 79 @Override
79 public String toString() { 80 public String toString() {
80 return MoreObjects.toStringHelper(getClass()) 81 return MoreObjects.toStringHelper(getClass())
81 - .add("id", getId()) 82 + .add("id", id())
82 - .add("match", getTrafficSelector()) 83 + .add("match", selector())
83 - .add("action", getTrafficTreatment()) 84 + .add("action", treatment())
84 - .add("ingressPort", getIngressPort()) 85 + .add("ingressPort", ingressPoint())
85 - .add("egressPort", getEgressPort()) 86 + .add("egressPort", egressPoint())
86 .add("path", path) 87 .add("path", path)
87 .toString(); 88 .toString();
88 } 89 }
90 +
91 + @Override
92 + public Collection<Link> requiredLinks() {
93 + return path.links();
94 + }
95 +
89 } 96 }
......
1 package org.onlab.onos.net.intent; 1 package org.onlab.onos.net.intent;
2 2
3 -import static com.google.common.base.Preconditions.checkNotNull; 3 +import com.google.common.base.MoreObjects;
4 -
5 -import java.util.Objects;
6 -
7 import org.onlab.onos.net.ConnectPoint; 4 import org.onlab.onos.net.ConnectPoint;
8 import org.onlab.onos.net.flow.TrafficSelector; 5 import org.onlab.onos.net.flow.TrafficSelector;
9 import org.onlab.onos.net.flow.TrafficTreatment; 6 import org.onlab.onos.net.flow.TrafficTreatment;
10 7
11 -import com.google.common.base.MoreObjects; 8 +import java.util.Objects;
9 +
10 +import static com.google.common.base.Preconditions.checkNotNull;
12 11
13 /** 12 /**
14 * Abstraction of point-to-point connectivity. 13 * Abstraction of point-to-point connectivity.
15 */ 14 */
16 public class PointToPointIntent extends ConnectivityIntent { 15 public class PointToPointIntent extends ConnectivityIntent {
17 16
18 - private final ConnectPoint ingressPort; 17 + private final ConnectPoint ingressPoint;
19 - private final ConnectPoint egressPort; 18 + private final ConnectPoint egressPoint;
20 19
21 /** 20 /**
22 * Creates a new point-to-point intent with the supplied ingress/egress 21 * Creates a new point-to-point intent with the supplied ingress/egress
23 * ports. 22 * ports.
24 * 23 *
25 * @param id intent identifier 24 * @param id intent identifier
26 - * @param match traffic match 25 + * @param selector traffic selector
27 - * @param action action 26 + * @param treatment treatment
28 - * @param ingressPort ingress port 27 + * @param ingressPoint ingress port
29 - * @param egressPort egress port 28 + * @param egressPoint egress port
30 - * @throws NullPointerException if {@code ingressPort} or {@code egressPort} is null. 29 + * @throws NullPointerException if {@code ingressPoint} or {@code egressPoints} is null.
31 */ 30 */
32 - public PointToPointIntent(IntentId id, TrafficSelector match, TrafficTreatment action, 31 + public PointToPointIntent(IntentId id, TrafficSelector selector,
33 - ConnectPoint ingressPort, ConnectPoint egressPort) { 32 + TrafficTreatment treatment,
34 - super(id, match, action); 33 + ConnectPoint ingressPoint,
35 - this.ingressPort = checkNotNull(ingressPort); 34 + ConnectPoint egressPoint) {
36 - this.egressPort = checkNotNull(egressPort); 35 + super(id, selector, treatment);
36 + this.ingressPoint = checkNotNull(ingressPoint);
37 + this.egressPoint = checkNotNull(egressPoint);
37 } 38 }
38 39
39 /** 40 /**
...@@ -41,8 +42,8 @@ public class PointToPointIntent extends ConnectivityIntent { ...@@ -41,8 +42,8 @@ public class PointToPointIntent extends ConnectivityIntent {
41 */ 42 */
42 protected PointToPointIntent() { 43 protected PointToPointIntent() {
43 super(); 44 super();
44 - this.ingressPort = null; 45 + this.ingressPoint = null;
45 - this.egressPort = null; 46 + this.egressPoint = null;
46 } 47 }
47 48
48 /** 49 /**
...@@ -51,8 +52,8 @@ public class PointToPointIntent extends ConnectivityIntent { ...@@ -51,8 +52,8 @@ public class PointToPointIntent extends ConnectivityIntent {
51 * 52 *
52 * @return ingress port 53 * @return ingress port
53 */ 54 */
54 - public ConnectPoint getIngressPort() { 55 + public ConnectPoint ingressPoint() {
55 - return ingressPort; 56 + return ingressPoint;
56 } 57 }
57 58
58 /** 59 /**
...@@ -60,8 +61,8 @@ public class PointToPointIntent extends ConnectivityIntent { ...@@ -60,8 +61,8 @@ public class PointToPointIntent extends ConnectivityIntent {
60 * 61 *
61 * @return egress port 62 * @return egress port
62 */ 63 */
63 - public ConnectPoint getEgressPort() { 64 + public ConnectPoint egressPoint() {
64 - return egressPort; 65 + return egressPoint;
65 } 66 }
66 67
67 @Override 68 @Override
...@@ -77,23 +78,23 @@ public class PointToPointIntent extends ConnectivityIntent { ...@@ -77,23 +78,23 @@ public class PointToPointIntent extends ConnectivityIntent {
77 } 78 }
78 79
79 PointToPointIntent that = (PointToPointIntent) o; 80 PointToPointIntent that = (PointToPointIntent) o;
80 - return Objects.equals(this.ingressPort, that.ingressPort) 81 + return Objects.equals(this.ingressPoint, that.ingressPoint)
81 - && Objects.equals(this.egressPort, that.egressPort); 82 + && Objects.equals(this.egressPoint, that.egressPoint);
82 } 83 }
83 84
84 @Override 85 @Override
85 public int hashCode() { 86 public int hashCode() {
86 - return Objects.hash(super.hashCode(), ingressPort, egressPort); 87 + return Objects.hash(super.hashCode(), ingressPoint, egressPoint);
87 } 88 }
88 89
89 @Override 90 @Override
90 public String toString() { 91 public String toString() {
91 return MoreObjects.toStringHelper(getClass()) 92 return MoreObjects.toStringHelper(getClass())
92 - .add("id", getId()) 93 + .add("id", id())
93 - .add("match", getTrafficSelector()) 94 + .add("match", selector())
94 - .add("action", getTrafficTreatment()) 95 + .add("action", treatment())
95 - .add("ingressPort", ingressPort) 96 + .add("ingressPoint", ingressPoint)
96 - .add("egressPort", egressPort) 97 + .add("egressPoints", egressPoint)
97 .toString(); 98 .toString();
98 } 99 }
99 100
......
1 package org.onlab.onos.net.intent; 1 package org.onlab.onos.net.intent;
2 2
3 -import static com.google.common.base.Preconditions.checkArgument; 3 +import com.google.common.base.MoreObjects;
4 -import static com.google.common.base.Preconditions.checkNotNull; 4 +import com.google.common.collect.Sets;
5 -
6 -import java.util.Objects;
7 -import java.util.Set;
8 -
9 import org.onlab.onos.net.ConnectPoint; 5 import org.onlab.onos.net.ConnectPoint;
10 import org.onlab.onos.net.flow.TrafficSelector; 6 import org.onlab.onos.net.flow.TrafficSelector;
11 import org.onlab.onos.net.flow.TrafficTreatment; 7 import org.onlab.onos.net.flow.TrafficTreatment;
12 8
13 -import com.google.common.base.MoreObjects; 9 +import java.util.Objects;
14 -import com.google.common.collect.Sets; 10 +import java.util.Set;
11 +
12 +import static com.google.common.base.Preconditions.checkArgument;
13 +import static com.google.common.base.Preconditions.checkNotNull;
15 14
16 /** 15 /**
17 * Abstraction of single source, multiple destination connectivity intent. 16 * Abstraction of single source, multiple destination connectivity intent.
18 */ 17 */
19 public class SinglePointToMultiPointIntent extends ConnectivityIntent { 18 public class SinglePointToMultiPointIntent extends ConnectivityIntent {
20 19
21 - private final ConnectPoint ingressPort; 20 + private final ConnectPoint ingressPoint;
22 - private final Set<ConnectPoint> egressPorts; 21 + private final Set<ConnectPoint> egressPoints;
23 22
24 /** 23 /**
25 * Creates a new single-to-multi point connectivity intent. 24 * Creates a new single-to-multi point connectivity intent.
26 * 25 *
27 * @param id intent identifier 26 * @param id intent identifier
28 - * @param match traffic match 27 + * @param selector traffic selector
29 - * @param action action 28 + * @param treatment treatment
30 - * @param ingressPort port on which traffic will ingress 29 + * @param ingressPoint port on which traffic will ingress
31 - * @param egressPorts set of ports on which traffic will egress 30 + * @param egressPoints set of ports on which traffic will egress
32 - * @throws NullPointerException if {@code ingressPort} or 31 + * @throws NullPointerException if {@code ingressPoint} or
33 - * {@code egressPorts} is null 32 + * {@code egressPoints} is null
34 - * @throws IllegalArgumentException if the size of {@code egressPorts} is 33 + * @throws IllegalArgumentException if the size of {@code egressPoints} is
35 * not more than 1 34 * not more than 1
36 */ 35 */
37 - public SinglePointToMultiPointIntent(IntentId id, TrafficSelector match, TrafficTreatment action, 36 + public SinglePointToMultiPointIntent(IntentId id, TrafficSelector selector,
38 - ConnectPoint ingressPort, 37 + TrafficTreatment treatment,
39 - Set<ConnectPoint> egressPorts) { 38 + ConnectPoint ingressPoint,
40 - super(id, match, action); 39 + Set<ConnectPoint> egressPoints) {
41 - 40 + super(id, selector, treatment);
42 - checkNotNull(egressPorts); 41 +
43 - checkArgument(!egressPorts.isEmpty(), 42 + checkNotNull(egressPoints);
43 + checkArgument(!egressPoints.isEmpty(),
44 "there should be at least one egress port"); 44 "there should be at least one egress port");
45 45
46 - this.ingressPort = checkNotNull(ingressPort); 46 + this.ingressPoint = checkNotNull(ingressPoint);
47 - this.egressPorts = Sets.newHashSet(egressPorts); 47 + this.egressPoints = Sets.newHashSet(egressPoints);
48 } 48 }
49 49
50 /** 50 /**
...@@ -52,8 +52,8 @@ public class SinglePointToMultiPointIntent extends ConnectivityIntent { ...@@ -52,8 +52,8 @@ public class SinglePointToMultiPointIntent extends ConnectivityIntent {
52 */ 52 */
53 protected SinglePointToMultiPointIntent() { 53 protected SinglePointToMultiPointIntent() {
54 super(); 54 super();
55 - this.ingressPort = null; 55 + this.ingressPoint = null;
56 - this.egressPorts = null; 56 + this.egressPoints = null;
57 } 57 }
58 58
59 /** 59 /**
...@@ -61,8 +61,8 @@ public class SinglePointToMultiPointIntent extends ConnectivityIntent { ...@@ -61,8 +61,8 @@ public class SinglePointToMultiPointIntent extends ConnectivityIntent {
61 * 61 *
62 * @return ingress port 62 * @return ingress port
63 */ 63 */
64 - public ConnectPoint getIngressPort() { 64 + public ConnectPoint ingressPoint() {
65 - return ingressPort; 65 + return ingressPoint;
66 } 66 }
67 67
68 /** 68 /**
...@@ -70,8 +70,8 @@ public class SinglePointToMultiPointIntent extends ConnectivityIntent { ...@@ -70,8 +70,8 @@ public class SinglePointToMultiPointIntent extends ConnectivityIntent {
70 * 70 *
71 * @return set of egress ports 71 * @return set of egress ports
72 */ 72 */
73 - public Set<ConnectPoint> getEgressPorts() { 73 + public Set<ConnectPoint> egressPoints() {
74 - return egressPorts; 74 + return egressPoints;
75 } 75 }
76 76
77 @Override 77 @Override
...@@ -87,23 +87,23 @@ public class SinglePointToMultiPointIntent extends ConnectivityIntent { ...@@ -87,23 +87,23 @@ public class SinglePointToMultiPointIntent extends ConnectivityIntent {
87 } 87 }
88 88
89 SinglePointToMultiPointIntent that = (SinglePointToMultiPointIntent) o; 89 SinglePointToMultiPointIntent that = (SinglePointToMultiPointIntent) o;
90 - return Objects.equals(this.ingressPort, that.ingressPort) 90 + return Objects.equals(this.ingressPoint, that.ingressPoint)
91 - && Objects.equals(this.egressPorts, that.egressPorts); 91 + && Objects.equals(this.egressPoints, that.egressPoints);
92 } 92 }
93 93
94 @Override 94 @Override
95 public int hashCode() { 95 public int hashCode() {
96 - return Objects.hash(super.hashCode(), ingressPort, egressPorts); 96 + return Objects.hash(super.hashCode(), ingressPoint, egressPoints);
97 } 97 }
98 98
99 @Override 99 @Override
100 public String toString() { 100 public String toString() {
101 return MoreObjects.toStringHelper(getClass()) 101 return MoreObjects.toStringHelper(getClass())
102 - .add("id", getId()) 102 + .add("id", id())
103 - .add("match", getTrafficSelector()) 103 + .add("match", selector())
104 - .add("action", getTrafficTreatment()) 104 + .add("action", treatment())
105 - .add("ingressPort", ingressPort) 105 + .add("ingressPoint", ingressPoint)
106 - .add("egressPort", egressPorts) 106 + .add("egressPort", egressPoints)
107 .toString(); 107 .toString();
108 } 108 }
109 109
......
1 /** 1 /**
2 - * Intent Package. TODO 2 + * Set of abstractions for conveying high-level intents for treatment of
3 + * selected network traffic by allowing applications to express the
4 + * <em>what</em> rather than the <em>how</em>. This makes such instructions
5 + * largely independent of topology and device specifics, thus allowing them to
6 + * survive topology mutations.
7 + * <p/>
8 + * The controller core provides a suite of built-in intents and their compilers
9 + * and installers. However, the intent framework is extensible in that it allows
10 + * additional intents and their compilers or installers to be added
11 + * dynamically at run-time. This allows others to enhance the initial arsenal of
12 + * connectivity and policy-based intents available in base controller software.
13 + * <p/>
14 + * The following diagram depicts the state transition diagram for each top-level intent:<br>
15 + * <img src="doc-files/intent-states.png" alt="ONOS intent states">
16 + * <p/>
17 + * The controller core accepts the intent specifications and translates them, via a
18 + * process referred to as intent compilation, to installable intents, which are
19 + * essentially actionable operations on the network environment.
20 + * These actions are carried out by intent installation process, which results
21 + * in some changes to the environment, e.g. tunnel links being provisioned,
22 + * flow rules being installed on the data-plane, optical lambdas being reserved.
23 + * <p/>
24 + * After an intent is submitted by an application, it will be sent immediately
25 + * (but asynchronously) into a compiling phase, then to installing phase and if
26 + * all goes according to plan into installed state. Once an application decides
27 + * it no longer wishes the intent to hold, it can withdraw it. This describes
28 + * the nominal flow. However, it may happen that some issue is encountered.
29 + * For example, an application may ask for an objective that is not currently
30 + * achievable, e.g. connectivity across to unconnected network segments.
31 + * If this is the case, the compiling phase may fail to produce a set of
32 + * installable intents and instead result in a failed compile. If this occurs,
33 + * only a change in the environment can trigger a transition back to the
34 + * compiling state.
35 + * <p/>
36 + * Similarly, an issue may be encountered during the installation phase in
37 + * which case the framework will attempt to recompile the intent to see if an
38 + * alternate approach is available. If so, the intent will be sent back to
39 + * installing phase. Otherwise, it will be parked in the failed state. Another
40 + * scenario that’s very likely to be encountered is where the intent is
41 + * successfully compiled and installed, but due to some topology event, such
42 + * as a downed or downgraded link, loss of throughput may occur or connectivity
43 + * may be lost altogether, thus impacting the viability of a previously
44 + * satisfied intent. If this occurs, the framework will attempt to recompile
45 + * the intent, and if an alternate approach is available, its installation
46 + * will be attempted. Otherwise, the original top-level intent will be parked
47 + * in the failed state.
48 + * <p/>
49 + * Please note that all *ing states, depicted in orange, are transitional and
50 + * are expected to last only a brief amount of time. The rest of the states
51 + * are parking states where the intent may spent some time; except for the
52 + * submitted state of course. There, the intent may pause, but only briefly,
53 + * while the system determines where to perform the compilation or while it
54 + * performs global recomputation/optimization across all prior intents.
3 */ 55 */
4 -
5 package org.onlab.onos.net.intent; 56 package org.onlab.onos.net.intent;
...\ No newline at end of file ...\ No newline at end of file
......
1 package org.onlab.onos.net.link; 1 package org.onlab.onos.net.link;
2 2
3 import org.onlab.onos.net.ConnectPoint; 3 import org.onlab.onos.net.ConnectPoint;
4 +import org.onlab.onos.net.Description;
4 import org.onlab.onos.net.Link; 5 import org.onlab.onos.net.Link;
5 6
6 /** 7 /**
7 * Describes an infrastructure link. 8 * Describes an infrastructure link.
8 */ 9 */
9 -public interface LinkDescription { 10 +public interface LinkDescription extends Description {
10 11
11 /** 12 /**
12 * Returns the link source. 13 * Returns the link source.
......
...@@ -24,7 +24,7 @@ public abstract class DefaultPacketContext implements PacketContext { ...@@ -24,7 +24,7 @@ public abstract class DefaultPacketContext implements PacketContext {
24 this.inPkt = inPkt; 24 this.inPkt = inPkt;
25 this.outPkt = outPkt; 25 this.outPkt = outPkt;
26 this.block = new AtomicBoolean(block); 26 this.block = new AtomicBoolean(block);
27 - this.builder = new DefaultTrafficTreatment.Builder(); 27 + this.builder = DefaultTrafficTreatment.builder();
28 } 28 }
29 29
30 @Override 30 @Override
......
...@@ -3,6 +3,7 @@ package org.onlab.onos.net.provider; ...@@ -3,6 +3,7 @@ package org.onlab.onos.net.provider;
3 import java.util.Objects; 3 import java.util.Objects;
4 4
5 import static com.google.common.base.MoreObjects.toStringHelper; 5 import static com.google.common.base.MoreObjects.toStringHelper;
6 +import static com.google.common.base.Preconditions.checkNotNull;
6 7
7 /** 8 /**
8 * External identity of a {@link org.onlab.onos.net.provider.Provider} family. 9 * External identity of a {@link org.onlab.onos.net.provider.Provider} family.
...@@ -19,10 +20,22 @@ import static com.google.common.base.MoreObjects.toStringHelper; ...@@ -19,10 +20,22 @@ import static com.google.common.base.MoreObjects.toStringHelper;
19 */ 20 */
20 public class ProviderId { 21 public class ProviderId {
21 22
23 + /**
24 + * Represents no provider ID.
25 + */
26 + public static final ProviderId NONE = new ProviderId();
27 +
22 private final String scheme; 28 private final String scheme;
23 private final String id; 29 private final String id;
24 private final boolean ancillary; 30 private final boolean ancillary;
25 31
32 + // For serialization
33 + private ProviderId() {
34 + scheme = null;
35 + id = null;
36 + ancillary = false;
37 + }
38 +
26 /** 39 /**
27 * Creates a new primary provider identifier from the specified string. 40 * Creates a new primary provider identifier from the specified string.
28 * The providers are expected to follow the reverse DNS convention, e.g. 41 * The providers are expected to follow the reverse DNS convention, e.g.
...@@ -45,8 +58,8 @@ public class ProviderId { ...@@ -45,8 +58,8 @@ public class ProviderId {
45 * @param ancillary ancillary provider indicator 58 * @param ancillary ancillary provider indicator
46 */ 59 */
47 public ProviderId(String scheme, String id, boolean ancillary) { 60 public ProviderId(String scheme, String id, boolean ancillary) {
48 - this.scheme = scheme; 61 + this.scheme = checkNotNull(scheme, "Scheme cannot be null");
49 - this.id = id; 62 + this.id = checkNotNull(id, "ID cannot be null");
50 this.ancillary = ancillary; 63 this.ancillary = ancillary;
51 } 64 }
52 65
......
1 package org.onlab.onos.net.proxyarp; 1 package org.onlab.onos.net.proxyarp;
2 2
3 +import org.onlab.onos.net.packet.PacketContext;
3 import org.onlab.packet.Ethernet; 4 import org.onlab.packet.Ethernet;
4 import org.onlab.packet.IpPrefix; 5 import org.onlab.packet.IpPrefix;
5 6
...@@ -33,4 +34,12 @@ public interface ProxyArpService { ...@@ -33,4 +34,12 @@ public interface ProxyArpService {
33 */ 34 */
34 void forward(Ethernet eth); 35 void forward(Ethernet eth);
35 36
37 + /**
38 + * Handles a arp packet.
39 + * Replies to arp requests and forwards request to the right place.
40 + * @param context the packet context to handle
41 + * @return true if handled, false otherwise.
42 + */
43 + boolean handleArp(PacketContext context);
44 +
36 } 45 }
......
1 package org.onlab.onos.net.topology; 1 package org.onlab.onos.net.topology;
2 2
3 import org.onlab.onos.event.AbstractEvent; 3 import org.onlab.onos.event.AbstractEvent;
4 +import org.onlab.onos.event.Event;
5 +
6 +import java.util.List;
4 7
5 /** 8 /**
6 * Describes network topology event. 9 * Describes network topology event.
7 */ 10 */
8 public class TopologyEvent extends AbstractEvent<TopologyEvent.Type, Topology> { 11 public class TopologyEvent extends AbstractEvent<TopologyEvent.Type, Topology> {
9 12
13 + private final List<Event> reasons;
14 +
10 /** 15 /**
11 * Type of topology events. 16 * Type of topology events.
12 */ 17 */
...@@ -23,9 +28,11 @@ public class TopologyEvent extends AbstractEvent<TopologyEvent.Type, Topology> { ...@@ -23,9 +28,11 @@ public class TopologyEvent extends AbstractEvent<TopologyEvent.Type, Topology> {
23 * 28 *
24 * @param type topology event type 29 * @param type topology event type
25 * @param topology event topology subject 30 * @param topology event topology subject
31 + * @param reasons list of events that triggered topology change
26 */ 32 */
27 - public TopologyEvent(Type type, Topology topology) { 33 + public TopologyEvent(Type type, Topology topology, List<Event> reasons) {
28 super(type, topology); 34 super(type, topology);
35 + this.reasons = reasons;
29 } 36 }
30 37
31 /** 38 /**
...@@ -33,10 +40,24 @@ public class TopologyEvent extends AbstractEvent<TopologyEvent.Type, Topology> { ...@@ -33,10 +40,24 @@ public class TopologyEvent extends AbstractEvent<TopologyEvent.Type, Topology> {
33 * 40 *
34 * @param type link event type 41 * @param type link event type
35 * @param topology event topology subject 42 * @param topology event topology subject
43 + * @param reasons list of events that triggered topology change
36 * @param time occurrence time 44 * @param time occurrence time
37 */ 45 */
38 - public TopologyEvent(Type type, Topology topology, long time) { 46 + public TopologyEvent(Type type, Topology topology, List<Event> reasons,
47 + long time) {
39 super(type, topology, time); 48 super(type, topology, time);
49 + this.reasons = reasons;
50 + }
51 +
52 +
53 + /**
54 + * Returns the list of events that triggered the topology change.
55 + *
56 + * @return list of events responsible for change in topology; null if
57 + * initial topology computation
58 + */
59 + public List<Event> reasons() {
60 + return reasons;
40 } 61 }
41 62
42 } 63 }
......
1 +package org.onlab.onos.store;
2 +
3 +import org.onlab.onos.cluster.MastershipTerm;
4 +import org.onlab.onos.net.DeviceId;
5 +
6 +//TODO: Consider renaming to DeviceClockProviderService?
7 +/**
8 +* Interface for feeding term information to a logical clock service
9 +* that vends per device timestamps.
10 +*/
11 +public interface ClockProviderService {
12 +
13 + /**
14 + * Updates the mastership term for the specified deviceId.
15 + *
16 + * @param deviceId device identifier.
17 + * @param term mastership term.
18 + */
19 + public void setMastershipTerm(DeviceId deviceId, MastershipTerm term);
20 +}
1 package org.onlab.onos.store; 1 package org.onlab.onos.store;
2 2
3 -import org.onlab.onos.cluster.MastershipTerm;
4 import org.onlab.onos.net.DeviceId; 3 import org.onlab.onos.net.DeviceId;
5 4
6 // TODO: Consider renaming to DeviceClockService? 5 // TODO: Consider renaming to DeviceClockService?
...@@ -15,12 +14,4 @@ public interface ClockService { ...@@ -15,12 +14,4 @@ public interface ClockService {
15 * @return timestamp. 14 * @return timestamp.
16 */ 15 */
17 public Timestamp getTimestamp(DeviceId deviceId); 16 public Timestamp getTimestamp(DeviceId deviceId);
18 -
19 - // TODO: Should this be here or separate as Admin service, etc.?
20 - /**
21 - * Updates the mastership term for the specified deviceId.
22 - * @param deviceId device identifier.
23 - * @param term mastership term.
24 - */
25 - public void setMastershipTerm(DeviceId deviceId, MastershipTerm term);
26 } 17 }
......
...@@ -2,7 +2,15 @@ package org.onlab.onos.store; ...@@ -2,7 +2,15 @@ package org.onlab.onos.store;
2 2
3 /** 3 /**
4 * Opaque version structure. 4 * Opaque version structure.
5 + * <p>
6 + * Classes implementing this interface must also implement
7 + * {@link #hashCode()} and {@link #equals(Object)}.
5 */ 8 */
6 public interface Timestamp extends Comparable<Timestamp> { 9 public interface Timestamp extends Comparable<Timestamp> {
7 10
11 + @Override
12 + public abstract int hashCode();
13 +
14 + @Override
15 + public abstract boolean equals(Object obj);
8 } 16 }
......
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
...@@ -36,6 +36,23 @@ public class DefaultAnnotationsTest { ...@@ -36,6 +36,23 @@ public class DefaultAnnotationsTest {
36 } 36 }
37 37
38 @Test 38 @Test
39 + public void union() {
40 + annotations = builder().set("foo", "1").set("bar", "2").remove("buz").build();
41 + assertEquals("incorrect keys", of("foo", "bar", "buz"), annotations.keys());
42 +
43 + SparseAnnotations updates = builder().remove("foo").set("bar", "3").set("goo", "4").remove("fuzz").build();
44 +
45 + SparseAnnotations result = DefaultAnnotations.union(annotations, updates);
46 +
47 + assertTrue("remove instruction in original remains", result.isRemoved("buz"));
48 + assertTrue("remove instruction in update remains", result.isRemoved("fuzz"));
49 + assertEquals("incorrect keys", of("buz", "goo", "bar", "fuzz"), result.keys());
50 + assertNull("incorrect value", result.value("foo"));
51 + assertEquals("incorrect value", "3", result.value("bar"));
52 + assertEquals("incorrect value", "4", result.value("goo"));
53 + }
54 +
55 + @Test
39 public void merge() { 56 public void merge() {
40 annotations = builder().set("foo", "1").set("bar", "2").build(); 57 annotations = builder().set("foo", "1").set("bar", "2").build();
41 assertEquals("incorrect keys", of("foo", "bar"), annotations.keys()); 58 assertEquals("incorrect keys", of("foo", "bar"), annotations.keys());
......
...@@ -58,7 +58,5 @@ public class DefaultDeviceTest { ...@@ -58,7 +58,5 @@ public class DefaultDeviceTest {
58 assertEquals("incorrect hw", HW, device.hwVersion()); 58 assertEquals("incorrect hw", HW, device.hwVersion());
59 assertEquals("incorrect sw", SW, device.swVersion()); 59 assertEquals("incorrect sw", SW, device.swVersion());
60 assertEquals("incorrect serial", SN1, device.serialNumber()); 60 assertEquals("incorrect serial", SN1, device.serialNumber());
61 - assertEquals("incorrect serial", SN1, device.serialNumber());
62 } 61 }
63 -
64 } 62 }
......
...@@ -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 }
......
...@@ -39,7 +39,7 @@ public class DeviceEventTest extends AbstractEventTest { ...@@ -39,7 +39,7 @@ public class DeviceEventTest extends AbstractEventTest {
39 Device device = createDevice(); 39 Device device = createDevice();
40 Port port = new DefaultPort(device, PortNumber.portNumber(123), true); 40 Port port = new DefaultPort(device, PortNumber.portNumber(123), true);
41 long before = System.currentTimeMillis(); 41 long before = System.currentTimeMillis();
42 - DeviceEvent event = new DeviceEvent(DeviceEvent.Type.DEVICE_ADDED, device); 42 + DeviceEvent event = new DeviceEvent(DeviceEvent.Type.DEVICE_ADDED, device, port);
43 long after = System.currentTimeMillis(); 43 long after = System.currentTimeMillis();
44 validateEvent(event, DeviceEvent.Type.DEVICE_ADDED, device, before, after); 44 validateEvent(event, DeviceEvent.Type.DEVICE_ADDED, device, before, after);
45 } 45 }
......
...@@ -16,8 +16,8 @@ import org.onlab.onos.net.flow.TrafficTreatment; ...@@ -16,8 +16,8 @@ import org.onlab.onos.net.flow.TrafficTreatment;
16 public abstract class ConnectivityIntentTest extends IntentTest { 16 public abstract class ConnectivityIntentTest extends IntentTest {
17 17
18 public static final IntentId IID = new IntentId(123); 18 public static final IntentId IID = new IntentId(123);
19 - public static final TrafficSelector MATCH = (new DefaultTrafficSelector.Builder()).build(); 19 + public static final TrafficSelector MATCH = DefaultTrafficSelector.builder().build();
20 - public static final TrafficTreatment NOP = (new DefaultTrafficTreatment.Builder()).build(); 20 + public static final TrafficTreatment NOP = DefaultTrafficTreatment.builder().build();
21 21
22 public static final ConnectPoint P1 = new ConnectPoint(DeviceId.deviceId("111"), PortNumber.portNumber(0x1)); 22 public static final ConnectPoint P1 = new ConnectPoint(DeviceId.deviceId("111"), PortNumber.portNumber(0x1));
23 public static final ConnectPoint P2 = new ConnectPoint(DeviceId.deviceId("222"), PortNumber.portNumber(0x2)); 23 public static final ConnectPoint P2 = new ConnectPoint(DeviceId.deviceId("222"), PortNumber.portNumber(0x2));
......
...@@ -11,19 +11,19 @@ import java.util.concurrent.ExecutorService; ...@@ -11,19 +11,19 @@ import java.util.concurrent.ExecutorService;
11 import java.util.concurrent.Executors; 11 import java.util.concurrent.Executors;
12 12
13 /** 13 /**
14 - * Fake implementation of the intent service to assist in developing tests 14 + * Fake implementation of the intent service to assist in developing tests of
15 - * of the interface contract. 15 + * the interface contract.
16 */ 16 */
17 public class FakeIntentManager implements TestableIntentService { 17 public class FakeIntentManager implements TestableIntentService {
18 18
19 private final Map<IntentId, Intent> intents = new HashMap<>(); 19 private final Map<IntentId, Intent> intents = new HashMap<>();
20 private final Map<IntentId, IntentState> intentStates = new HashMap<>(); 20 private final Map<IntentId, IntentState> intentStates = new HashMap<>();
21 private final Map<IntentId, List<InstallableIntent>> installables = new HashMap<>(); 21 private final Map<IntentId, List<InstallableIntent>> installables = new HashMap<>();
22 - private final Set<IntentEventListener> listeners = new HashSet<>(); 22 + private final Set<IntentListener> listeners = new HashSet<>();
23 23
24 private final Map<Class<? extends Intent>, IntentCompiler<? extends Intent>> compilers = new HashMap<>(); 24 private final Map<Class<? extends Intent>, IntentCompiler<? extends Intent>> compilers = new HashMap<>();
25 - private final Map<Class<? extends InstallableIntent>, 25 + private final Map<Class<? extends InstallableIntent>, IntentInstaller<? extends InstallableIntent>> installers
26 - IntentInstaller<? extends InstallableIntent>> installers = new HashMap<>(); 26 + = new HashMap<>();
27 27
28 private final ExecutorService executor = Executors.newSingleThreadExecutor(); 28 private final ExecutorService executor = Executors.newSingleThreadExecutor();
29 private final List<IntentException> exceptions = new ArrayList<>(); 29 private final List<IntentException> exceptions = new ArrayList<>();
...@@ -40,8 +40,7 @@ public class FakeIntentManager implements TestableIntentService { ...@@ -40,8 +40,7 @@ public class FakeIntentManager implements TestableIntentService {
40 @Override 40 @Override
41 public void run() { 41 public void run() {
42 try { 42 try {
43 - List<InstallableIntent> installable = compileIntent(intent); 43 + executeCompilingPhase(intent);
44 - installIntents(intent, installable);
45 } catch (IntentException e) { 44 } catch (IntentException e) {
46 exceptions.add(e); 45 exceptions.add(e);
47 } 46 }
...@@ -55,8 +54,8 @@ public class FakeIntentManager implements TestableIntentService { ...@@ -55,8 +54,8 @@ public class FakeIntentManager implements TestableIntentService {
55 @Override 54 @Override
56 public void run() { 55 public void run() {
57 try { 56 try {
58 - List<InstallableIntent> installable = getInstallable(intent.getId()); 57 + List<InstallableIntent> installable = getInstallable(intent.id());
59 - uninstallIntents(intent, installable); 58 + executeWithdrawingPhase(intent, installable);
60 } catch (IntentException e) { 59 } catch (IntentException e) {
61 exceptions.add(e); 60 exceptions.add(e);
62 } 61 }
...@@ -76,61 +75,68 @@ public class FakeIntentManager implements TestableIntentService { ...@@ -76,61 +75,68 @@ public class FakeIntentManager implements TestableIntentService {
76 75
77 private <T extends InstallableIntent> IntentInstaller<T> getInstaller(T intent) { 76 private <T extends InstallableIntent> IntentInstaller<T> getInstaller(T intent) {
78 @SuppressWarnings("unchecked") 77 @SuppressWarnings("unchecked")
79 - IntentInstaller<T> installer = (IntentInstaller<T>) installers.get(intent.getClass()); 78 + IntentInstaller<T> installer = (IntentInstaller<T>) installers.get(intent
79 + .getClass());
80 if (installer == null) { 80 if (installer == null) {
81 throw new IntentException("no installer for class " + intent.getClass()); 81 throw new IntentException("no installer for class " + intent.getClass());
82 } 82 }
83 return installer; 83 return installer;
84 } 84 }
85 85
86 - private <T extends Intent> List<InstallableIntent> compileIntent(T intent) { 86 + private <T extends Intent> void executeCompilingPhase(T intent) {
87 + setState(intent, IntentState.COMPILING);
87 try { 88 try {
88 // For the fake, we compile using a single level pass 89 // For the fake, we compile using a single level pass
89 List<InstallableIntent> installable = new ArrayList<>(); 90 List<InstallableIntent> installable = new ArrayList<>();
90 for (Intent compiled : getCompiler(intent).compile(intent)) { 91 for (Intent compiled : getCompiler(intent).compile(intent)) {
91 installable.add((InstallableIntent) compiled); 92 installable.add((InstallableIntent) compiled);
92 } 93 }
93 - setState(intent, IntentState.COMPILED); 94 + executeInstallingPhase(intent, installable);
94 - return installable; 95 +
95 } catch (IntentException e) { 96 } catch (IntentException e) {
96 setState(intent, IntentState.FAILED); 97 setState(intent, IntentState.FAILED);
97 - throw e; 98 + dispatch(new IntentEvent(IntentEvent.Type.FAILED, intent));
98 } 99 }
99 } 100 }
100 101
101 - private void installIntents(Intent intent, List<InstallableIntent> installable) { 102 + private void executeInstallingPhase(Intent intent,
103 + List<InstallableIntent> installable) {
104 + setState(intent, IntentState.INSTALLING);
102 try { 105 try {
103 for (InstallableIntent ii : installable) { 106 for (InstallableIntent ii : installable) {
104 registerSubclassInstallerIfNeeded(ii); 107 registerSubclassInstallerIfNeeded(ii);
105 getInstaller(ii).install(ii); 108 getInstaller(ii).install(ii);
106 } 109 }
107 setState(intent, IntentState.INSTALLED); 110 setState(intent, IntentState.INSTALLED);
108 - putInstallable(intent.getId(), installable); 111 + putInstallable(intent.id(), installable);
112 + dispatch(new IntentEvent(IntentEvent.Type.INSTALLED, intent));
113 +
109 } catch (IntentException e) { 114 } catch (IntentException e) {
110 setState(intent, IntentState.FAILED); 115 setState(intent, IntentState.FAILED);
111 - throw e; 116 + dispatch(new IntentEvent(IntentEvent.Type.FAILED, intent));
112 } 117 }
113 } 118 }
114 119
115 - private void uninstallIntents(Intent intent, List<InstallableIntent> installable) { 120 + private void executeWithdrawingPhase(Intent intent,
121 + List<InstallableIntent> installable) {
122 + setState(intent, IntentState.WITHDRAWING);
116 try { 123 try {
117 for (InstallableIntent ii : installable) { 124 for (InstallableIntent ii : installable) {
118 getInstaller(ii).uninstall(ii); 125 getInstaller(ii).uninstall(ii);
119 } 126 }
127 + removeInstallable(intent.id());
120 setState(intent, IntentState.WITHDRAWN); 128 setState(intent, IntentState.WITHDRAWN);
121 - removeInstallable(intent.getId()); 129 + dispatch(new IntentEvent(IntentEvent.Type.WITHDRAWN, intent));
122 } catch (IntentException e) { 130 } catch (IntentException e) {
131 + // FIXME: Rework this to always go from WITHDRAWING to WITHDRAWN!
123 setState(intent, IntentState.FAILED); 132 setState(intent, IntentState.FAILED);
124 - throw e; 133 + dispatch(new IntentEvent(IntentEvent.Type.FAILED, intent));
125 } 134 }
126 } 135 }
127 136
128 -
129 // Sets the internal state for the given intent and dispatches an event 137 // Sets the internal state for the given intent and dispatches an event
130 private void setState(Intent intent, IntentState state) { 138 private void setState(Intent intent, IntentState state) {
131 - IntentState previous = intentStates.get(intent.getId()); 139 + intentStates.put(intent.id(), state);
132 - intentStates.put(intent.getId(), state);
133 - dispatch(new IntentEvent(intent, state, previous, System.currentTimeMillis()));
134 } 140 }
135 141
136 private void putInstallable(IntentId id, List<InstallableIntent> installable) { 142 private void putInstallable(IntentId id, List<InstallableIntent> installable) {
...@@ -152,15 +158,15 @@ public class FakeIntentManager implements TestableIntentService { ...@@ -152,15 +158,15 @@ public class FakeIntentManager implements TestableIntentService {
152 158
153 @Override 159 @Override
154 public void submit(Intent intent) { 160 public void submit(Intent intent) {
155 - intents.put(intent.getId(), intent); 161 + intents.put(intent.id(), intent);
156 setState(intent, IntentState.SUBMITTED); 162 setState(intent, IntentState.SUBMITTED);
163 + dispatch(new IntentEvent(IntentEvent.Type.SUBMITTED, intent));
157 executeSubmit(intent); 164 executeSubmit(intent);
158 } 165 }
159 166
160 @Override 167 @Override
161 public void withdraw(Intent intent) { 168 public void withdraw(Intent intent) {
162 - intents.remove(intent.getId()); 169 + intents.remove(intent.id());
163 - setState(intent, IntentState.WITHDRAWING);
164 executeWithdraw(intent); 170 executeWithdraw(intent);
165 } 171 }
166 172
...@@ -175,6 +181,11 @@ public class FakeIntentManager implements TestableIntentService { ...@@ -175,6 +181,11 @@ public class FakeIntentManager implements TestableIntentService {
175 } 181 }
176 182
177 @Override 183 @Override
184 + public long getIntentCount() {
185 + return intents.size();
186 + }
187 +
188 + @Override
178 public Intent getIntent(IntentId id) { 189 public Intent getIntent(IntentId id) {
179 return intents.get(id); 190 return intents.get(id);
180 } 191 }
...@@ -185,23 +196,24 @@ public class FakeIntentManager implements TestableIntentService { ...@@ -185,23 +196,24 @@ public class FakeIntentManager implements TestableIntentService {
185 } 196 }
186 197
187 @Override 198 @Override
188 - public void addListener(IntentEventListener listener) { 199 + public void addListener(IntentListener listener) {
189 listeners.add(listener); 200 listeners.add(listener);
190 } 201 }
191 202
192 @Override 203 @Override
193 - public void removeListener(IntentEventListener listener) { 204 + public void removeListener(IntentListener listener) {
194 listeners.remove(listener); 205 listeners.remove(listener);
195 } 206 }
196 207
197 private void dispatch(IntentEvent event) { 208 private void dispatch(IntentEvent event) {
198 - for (IntentEventListener listener : listeners) { 209 + for (IntentListener listener : listeners) {
199 listener.event(event); 210 listener.event(event);
200 } 211 }
201 } 212 }
202 213
203 @Override 214 @Override
204 - public <T extends Intent> void registerCompiler(Class<T> cls, IntentCompiler<T> compiler) { 215 + public <T extends Intent> void registerCompiler(Class<T> cls,
216 + IntentCompiler<T> compiler) {
205 compilers.put(cls, compiler); 217 compilers.put(cls, compiler);
206 } 218 }
207 219
...@@ -216,7 +228,8 @@ public class FakeIntentManager implements TestableIntentService { ...@@ -216,7 +228,8 @@ public class FakeIntentManager implements TestableIntentService {
216 } 228 }
217 229
218 @Override 230 @Override
219 - public <T extends InstallableIntent> void registerInstaller(Class<T> cls, IntentInstaller<T> installer) { 231 + public <T extends InstallableIntent> void registerInstaller(Class<T> cls,
232 + IntentInstaller<T> installer) {
220 installers.put(cls, installer); 233 installers.put(cls, installer);
221 } 234 }
222 235
...@@ -252,7 +265,8 @@ public class FakeIntentManager implements TestableIntentService { ...@@ -252,7 +265,8 @@ public class FakeIntentManager implements TestableIntentService {
252 if (!installers.containsKey(intent.getClass())) { 265 if (!installers.containsKey(intent.getClass())) {
253 Class<?> cls = intent.getClass(); 266 Class<?> cls = intent.getClass();
254 while (cls != Object.class) { 267 while (cls != Object.class) {
255 - // As long as we're within the InstallableIntent class descendants 268 + // As long as we're within the InstallableIntent class
269 + // descendants
256 if (InstallableIntent.class.isAssignableFrom(cls)) { 270 if (InstallableIntent.class.isAssignableFrom(cls)) {
257 IntentInstaller<?> installer = installers.get(cls); 271 IntentInstaller<?> installer = installers.get(cls);
258 if (installer != null) { 272 if (installer != null) {
......
...@@ -10,11 +10,9 @@ import java.util.Collections; ...@@ -10,11 +10,9 @@ import java.util.Collections;
10 import java.util.Iterator; 10 import java.util.Iterator;
11 import java.util.List; 11 import java.util.List;
12 12
13 -import static org.onlab.onos.net.intent.IntentState.*;
14 import static org.junit.Assert.*; 13 import static org.junit.Assert.*;
14 +import static org.onlab.onos.net.intent.IntentEvent.Type.*;
15 15
16 -// TODO: consider make it categorized as integration test when it become
17 -// slow test or fragile test
18 /** 16 /**
19 * Suite of tests for the intent service contract. 17 * Suite of tests for the intent service contract.
20 */ 18 */
...@@ -51,7 +49,7 @@ public class IntentServiceTest { ...@@ -51,7 +49,7 @@ public class IntentServiceTest {
51 @Test 49 @Test
52 public void basics() { 50 public void basics() {
53 // Make sure there are no intents 51 // Make sure there are no intents
54 - assertEquals("incorrect intent count", 0, service.getIntents().size()); 52 + assertEquals("incorrect intent count", 0, service.getIntentCount());
55 53
56 // Register a compiler and an installer both setup for success. 54 // Register a compiler and an installer both setup for success.
57 service.registerCompiler(TestIntent.class, new TestCompiler(new TestInstallableIntent(INSTALLABLE_IID))); 55 service.registerCompiler(TestIntent.class, new TestCompiler(new TestInstallableIntent(INSTALLABLE_IID)));
...@@ -64,17 +62,16 @@ public class IntentServiceTest { ...@@ -64,17 +62,16 @@ public class IntentServiceTest {
64 TestTools.assertAfter(GRACE_MS, new Runnable() { 62 TestTools.assertAfter(GRACE_MS, new Runnable() {
65 @Override 63 @Override
66 public void run() { 64 public void run() {
67 - assertEquals("incorrect intent state", INSTALLED, 65 + assertEquals("incorrect intent state", IntentState.INSTALLED,
68 - service.getIntentState(intent.getId())); 66 + service.getIntentState(intent.id()));
69 } 67 }
70 }); 68 });
71 69
72 // Make sure that all expected events have been emitted 70 // Make sure that all expected events have been emitted
73 - validateEvents(intent, SUBMITTED, COMPILED, INSTALLED); 71 + validateEvents(intent, SUBMITTED, INSTALLED);
74 72
75 // Make sure there is just one intent (and is ours) 73 // Make sure there is just one intent (and is ours)
76 - assertEquals("incorrect intent count", 1, service.getIntents().size()); 74 + assertEquals("incorrect intent count", 1, service.getIntentCount());
77 - assertEquals("incorrect intent", intent, service.getIntent(intent.getId()));
78 75
79 // Reset the listener events 76 // Reset the listener events
80 listener.events.clear(); 77 listener.events.clear();
...@@ -86,19 +83,19 @@ public class IntentServiceTest { ...@@ -86,19 +83,19 @@ public class IntentServiceTest {
86 TestTools.assertAfter(GRACE_MS, new Runnable() { 83 TestTools.assertAfter(GRACE_MS, new Runnable() {
87 @Override 84 @Override
88 public void run() { 85 public void run() {
89 - assertEquals("incorrect intent state", WITHDRAWN, 86 + assertEquals("incorrect intent state", IntentState.WITHDRAWN,
90 - service.getIntentState(intent.getId())); 87 + service.getIntentState(intent.id()));
91 } 88 }
92 }); 89 });
93 90
94 // Make sure that all expected events have been emitted 91 // Make sure that all expected events have been emitted
95 - validateEvents(intent, WITHDRAWING, WITHDRAWN); 92 + validateEvents(intent, WITHDRAWN);
96 93
97 // TODO: discuss what is the fate of intents after they have been withdrawn 94 // TODO: discuss what is the fate of intents after they have been withdrawn
98 // Make sure that the intent is no longer in the system 95 // Make sure that the intent is no longer in the system
99 // assertEquals("incorrect intent count", 0, service.getIntents().size()); 96 // assertEquals("incorrect intent count", 0, service.getIntents().size());
100 -// assertNull("intent should not be found", service.getIntent(intent.getId())); 97 +// assertNull("intent should not be found", service.getIntent(intent.id()));
101 -// assertNull("intent state should not be found", service.getIntentState(intent.getId())); 98 +// assertNull("intent state should not be found", service.getIntentState(intent.id()));
102 } 99 }
103 100
104 @Test 101 @Test
...@@ -114,8 +111,8 @@ public class IntentServiceTest { ...@@ -114,8 +111,8 @@ public class IntentServiceTest {
114 TestTools.assertAfter(GRACE_MS, new Runnable() { 111 TestTools.assertAfter(GRACE_MS, new Runnable() {
115 @Override 112 @Override
116 public void run() { 113 public void run() {
117 - assertEquals("incorrect intent state", FAILED, 114 + assertEquals("incorrect intent state", IntentState.FAILED,
118 - service.getIntentState(intent.getId())); 115 + service.getIntentState(intent.id()));
119 } 116 }
120 }); 117 });
121 118
...@@ -137,13 +134,13 @@ public class IntentServiceTest { ...@@ -137,13 +134,13 @@ public class IntentServiceTest {
137 TestTools.assertAfter(GRACE_MS, new Runnable() { 134 TestTools.assertAfter(GRACE_MS, new Runnable() {
138 @Override 135 @Override
139 public void run() { 136 public void run() {
140 - assertEquals("incorrect intent state", FAILED, 137 + assertEquals("incorrect intent state", IntentState.FAILED,
141 - service.getIntentState(intent.getId())); 138 + service.getIntentState(intent.id()));
142 } 139 }
143 }); 140 });
144 141
145 // Make sure that all expected events have been emitted 142 // Make sure that all expected events have been emitted
146 - validateEvents(intent, SUBMITTED, COMPILED, FAILED); 143 + validateEvents(intent, SUBMITTED, FAILED);
147 } 144 }
148 145
149 /** 146 /**
...@@ -152,23 +149,23 @@ public class IntentServiceTest { ...@@ -152,23 +149,23 @@ public class IntentServiceTest {
152 * considered. 149 * considered.
153 * 150 *
154 * @param intent intent subject 151 * @param intent intent subject
155 - * @param states list of states for which events are expected 152 + * @param types list of event types for which events are expected
156 */ 153 */
157 - protected void validateEvents(Intent intent, IntentState... states) { 154 + protected void validateEvents(Intent intent, IntentEvent.Type... types) {
158 Iterator<IntentEvent> events = listener.events.iterator(); 155 Iterator<IntentEvent> events = listener.events.iterator();
159 - for (IntentState state : states) { 156 + for (IntentEvent.Type type : types) {
160 IntentEvent event = events.hasNext() ? events.next() : null; 157 IntentEvent event = events.hasNext() ? events.next() : null;
161 if (event == null) { 158 if (event == null) {
162 - fail("expected event not found: " + state); 159 + fail("expected event not found: " + type);
163 - } else if (intent.equals(event.getIntent())) { 160 + } else if (intent.equals(event.subject())) {
164 - assertEquals("incorrect state", state, event.getState()); 161 + assertEquals("incorrect state", type, event.type());
165 } 162 }
166 } 163 }
167 164
168 // Remainder of events should not apply to this intent; make sure. 165 // Remainder of events should not apply to this intent; make sure.
169 while (events.hasNext()) { 166 while (events.hasNext()) {
170 assertFalse("unexpected event for intent", 167 assertFalse("unexpected event for intent",
171 - intent.equals(events.next().getIntent())); 168 + intent.equals(events.next().subject()));
172 } 169 }
173 } 170 }
174 171
...@@ -229,8 +226,8 @@ public class IntentServiceTest { ...@@ -229,8 +226,8 @@ public class IntentServiceTest {
229 TestTools.assertAfter(GRACE_MS, new Runnable() { 226 TestTools.assertAfter(GRACE_MS, new Runnable() {
230 @Override 227 @Override
231 public void run() { 228 public void run() {
232 - assertEquals("incorrect intent state", INSTALLED, 229 + assertEquals("incorrect intent state", IntentState.INSTALLED,
233 - service.getIntentState(intent.getId())); 230 + service.getIntentState(intent.id()));
234 } 231 }
235 }); 232 });
236 233
...@@ -250,7 +247,7 @@ public class IntentServiceTest { ...@@ -250,7 +247,7 @@ public class IntentServiceTest {
250 247
251 248
252 // Fixture to track emitted intent events 249 // Fixture to track emitted intent events
253 - protected class TestListener implements IntentEventListener { 250 + protected class TestListener implements IntentListener {
254 final List<IntentEvent> events = new ArrayList<>(); 251 final List<IntentEvent> events = new ArrayList<>();
255 252
256 @Override 253 @Override
......
...@@ -12,10 +12,10 @@ public class MultiPointToSinglePointIntentTest extends ConnectivityIntentTest { ...@@ -12,10 +12,10 @@ public class MultiPointToSinglePointIntentTest extends ConnectivityIntentTest {
12 @Test 12 @Test
13 public void basics() { 13 public void basics() {
14 MultiPointToSinglePointIntent intent = createOne(); 14 MultiPointToSinglePointIntent intent = createOne();
15 - assertEquals("incorrect id", IID, intent.getId()); 15 + assertEquals("incorrect id", IID, intent.id());
16 - assertEquals("incorrect match", MATCH, intent.getTrafficSelector()); 16 + assertEquals("incorrect match", MATCH, intent.selector());
17 - assertEquals("incorrect ingress", PS1, intent.getIngressPorts()); 17 + assertEquals("incorrect ingress", PS1, intent.ingressPoints());
18 - assertEquals("incorrect egress", P2, intent.getEgressPort()); 18 + assertEquals("incorrect egress", P2, intent.egressPoint());
19 } 19 }
20 20
21 @Override 21 @Override
......
...@@ -16,12 +16,12 @@ public class PathIntentTest extends ConnectivityIntentTest { ...@@ -16,12 +16,12 @@ public class PathIntentTest extends ConnectivityIntentTest {
16 @Test 16 @Test
17 public void basics() { 17 public void basics() {
18 PathIntent intent = createOne(); 18 PathIntent intent = createOne();
19 - assertEquals("incorrect id", IID, intent.getId()); 19 + assertEquals("incorrect id", IID, intent.id());
20 - assertEquals("incorrect match", MATCH, intent.getTrafficSelector()); 20 + assertEquals("incorrect match", MATCH, intent.selector());
21 - assertEquals("incorrect action", NOP, intent.getTrafficTreatment()); 21 + assertEquals("incorrect action", NOP, intent.treatment());
22 - assertEquals("incorrect ingress", P1, intent.getIngressPort()); 22 + assertEquals("incorrect ingress", P1, intent.ingressPoint());
23 - assertEquals("incorrect egress", P2, intent.getEgressPort()); 23 + assertEquals("incorrect egress", P2, intent.egressPoint());
24 - assertEquals("incorrect path", PATH1, intent.getPath()); 24 + assertEquals("incorrect path", PATH1, intent.path());
25 } 25 }
26 26
27 @Override 27 @Override
......
...@@ -12,10 +12,10 @@ public class PointToPointIntentTest extends ConnectivityIntentTest { ...@@ -12,10 +12,10 @@ public class PointToPointIntentTest extends ConnectivityIntentTest {
12 @Test 12 @Test
13 public void basics() { 13 public void basics() {
14 PointToPointIntent intent = createOne(); 14 PointToPointIntent intent = createOne();
15 - assertEquals("incorrect id", IID, intent.getId()); 15 + assertEquals("incorrect id", IID, intent.id());
16 - assertEquals("incorrect match", MATCH, intent.getTrafficSelector()); 16 + assertEquals("incorrect match", MATCH, intent.selector());
17 - assertEquals("incorrect ingress", P1, intent.getIngressPort()); 17 + assertEquals("incorrect ingress", P1, intent.ingressPoint());
18 - assertEquals("incorrect egress", P2, intent.getEgressPort()); 18 + assertEquals("incorrect egress", P2, intent.egressPoint());
19 } 19 }
20 20
21 @Override 21 @Override
......
...@@ -12,10 +12,10 @@ public class SinglePointToMultiPointIntentTest extends ConnectivityIntentTest { ...@@ -12,10 +12,10 @@ public class SinglePointToMultiPointIntentTest extends ConnectivityIntentTest {
12 @Test 12 @Test
13 public void basics() { 13 public void basics() {
14 SinglePointToMultiPointIntent intent = createOne(); 14 SinglePointToMultiPointIntent intent = createOne();
15 - assertEquals("incorrect id", IID, intent.getId()); 15 + assertEquals("incorrect id", IID, intent.id());
16 - assertEquals("incorrect match", MATCH, intent.getTrafficSelector()); 16 + assertEquals("incorrect match", MATCH, intent.selector());
17 - assertEquals("incorrect ingress", P1, intent.getIngressPort()); 17 + assertEquals("incorrect ingress", P1, intent.ingressPoint());
18 - assertEquals("incorrect egress", PS2, intent.getEgressPorts()); 18 + assertEquals("incorrect egress", PS2, intent.egressPoints());
19 } 19 }
20 20
21 @Override 21 @Override
......
1 package org.onlab.onos.net.intent; 1 package org.onlab.onos.net.intent;
2 //TODO is this the right package? 2 //TODO is this the right package?
3 3
4 +import org.onlab.onos.net.Link;
5 +
6 +import java.util.Collection;
7 +
4 /** 8 /**
5 * An installable intent used in the unit test. 9 * An installable intent used in the unit test.
6 * 10 *
...@@ -25,4 +29,8 @@ public class TestInstallableIntent extends AbstractIntent implements Installable ...@@ -25,4 +29,8 @@ public class TestInstallableIntent extends AbstractIntent implements Installable
25 super(); 29 super();
26 } 30 }
27 31
32 + @Override
33 + public Collection<Link> requiredLinks() {
34 + return null;
35 + }
28 } 36 }
......
...@@ -37,5 +37,4 @@ public class DefaultGraphDescriptionTest { ...@@ -37,5 +37,4 @@ public class DefaultGraphDescriptionTest {
37 new DefaultGraphDescription(4321L, ImmutableSet.of(DEV1, DEV3), 37 new DefaultGraphDescription(4321L, ImmutableSet.of(DEV1, DEV3),
38 ImmutableSet.of(L1, L2)); 38 ImmutableSet.of(L1, L2));
39 } 39 }
40 -
41 } 40 }
......
...@@ -50,5 +50,4 @@ public class DefaultTopologyEdgeTest { ...@@ -50,5 +50,4 @@ public class DefaultTopologyEdgeTest {
50 new DefaultTopologyEdge(V2, V1, L2)) 50 new DefaultTopologyEdge(V2, V1, L2))
51 .testEquals(); 51 .testEquals();
52 } 52 }
53 -
54 } 53 }
......
...@@ -26,5 +26,4 @@ public class DefaultTopologyVertexTest { ...@@ -26,5 +26,4 @@ public class DefaultTopologyVertexTest {
26 .addEqualityGroup(new DefaultTopologyVertex(D2), 26 .addEqualityGroup(new DefaultTopologyVertex(D2),
27 new DefaultTopologyVertex(D2)).testEquals(); 27 new DefaultTopologyVertex(D2)).testEquals();
28 } 28 }
29 -
30 } 29 }
......
...@@ -36,6 +36,12 @@ ...@@ -36,6 +36,12 @@
36 <scope>test</scope> 36 <scope>test</scope>
37 </dependency> 37 </dependency>
38 38
39 + <dependency>
40 + <groupId>org.easymock</groupId>
41 + <artifactId>easymock</artifactId>
42 + <scope>test</scope>
43 + </dependency>
44 +
39 <!-- TODO Consider removing store dependency. 45 <!-- TODO Consider removing store dependency.
40 Currently required for DistributedDeviceManagerTest. --> 46 Currently required for DistributedDeviceManagerTest. -->
41 <dependency> 47 <dependency>
......
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 +}
...@@ -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;
...@@ -38,7 +39,7 @@ import org.onlab.onos.net.device.DeviceStoreDelegate; ...@@ -38,7 +39,7 @@ import org.onlab.onos.net.device.DeviceStoreDelegate;
38 import org.onlab.onos.net.device.PortDescription; 39 import org.onlab.onos.net.device.PortDescription;
39 import org.onlab.onos.net.provider.AbstractProviderRegistry; 40 import org.onlab.onos.net.provider.AbstractProviderRegistry;
40 import org.onlab.onos.net.provider.AbstractProviderService; 41 import org.onlab.onos.net.provider.AbstractProviderService;
41 -import org.onlab.onos.store.ClockService; 42 +import org.onlab.onos.store.ClockProviderService;
42 import org.slf4j.Logger; 43 import org.slf4j.Logger;
43 44
44 /** 45 /**
...@@ -80,7 +81,7 @@ public class DeviceManager ...@@ -80,7 +81,7 @@ public class DeviceManager
80 protected MastershipTermService termService; 81 protected MastershipTermService termService;
81 82
82 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) 83 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
83 - protected ClockService clockService; 84 + protected ClockProviderService clockProviderService;
84 85
85 @Activate 86 @Activate
86 public void activate() { 87 public void activate() {
...@@ -144,6 +145,10 @@ public class DeviceManager ...@@ -144,6 +145,10 @@ public class DeviceManager
144 private void applyRole(DeviceId deviceId, MastershipRole newRole) { 145 private void applyRole(DeviceId deviceId, MastershipRole newRole) {
145 if (newRole.equals(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,13 +198,50 @@ public class DeviceManager ...@@ -193,13 +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
224 + // If there was a change of any kind, tell the provider
225 + // that this instance is the master.
226 + // Note: event can be null, if mastership was lost between
227 + // roleRequest and store update calls.
199 if (event != null) { 228 if (event != null) {
200 - log.info("Device {} connected", deviceId); 229 + // TODO: Check switch reconnected case. Is it assured that
201 - provider().roleChanged(event.subject(), 230 + // event will never be null?
202 - mastershipService.requestRoleFor(deviceId)); 231 + // Could there be a situation MastershipService told this
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);
203 post(event); 245 post(event);
204 } 246 }
205 } 247 }
...@@ -208,6 +250,15 @@ public class DeviceManager ...@@ -208,6 +250,15 @@ public class DeviceManager
208 public void deviceDisconnected(DeviceId deviceId) { 250 public void deviceDisconnected(DeviceId deviceId) {
209 checkNotNull(deviceId, DEVICE_ID_NULL); 251 checkNotNull(deviceId, DEVICE_ID_NULL);
210 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 + return;
261 + }
211 DeviceEvent event = store.markOffline(deviceId); 262 DeviceEvent event = store.markOffline(deviceId);
212 //we're no longer capable of being master or a candidate. 263 //we're no longer capable of being master or a candidate.
213 mastershipService.relinquishMastership(deviceId); 264 mastershipService.relinquishMastership(deviceId);
...@@ -253,6 +304,9 @@ public class DeviceManager ...@@ -253,6 +304,9 @@ public class DeviceManager
253 // FIXME: implement response to this notification 304 // FIXME: implement response to this notification
254 log.warn("Failed to assert role [{}] onto Device {}", role, 305 log.warn("Failed to assert role [{}] onto Device {}", role,
255 deviceId); 306 deviceId);
307 + if (role == MastershipRole.MASTER) {
308 + mastershipService.relinquishMastership(deviceId);
309 + }
256 } 310 }
257 } 311 }
258 312
...@@ -268,11 +322,17 @@ public class DeviceManager ...@@ -268,11 +322,17 @@ public class DeviceManager
268 322
269 @Override 323 @Override
270 public void event(MastershipEvent event) { 324 public void event(MastershipEvent event) {
271 - DeviceId did = event.subject(); 325 + final DeviceId did = event.subject();
272 if (isAvailable(did)) { 326 if (isAvailable(did)) {
273 - if (event.master().equals(clusterService.getLocalNode().id())) { 327 + final NodeId myNodeId = clusterService.getLocalNode().id();
328 +
329 + if (myNodeId.equals(event.master())) {
274 MastershipTerm term = termService.getMastershipTerm(did); 330 MastershipTerm term = termService.getMastershipTerm(did);
275 - clockService.setMastershipTerm(did, term); 331 +
332 + if (term.master().equals(myNodeId)) {
333 + // only set the new term if I am the master
334 + clockProviderService.setMastershipTerm(did, term);
335 + }
276 applyRole(did, MastershipRole.MASTER); 336 applyRole(did, MastershipRole.MASTER);
277 } else { 337 } else {
278 applyRole(did, MastershipRole.STANDBY); 338 applyRole(did, MastershipRole.STANDBY);
......
...@@ -5,9 +5,10 @@ import static org.slf4j.LoggerFactory.getLogger; ...@@ -5,9 +5,10 @@ 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.Map; 8 +import java.util.concurrent.ExecutionException;
9 -import java.util.concurrent.ConcurrentHashMap; 9 +import java.util.concurrent.Future;
10 -import java.util.concurrent.atomic.AtomicInteger; 10 +import java.util.concurrent.TimeUnit;
11 +import java.util.concurrent.TimeoutException;
11 12
12 import org.apache.felix.scr.annotations.Activate; 13 import org.apache.felix.scr.annotations.Activate;
13 import org.apache.felix.scr.annotations.Component; 14 import org.apache.felix.scr.annotations.Component;
...@@ -21,7 +22,11 @@ import org.onlab.onos.event.EventDeliveryService; ...@@ -21,7 +22,11 @@ import org.onlab.onos.event.EventDeliveryService;
21 import org.onlab.onos.net.Device; 22 import org.onlab.onos.net.Device;
22 import org.onlab.onos.net.DeviceId; 23 import org.onlab.onos.net.DeviceId;
23 import org.onlab.onos.net.device.DeviceService; 24 import org.onlab.onos.net.device.DeviceService;
25 +import org.onlab.onos.net.flow.CompletedBatchOperation;
26 +import org.onlab.onos.net.flow.FlowEntry;
24 import org.onlab.onos.net.flow.FlowRule; 27 import org.onlab.onos.net.flow.FlowRule;
28 +import org.onlab.onos.net.flow.FlowRuleBatchEntry;
29 +import org.onlab.onos.net.flow.FlowRuleBatchOperation;
25 import org.onlab.onos.net.flow.FlowRuleEvent; 30 import org.onlab.onos.net.flow.FlowRuleEvent;
26 import org.onlab.onos.net.flow.FlowRuleListener; 31 import org.onlab.onos.net.flow.FlowRuleListener;
27 import org.onlab.onos.net.flow.FlowRuleProvider; 32 import org.onlab.onos.net.flow.FlowRuleProvider;
...@@ -34,7 +39,9 @@ import org.onlab.onos.net.provider.AbstractProviderRegistry; ...@@ -34,7 +39,9 @@ import org.onlab.onos.net.provider.AbstractProviderRegistry;
34 import org.onlab.onos.net.provider.AbstractProviderService; 39 import org.onlab.onos.net.provider.AbstractProviderService;
35 import org.slf4j.Logger; 40 import org.slf4j.Logger;
36 41
42 +import com.google.common.collect.ArrayListMultimap;
37 import com.google.common.collect.Lists; 43 import com.google.common.collect.Lists;
44 +import com.google.common.collect.Multimap;
38 45
39 /** 46 /**
40 * Provides implementation of the flow NB &amp; SB APIs. 47 * Provides implementation of the flow NB &amp; SB APIs.
...@@ -42,8 +49,8 @@ import com.google.common.collect.Lists; ...@@ -42,8 +49,8 @@ import com.google.common.collect.Lists;
42 @Component(immediate = true) 49 @Component(immediate = true)
43 @Service 50 @Service
44 public class FlowRuleManager 51 public class FlowRuleManager
45 -extends AbstractProviderRegistry<FlowRuleProvider, FlowRuleProviderService> 52 + extends AbstractProviderRegistry<FlowRuleProvider, FlowRuleProviderService>
46 -implements FlowRuleService, FlowRuleProviderRegistry { 53 + implements FlowRuleService, FlowRuleProviderRegistry {
47 54
48 public static final String FLOW_RULE_NULL = "FlowRule cannot be null"; 55 public static final String FLOW_RULE_NULL = "FlowRule cannot be null";
49 private final Logger log = getLogger(getClass()); 56 private final Logger log = getLogger(getClass());
...@@ -62,8 +69,6 @@ implements FlowRuleService, FlowRuleProviderRegistry { ...@@ -62,8 +69,6 @@ implements FlowRuleService, FlowRuleProviderRegistry {
62 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) 69 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
63 protected DeviceService deviceService; 70 protected DeviceService deviceService;
64 71
65 - private final Map<FlowRule, AtomicInteger> deadRounds = new ConcurrentHashMap<>();
66 -
67 @Activate 72 @Activate
68 public void activate() { 73 public void activate() {
69 store.setDelegate(delegate); 74 store.setDelegate(delegate);
...@@ -79,7 +84,12 @@ implements FlowRuleService, FlowRuleProviderRegistry { ...@@ -79,7 +84,12 @@ implements FlowRuleService, FlowRuleProviderRegistry {
79 } 84 }
80 85
81 @Override 86 @Override
82 - public Iterable<FlowRule> getFlowEntries(DeviceId deviceId) { 87 + public int getFlowRuleCount() {
88 + return store.getFlowRuleCount();
89 + }
90 +
91 + @Override
92 + public Iterable<FlowEntry> getFlowEntries(DeviceId deviceId) {
83 return store.getFlowEntries(deviceId); 93 return store.getFlowEntries(deviceId);
84 } 94 }
85 95
...@@ -89,7 +99,6 @@ implements FlowRuleService, FlowRuleProviderRegistry { ...@@ -89,7 +99,6 @@ implements FlowRuleService, FlowRuleProviderRegistry {
89 FlowRule f = flowRules[i]; 99 FlowRule f = flowRules[i];
90 final Device device = deviceService.getDevice(f.deviceId()); 100 final Device device = deviceService.getDevice(f.deviceId());
91 final FlowRuleProvider frp = getProvider(device.providerId()); 101 final FlowRuleProvider frp = getProvider(device.providerId());
92 - deadRounds.put(f, new AtomicInteger(0));
93 store.storeFlowRule(f); 102 store.storeFlowRule(f);
94 frp.applyFlowRule(f); 103 frp.applyFlowRule(f);
95 } 104 }
...@@ -103,12 +112,13 @@ implements FlowRuleService, FlowRuleProviderRegistry { ...@@ -103,12 +112,13 @@ implements FlowRuleService, FlowRuleProviderRegistry {
103 for (int i = 0; i < flowRules.length; i++) { 112 for (int i = 0; i < flowRules.length; i++) {
104 f = flowRules[i]; 113 f = flowRules[i];
105 device = deviceService.getDevice(f.deviceId()); 114 device = deviceService.getDevice(f.deviceId());
106 - frp = getProvider(device.providerId());
107 - deadRounds.remove(f);
108 store.deleteFlowRule(f); 115 store.deleteFlowRule(f);
116 + if (device != null) {
117 + frp = getProvider(device.providerId());
109 frp.removeFlowRule(f); 118 frp.removeFlowRule(f);
110 } 119 }
111 } 120 }
121 + }
112 122
113 @Override 123 @Override
114 public void removeFlowRulesById(ApplicationId id) { 124 public void removeFlowRulesById(ApplicationId id) {
...@@ -126,7 +136,39 @@ implements FlowRuleService, FlowRuleProviderRegistry { ...@@ -126,7 +136,39 @@ implements FlowRuleService, FlowRuleProviderRegistry {
126 136
127 @Override 137 @Override
128 public Iterable<FlowRule> getFlowRulesById(ApplicationId id) { 138 public Iterable<FlowRule> getFlowRulesById(ApplicationId id) {
129 - return store.getFlowEntriesByAppId(id); 139 + return store.getFlowRulesByAppId(id);
140 + }
141 +
142 + @Override
143 + public Future<CompletedBatchOperation> applyBatch(
144 + FlowRuleBatchOperation batch) {
145 + Multimap<FlowRuleProvider, FlowRuleBatchEntry> batches =
146 + ArrayListMultimap.create();
147 + List<Future<Void>> futures = Lists.newArrayList();
148 + for (FlowRuleBatchEntry fbe : batch.getOperations()) {
149 + final FlowRule f = fbe.getTarget();
150 + final Device device = deviceService.getDevice(f.deviceId());
151 + final FlowRuleProvider frp = getProvider(device.providerId());
152 + batches.put(frp, fbe);
153 + switch (fbe.getOperator()) {
154 + case ADD:
155 + store.storeFlowRule(f);
156 + break;
157 + case REMOVE:
158 + store.deleteFlowRule(f);
159 + break;
160 + case MODIFY:
161 + default:
162 + log.error("Batch operation type {} unsupported.", fbe.getOperator());
163 + }
164 + }
165 + for (FlowRuleProvider provider : batches.keySet()) {
166 + FlowRuleBatchOperation b =
167 + new FlowRuleBatchOperation(batches.get(provider));
168 + Future<Void> future = provider.executeBatch(b);
169 + futures.add(future);
170 + }
171 + return new FlowRuleBatchFuture(futures);
130 } 172 }
131 173
132 @Override 174 @Override
...@@ -154,15 +196,15 @@ implements FlowRuleService, FlowRuleProviderRegistry { ...@@ -154,15 +196,15 @@ implements FlowRuleService, FlowRuleProviderRegistry {
154 } 196 }
155 197
156 @Override 198 @Override
157 - public void flowRemoved(FlowRule flowRule) { 199 + public void flowRemoved(FlowEntry flowEntry) {
158 - checkNotNull(flowRule, FLOW_RULE_NULL); 200 + checkNotNull(flowEntry, FLOW_RULE_NULL);
159 checkValidity(); 201 checkValidity();
160 - FlowRule stored = store.getFlowRule(flowRule); 202 + FlowEntry stored = store.getFlowEntry(flowEntry);
161 if (stored == null) { 203 if (stored == null) {
162 - log.debug("Rule already evicted from store: {}", flowRule); 204 + log.info("Rule already evicted from store: {}", flowEntry);
163 return; 205 return;
164 } 206 }
165 - Device device = deviceService.getDevice(flowRule.deviceId()); 207 + Device device = deviceService.getDevice(flowEntry.deviceId());
166 FlowRuleProvider frp = getProvider(device.providerId()); 208 FlowRuleProvider frp = getProvider(device.providerId());
167 FlowRuleEvent event = null; 209 FlowRuleEvent event = null;
168 switch (stored.state()) { 210 switch (stored.state()) {
...@@ -172,20 +214,20 @@ implements FlowRuleService, FlowRuleProviderRegistry { ...@@ -172,20 +214,20 @@ implements FlowRuleService, FlowRuleProviderRegistry {
172 break; 214 break;
173 case PENDING_REMOVE: 215 case PENDING_REMOVE:
174 case REMOVED: 216 case REMOVED:
175 - event = store.removeFlowRule(flowRule); 217 + event = store.removeFlowRule(stored);
176 break; 218 break;
177 default: 219 default:
178 break; 220 break;
179 221
180 } 222 }
181 if (event != null) { 223 if (event != null) {
182 - log.debug("Flow {} removed", flowRule); 224 + log.debug("Flow {} removed", flowEntry);
183 post(event); 225 post(event);
184 } 226 }
185 } 227 }
186 228
187 229
188 - private void flowMissing(FlowRule flowRule) { 230 + private void flowMissing(FlowEntry flowRule) {
189 checkNotNull(flowRule, FLOW_RULE_NULL); 231 checkNotNull(flowRule, FLOW_RULE_NULL);
190 checkValidity(); 232 checkValidity();
191 Device device = deviceService.getDevice(flowRule.deviceId()); 233 Device device = deviceService.getDevice(flowRule.deviceId());
...@@ -221,36 +263,40 @@ implements FlowRuleService, FlowRuleProviderRegistry { ...@@ -221,36 +263,40 @@ implements FlowRuleService, FlowRuleProviderRegistry {
221 } 263 }
222 264
223 265
224 - private void flowAdded(FlowRule flowRule) { 266 + private void flowAdded(FlowEntry flowEntry) {
225 - checkNotNull(flowRule, FLOW_RULE_NULL); 267 + checkNotNull(flowEntry, FLOW_RULE_NULL);
226 checkValidity(); 268 checkValidity();
227 269
228 - if (deadRounds.containsKey(flowRule) && 270 + if (checkRuleLiveness(flowEntry, store.getFlowEntry(flowEntry))) {
229 - checkRuleLiveness(flowRule, store.getFlowRule(flowRule))) {
230 271
231 - FlowRuleEvent event = store.addOrUpdateFlowRule(flowRule); 272 + FlowRuleEvent event = store.addOrUpdateFlowRule(flowEntry);
232 if (event == null) { 273 if (event == null) {
233 log.debug("No flow store event generated."); 274 log.debug("No flow store event generated.");
234 } else { 275 } else {
235 - log.debug("Flow {} {}", flowRule, event.type()); 276 + log.debug("Flow {} {}", flowEntry, event.type());
236 post(event); 277 post(event);
237 } 278 }
238 } else { 279 } else {
239 - removeFlowRules(flowRule); 280 + removeFlowRules(flowEntry);
240 } 281 }
241 282
242 } 283 }
243 284
244 - private boolean checkRuleLiveness(FlowRule swRule, FlowRule storedRule) { 285 + private boolean checkRuleLiveness(FlowEntry swRule, FlowEntry storedRule) {
245 - int timeout = storedRule.timeout(); 286 + if (storedRule == null) {
287 + return false;
288 + }
289 + long timeout = storedRule.timeout() * 1000;
290 + Long currentTime = System.currentTimeMillis();
246 if (storedRule.packets() != swRule.packets()) { 291 if (storedRule.packets() != swRule.packets()) {
247 - deadRounds.get(swRule).set(0); 292 + storedRule.setLastSeen();
248 return true; 293 return true;
249 } 294 }
250 295
251 - return (deadRounds.get(swRule).getAndIncrement() * 296 + if ((currentTime - storedRule.lastSeen()) <= timeout) {
252 - FlowRuleProvider.POLL_INTERVAL) <= timeout; 297 + return true;
253 - 298 + }
299 + return false;
254 } 300 }
255 301
256 // Posts the specified event to the local event dispatcher. 302 // Posts the specified event to the local event dispatcher.
...@@ -261,13 +307,13 @@ implements FlowRuleService, FlowRuleProviderRegistry { ...@@ -261,13 +307,13 @@ implements FlowRuleService, FlowRuleProviderRegistry {
261 } 307 }
262 308
263 @Override 309 @Override
264 - public void pushFlowMetrics(DeviceId deviceId, Iterable<FlowRule> flowEntries) { 310 + public void pushFlowMetrics(DeviceId deviceId, Iterable<FlowEntry> flowEntries) {
265 - List<FlowRule> storedRules = Lists.newLinkedList(store.getFlowEntries(deviceId)); 311 + List<FlowEntry> storedRules = Lists.newLinkedList(store.getFlowEntries(deviceId));
266 312
267 - Iterator<FlowRule> switchRulesIterator = flowEntries.iterator(); 313 + Iterator<FlowEntry> switchRulesIterator = flowEntries.iterator();
268 314
269 while (switchRulesIterator.hasNext()) { 315 while (switchRulesIterator.hasNext()) {
270 - FlowRule rule = switchRulesIterator.next(); 316 + FlowEntry rule = switchRulesIterator.next();
271 if (storedRules.remove(rule)) { 317 if (storedRules.remove(rule)) {
272 // we both have the rule, let's update some info then. 318 // we both have the rule, let's update some info then.
273 flowAdded(rule); 319 flowAdded(rule);
...@@ -276,7 +322,7 @@ implements FlowRuleService, FlowRuleProviderRegistry { ...@@ -276,7 +322,7 @@ implements FlowRuleService, FlowRuleProviderRegistry {
276 extraneousFlow(rule); 322 extraneousFlow(rule);
277 } 323 }
278 } 324 }
279 - for (FlowRule rule : storedRules) { 325 + for (FlowEntry rule : storedRules) {
280 // there are rules in the store that aren't on the switch 326 // there are rules in the store that aren't on the switch
281 flowMissing(rule); 327 flowMissing(rule);
282 328
...@@ -291,4 +337,63 @@ implements FlowRuleService, FlowRuleProviderRegistry { ...@@ -291,4 +337,63 @@ implements FlowRuleService, FlowRuleProviderRegistry {
291 eventDispatcher.post(event); 337 eventDispatcher.post(event);
292 } 338 }
293 } 339 }
340 +
341 + private class FlowRuleBatchFuture
342 + implements Future<CompletedBatchOperation> {
343 +
344 + private final List<Future<Void>> futures;
345 +
346 + public FlowRuleBatchFuture(List<Future<Void>> futures) {
347 + this.futures = futures;
348 + }
349 +
350 + @Override
351 + public boolean cancel(boolean mayInterruptIfRunning) {
352 + // TODO Auto-generated method stub
353 + return false;
354 + }
355 +
356 + @Override
357 + public boolean isCancelled() {
358 + // TODO Auto-generated method stub
359 + return false;
360 + }
361 +
362 + @Override
363 + public boolean isDone() {
364 + boolean isDone = true;
365 + for (Future<Void> future : futures) {
366 + isDone &= future.isDone();
367 + }
368 + return isDone;
369 + }
370 +
371 + @Override
372 + public CompletedBatchOperation get() throws InterruptedException,
373 + ExecutionException {
374 + // TODO Auto-generated method stub
375 + for (Future<Void> future : futures) {
376 + future.get();
377 + }
378 + return new CompletedBatchOperation();
379 + }
380 +
381 + @Override
382 + public CompletedBatchOperation get(long timeout, TimeUnit unit)
383 + throws InterruptedException, ExecutionException,
384 + TimeoutException {
385 + // TODO we should decrement the timeout
386 + long start = System.nanoTime();
387 + long end = start + unit.toNanos(timeout);
388 + for (Future<Void> future : futures) {
389 + long now = System.nanoTime();
390 + long thisTimeout = end - now;
391 + future.get(thisTimeout, TimeUnit.NANOSECONDS);
392 + }
393 + return new CompletedBatchOperation();
394 + }
395 +
396 + }
397 +
398 +
294 } 399 }
......
...@@ -76,7 +76,7 @@ public class HostManager ...@@ -76,7 +76,7 @@ public class HostManager
76 eventDispatcher.addSink(HostEvent.class, listenerRegistry); 76 eventDispatcher.addSink(HostEvent.class, listenerRegistry);
77 77
78 monitor = new HostMonitor(deviceService, packetService, this); 78 monitor = new HostMonitor(deviceService, packetService, this);
79 - 79 + monitor.start();
80 } 80 }
81 81
82 @Deactivate 82 @Deactivate
......
...@@ -2,11 +2,11 @@ package org.onlab.onos.net.host.impl; ...@@ -2,11 +2,11 @@ package org.onlab.onos.net.host.impl;
2 2
3 import java.nio.ByteBuffer; 3 import java.nio.ByteBuffer;
4 import java.util.ArrayList; 4 import java.util.ArrayList;
5 -import java.util.HashSet; 5 +import java.util.Collections;
6 import java.util.List; 6 import java.util.List;
7 -import java.util.Map;
8 import java.util.Set; 7 import java.util.Set;
9 import java.util.concurrent.ConcurrentHashMap; 8 import java.util.concurrent.ConcurrentHashMap;
9 +import java.util.concurrent.ConcurrentMap;
10 import java.util.concurrent.TimeUnit; 10 import java.util.concurrent.TimeUnit;
11 11
12 import org.jboss.netty.util.Timeout; 12 import org.jboss.netty.util.Timeout;
...@@ -33,8 +33,6 @@ import org.onlab.packet.IpAddress; ...@@ -33,8 +33,6 @@ import org.onlab.packet.IpAddress;
33 import org.onlab.packet.IpPrefix; 33 import org.onlab.packet.IpPrefix;
34 import org.onlab.packet.MacAddress; 34 import org.onlab.packet.MacAddress;
35 import org.onlab.util.Timer; 35 import org.onlab.util.Timer;
36 -import org.slf4j.Logger;
37 -import org.slf4j.LoggerFactory;
38 36
39 /** 37 /**
40 * Monitors hosts on the dataplane to detect changes in host data. 38 * Monitors hosts on the dataplane to detect changes in host data.
...@@ -44,70 +42,91 @@ import org.slf4j.LoggerFactory; ...@@ -44,70 +42,91 @@ import org.slf4j.LoggerFactory;
44 * probe for hosts that have not yet been detected (specified by IP address). 42 * probe for hosts that have not yet been detected (specified by IP address).
45 */ 43 */
46 public class HostMonitor implements TimerTask { 44 public class HostMonitor implements TimerTask {
47 - private static final Logger log = LoggerFactory.getLogger(HostMonitor.class);
48 -
49 - private static final byte[] ZERO_MAC_ADDRESS =
50 - MacAddress.valueOf("00:00:00:00:00:00").getAddress();
51 -
52 - // TODO put on Ethernet
53 - private static final byte[] BROADCAST_MAC =
54 - MacAddress.valueOf("ff:ff:ff:ff:ff:ff").getAddress();
55 -
56 private DeviceService deviceService; 45 private DeviceService deviceService;
57 private PacketService packetService; 46 private PacketService packetService;
58 private HostManager hostManager; 47 private HostManager hostManager;
59 48
60 private final Set<IpAddress> monitoredAddresses; 49 private final Set<IpAddress> monitoredAddresses;
61 50
62 - private final Map<ProviderId, HostProvider> hostProviders; 51 + private final ConcurrentMap<ProviderId, HostProvider> hostProviders;
63 52
64 - private final long probeRate; 53 + private static final long DEFAULT_PROBE_RATE = 30000; // milliseconds
54 + private long probeRate = DEFAULT_PROBE_RATE;
65 55
66 - private final Timeout timeout; 56 + private Timeout timeout;
67 57
68 - public HostMonitor( 58 + /**
69 - DeviceService deviceService, 59 + * Creates a new host monitor.
70 - PacketService packetService, 60 + *
71 - HostManager hostService) { 61 + * @param deviceService device service used to find edge ports
62 + * @param packetService packet service used to send packets on the data plane
63 + * @param hostManager host manager used to look up host information and
64 + * probe existing hosts
65 + */
66 + public HostMonitor(DeviceService deviceService, PacketService packetService,
67 + HostManager hostManager) {
72 68
73 this.deviceService = deviceService; 69 this.deviceService = deviceService;
74 this.packetService = packetService; 70 this.packetService = packetService;
75 - this.hostManager = hostService; 71 + this.hostManager = hostManager;
76 72
77 - monitoredAddresses = new HashSet<>(); 73 + monitoredAddresses = Collections.newSetFromMap(
74 + new ConcurrentHashMap<IpAddress, Boolean>());
78 hostProviders = new ConcurrentHashMap<>(); 75 hostProviders = new ConcurrentHashMap<>();
79 76
80 - probeRate = 30000; // milliseconds
81 -
82 timeout = Timer.getTimer().newTimeout(this, 0, TimeUnit.MILLISECONDS); 77 timeout = Timer.getTimer().newTimeout(this, 0, TimeUnit.MILLISECONDS);
83 -
84 - addDefaultAddresses();
85 - }
86 -
87 - private void addDefaultAddresses() {
88 - //monitoredAddresses.add(IpAddress.valueOf("10.0.0.1"));
89 } 78 }
90 79
80 + /**
81 + * Adds an IP address to be monitored by the host monitor. The monitor will
82 + * periodically probe the host to detect changes.
83 + *
84 + * @param ip IP address of the host to monitor
85 + */
91 void addMonitoringFor(IpAddress ip) { 86 void addMonitoringFor(IpAddress ip) {
92 monitoredAddresses.add(ip); 87 monitoredAddresses.add(ip);
93 } 88 }
94 89
90 + /**
91 + * Stops monitoring the given IP address.
92 + *
93 + * @param ip IP address to stop monitoring on
94 + */
95 void stopMonitoring(IpAddress ip) { 95 void stopMonitoring(IpAddress ip) {
96 monitoredAddresses.remove(ip); 96 monitoredAddresses.remove(ip);
97 } 97 }
98 98
99 + /**
100 + * Starts the host monitor. Does nothing if the monitor is already running.
101 + */
102 + void start() {
103 + synchronized (this) {
104 + if (timeout == null) {
105 + timeout = Timer.getTimer().newTimeout(this, 0, TimeUnit.MILLISECONDS);
106 + }
107 + }
108 + }
109 +
110 + /**
111 + * Stops the host monitor.
112 + */
99 void shutdown() { 113 void shutdown() {
114 + synchronized (this) {
100 timeout.cancel(); 115 timeout.cancel();
116 + timeout = null;
117 + }
101 } 118 }
102 119
120 + /**
121 + * Registers a host provider with the host monitor. The monitor can use the
122 + * provider to probe hosts.
123 + *
124 + * @param provider the host provider to register
125 + */
103 void registerHostProvider(HostProvider provider) { 126 void registerHostProvider(HostProvider provider) {
104 hostProviders.put(provider.id(), provider); 127 hostProviders.put(provider.id(), provider);
105 } 128 }
106 129
107 - void unregisterHostProvider(HostProvider provider) {
108 - // TODO find out how to call this
109 - }
110 -
111 @Override 130 @Override
112 public void run(Timeout timeout) throws Exception { 131 public void run(Timeout timeout) throws Exception {
113 for (IpAddress ip : monitoredAddresses) { 132 for (IpAddress ip : monitoredAddresses) {
...@@ -121,14 +140,16 @@ public class HostMonitor implements TimerTask { ...@@ -121,14 +140,16 @@ public class HostMonitor implements TimerTask {
121 } else { 140 } else {
122 for (Host host : hosts) { 141 for (Host host : hosts) {
123 HostProvider provider = hostProviders.get(host.providerId()); 142 HostProvider provider = hostProviders.get(host.providerId());
124 - if (provider != null) { 143 + if (provider == null) {
144 + hostProviders.remove(host.providerId(), null);
145 + } else {
125 provider.triggerProbe(host); 146 provider.triggerProbe(host);
126 } 147 }
127 } 148 }
128 } 149 }
129 } 150 }
130 151
131 - timeout = Timer.getTimer().newTimeout(this, probeRate, TimeUnit.MILLISECONDS); 152 + this.timeout = Timer.getTimer().newTimeout(this, probeRate, TimeUnit.MILLISECONDS);
132 } 153 }
133 154
134 /** 155 /**
...@@ -161,7 +182,7 @@ public class HostMonitor implements TimerTask { ...@@ -161,7 +182,7 @@ public class HostMonitor implements TimerTask {
161 List<Instruction> instructions = new ArrayList<>(); 182 List<Instruction> instructions = new ArrayList<>();
162 instructions.add(Instructions.createOutput(port.number())); 183 instructions.add(Instructions.createOutput(port.number()));
163 184
164 - TrafficTreatment treatment = new DefaultTrafficTreatment.Builder() 185 + TrafficTreatment treatment = DefaultTrafficTreatment.builder()
165 .setOutput(port.number()) 186 .setOutput(port.number())
166 .build(); 187 .build();
167 188
...@@ -184,12 +205,12 @@ public class HostMonitor implements TimerTask { ...@@ -184,12 +205,12 @@ public class HostMonitor implements TimerTask {
184 205
185 arp.setSenderHardwareAddress(sourceMac.getAddress()) 206 arp.setSenderHardwareAddress(sourceMac.getAddress())
186 .setSenderProtocolAddress(sourceIp.toOctets()) 207 .setSenderProtocolAddress(sourceIp.toOctets())
187 - .setTargetHardwareAddress(ZERO_MAC_ADDRESS) 208 + .setTargetHardwareAddress(MacAddress.ZERO_MAC_ADDRESS)
188 .setTargetProtocolAddress(targetIp.toOctets()); 209 .setTargetProtocolAddress(targetIp.toOctets());
189 210
190 Ethernet ethernet = new Ethernet(); 211 Ethernet ethernet = new Ethernet();
191 ethernet.setEtherType(Ethernet.TYPE_ARP) 212 ethernet.setEtherType(Ethernet.TYPE_ARP)
192 - .setDestinationMACAddress(BROADCAST_MAC) 213 + .setDestinationMACAddress(MacAddress.BROADCAST_MAC)
193 .setSourceMACAddress(sourceMac.getAddress()) 214 .setSourceMACAddress(sourceMac.getAddress())
194 .setPayload(arp); 215 .setPayload(arp);
195 216
......
1 +package org.onlab.onos.net.intent.impl;
2 +
3 +import org.onlab.onos.net.intent.IdGenerator;
4 +
5 +/**
6 + * Base class of {@link IdGenerator} implementations which use {@link IdBlockAllocator} as
7 + * backend.
8 + *
9 + * @param <T> the type of ID
10 + */
11 +public abstract class AbstractBlockAllocatorBasedIdGenerator<T> implements IdGenerator<T> {
12 + protected final IdBlockAllocator allocator;
13 + protected IdBlock idBlock;
14 +
15 + /**
16 + * Constructs an ID generator which use {@link IdBlockAllocator} as backend.
17 + *
18 + * @param allocator
19 + */
20 + protected AbstractBlockAllocatorBasedIdGenerator(IdBlockAllocator allocator) {
21 + this.allocator = allocator;
22 + this.idBlock = allocator.allocateUniqueIdBlock();
23 + }
24 +
25 + @Override
26 + public synchronized T getNewId() {
27 + try {
28 + return convertFrom(idBlock.getNextId());
29 + } catch (UnavailableIdException e) {
30 + idBlock = allocator.allocateUniqueIdBlock();
31 + return convertFrom(idBlock.getNextId());
32 + }
33 + }
34 +
35 + /**
36 + * Returns an ID instance of {@code T} type from the long value.
37 + *
38 + * @param value original long value
39 + * @return ID instance
40 + */
41 + protected abstract T convertFrom(long value);
42 +}
1 +package org.onlab.onos.net.intent.impl;
2 +
3 +public class DummyIdBlockAllocator implements IdBlockAllocator {
4 + private long blockTop;
5 + private static final long BLOCK_SIZE = 0x1000000L;
6 +
7 + /**
8 + * Returns a block of IDs which are unique and unused.
9 + * Range of IDs is fixed size and is assigned incrementally as this method
10 + * called.
11 + *
12 + * @return an IdBlock containing a set of unique IDs
13 + */
14 + @Override
15 + public IdBlock allocateUniqueIdBlock() {
16 + synchronized (this) {
17 + long blockHead = blockTop;
18 + long blockTail = blockTop + BLOCK_SIZE;
19 +
20 + IdBlock block = new IdBlock(blockHead, BLOCK_SIZE);
21 + blockTop = blockTail;
22 +
23 + return block;
24 + }
25 + }
26 +
27 + @Override
28 + public IdBlock allocateUniqueIdBlock(long range) {
29 + throw new UnsupportedOperationException("Not supported yet");
30 + }
31 +}
1 +package org.onlab.onos.net.intent.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.Deactivate;
6 +import org.apache.felix.scr.annotations.Reference;
7 +import org.apache.felix.scr.annotations.ReferenceCardinality;
8 +import org.onlab.onos.net.Host;
9 +import org.onlab.onos.net.HostId;
10 +import org.onlab.onos.net.Path;
11 +import org.onlab.onos.net.flow.TrafficSelector;
12 +import org.onlab.onos.net.host.HostService;
13 +import org.onlab.onos.net.intent.HostToHostIntent;
14 +import org.onlab.onos.net.intent.IdGenerator;
15 +import org.onlab.onos.net.intent.Intent;
16 +import org.onlab.onos.net.intent.IntentCompiler;
17 +import org.onlab.onos.net.intent.IntentExtensionService;
18 +import org.onlab.onos.net.intent.IntentId;
19 +import org.onlab.onos.net.intent.PathIntent;
20 +import org.onlab.onos.net.topology.PathService;
21 +
22 +import java.util.Arrays;
23 +import java.util.List;
24 +import java.util.Set;
25 +
26 +import static org.onlab.onos.net.flow.DefaultTrafficSelector.builder;
27 +
28 +/**
29 + * A intent compiler for {@link HostToHostIntent}.
30 + */
31 +@Component(immediate = true)
32 +public class HostToHostIntentCompiler
33 + implements IntentCompiler<HostToHostIntent> {
34 +
35 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
36 + protected IntentExtensionService intentManager;
37 +
38 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
39 + protected PathService pathService;
40 +
41 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
42 + protected HostService hostService;
43 +
44 + private IdGenerator<IntentId> intentIdGenerator;
45 +
46 + @Activate
47 + public void activate() {
48 + IdBlockAllocator idBlockAllocator = new DummyIdBlockAllocator();
49 + intentIdGenerator = new IdBlockAllocatorBasedIntentIdGenerator(idBlockAllocator);
50 + intentManager.registerCompiler(HostToHostIntent.class, this);
51 + }
52 +
53 + @Deactivate
54 + public void deactivate() {
55 + intentManager.unregisterCompiler(HostToHostIntent.class);
56 + }
57 +
58 + @Override
59 + public List<Intent> compile(HostToHostIntent intent) {
60 + Path pathOne = getPath(intent.one(), intent.two());
61 + Path pathTwo = getPath(intent.two(), intent.one());
62 +
63 + Host one = hostService.getHost(intent.one());
64 + Host two = hostService.getHost(intent.two());
65 +
66 + return Arrays.asList(createPathIntent(pathOne, one, two, intent),
67 + createPathIntent(pathTwo, two, one, intent));
68 + }
69 +
70 + // Creates a path intent from the specified path and original connectivity intent.
71 + private Intent createPathIntent(Path path, Host src, Host dst,
72 + HostToHostIntent intent) {
73 +
74 + TrafficSelector selector = builder(intent.selector())
75 + .matchEthSrc(src.mac()).matchEthDst(dst.mac()).build();
76 +
77 + return new PathIntent(intentIdGenerator.getNewId(),
78 + selector, intent.treatment(),
79 + path.src(), path.dst(), path);
80 + }
81 +
82 + private Path getPath(HostId one, HostId two) {
83 + Set<Path> paths = pathService.getPaths(one, two);
84 + if (paths.isEmpty()) {
85 + throw new PathNotFoundException("No path from host " + one + " to " + two);
86 + }
87 + // TODO: let's be more intelligent about this eventually
88 + return paths.iterator().next();
89 + }
90 +}
1 +package org.onlab.onos.net.intent.impl;
2 +
3 +import static com.google.common.base.Preconditions.checkArgument;
4 +
5 +import java.util.Objects;
6 +import java.util.concurrent.atomic.AtomicLong;
7 +
8 +import com.google.common.base.MoreObjects;
9 +
10 +/**
11 + * A class representing an ID space.
12 + */
13 +public final class IdBlock {
14 + private final long start;
15 + private final long size;
16 +
17 + private final AtomicLong currentId;
18 +
19 + /**
20 + * Constructs a new ID block with the specified size and initial value.
21 + *
22 + * @param start initial value of the block
23 + * @param size size of the block
24 + * @throws IllegalArgumentException if the size is less than or equal to 0
25 + */
26 + public IdBlock(long start, long size) {
27 + checkArgument(size > 0, "size should be more than 0, but %s", size);
28 +
29 + this.start = start;
30 + this.size = size;
31 +
32 + this.currentId = new AtomicLong(start);
33 + }
34 +
35 + // TODO: consider if this method is needed or not
36 + /**
37 + * Returns the initial value.
38 + *
39 + * @return initial value
40 + */
41 + public long getStart() {
42 + return start;
43 + }
44 +
45 + // TODO: consider if this method is needed or not
46 + /**
47 + * Returns the last value.
48 + *
49 + * @return last value
50 + */
51 + public long getEnd() {
52 + return start + size - 1;
53 + }
54 +
55 + /**
56 + * Returns the block size.
57 + *
58 + * @return block size
59 + */
60 + public long getSize() {
61 + return size;
62 + }
63 +
64 + /**
65 + * Returns the next ID in the block.
66 + *
67 + * @return next ID
68 + * @throws UnavailableIdException if there is no available ID in the block.
69 + */
70 + public long getNextId() {
71 + final long id = currentId.getAndIncrement();
72 + if (id > getEnd()) {
73 + throw new UnavailableIdException(String.format(
74 + "used all IDs in allocated space (size: %d, end: %d, current: %d)",
75 + size, getEnd(), id
76 + ));
77 + }
78 +
79 + return id;
80 + }
81 +
82 + // TODO: Do we really need equals and hashCode? Should it contain currentId?
83 + @Override
84 + public boolean equals(Object o) {
85 + if (this == o) {
86 + return true;
87 + }
88 + if (o == null || getClass() != o.getClass()) {
89 + return false;
90 + }
91 +
92 + IdBlock that = (IdBlock) o;
93 + return Objects.equals(this.start, that.start)
94 + && Objects.equals(this.size, that.size)
95 + && Objects.equals(this.currentId.get(), that.currentId.get());
96 + }
97 +
98 + @Override
99 + public int hashCode() {
100 + return Objects.hash(start, size, currentId);
101 + }
102 +
103 + @Override
104 + public String toString() {
105 + return MoreObjects.toStringHelper(getClass())
106 + .add("start", start)
107 + .add("size", size)
108 + .add("currentId", currentId)
109 + .toString();
110 + }
111 +}
1 +package org.onlab.onos.net.intent.impl;
2 +
3 +/**
4 + * An interface that gives unique ID spaces.
5 + */
6 +public interface IdBlockAllocator {
7 + /**
8 + * Allocates a unique Id Block.
9 + *
10 + * @return Id Block.
11 + */
12 + IdBlock allocateUniqueIdBlock();
13 +
14 + /**
15 + * Allocates next unique id and retrieve a new range of ids if needed.
16 + *
17 + * @param range range to use for the identifier
18 + * @return Id Block.
19 + */
20 + IdBlock allocateUniqueIdBlock(long range);
21 +}
1 +package org.onlab.onos.net.intent.impl;
2 +
3 +import org.onlab.onos.net.intent.IntentId;
4 +
5 +/**
6 + * An implementation of {@link org.onlab.onos.net.intent.IdGenerator} of intent ID,
7 + * which uses {@link IdBlockAllocator}.
8 + */
9 +public class IdBlockAllocatorBasedIntentIdGenerator extends AbstractBlockAllocatorBasedIdGenerator<IntentId> {
10 +
11 + /**
12 + * Constructs an intent ID generator, which uses the specified ID block allocator
13 + * to generate a global unique intent ID.
14 + *
15 + * @param allocator the ID block allocator to use for generating intent IDs
16 + */
17 + public IdBlockAllocatorBasedIntentIdGenerator(IdBlockAllocator allocator) {
18 + super(allocator);
19 + }
20 +
21 + @Override
22 + protected IntentId convertFrom(long value) {
23 + return new IntentId(value);
24 + }
25 +}
1 +package org.onlab.onos.net.intent.impl;
2 +
3 +import org.onlab.onos.net.intent.IntentException;
4 +
5 +/**
6 + * An exception thrown when a intent compilation fails.
7 + */
8 +public class IntentCompilationException extends IntentException {
9 + private static final long serialVersionUID = 235237603018210810L;
10 +
11 + public IntentCompilationException() {
12 + super();
13 + }
14 +
15 + public IntentCompilationException(String message) {
16 + super(message);
17 + }
18 +
19 + public IntentCompilationException(String message, Throwable cause) {
20 + super(message, cause);
21 + }
22 +}
1 +package org.onlab.onos.net.intent.impl;
2 +
3 +import org.onlab.onos.net.intent.IntentException;
4 +
5 +/**
6 + * An exception thrown when intent installation fails.
7 + */
8 +public class IntentInstallationException extends IntentException {
9 + private static final long serialVersionUID = 3720268258616014168L;
10 +
11 + public IntentInstallationException() {
12 + super();
13 + }
14 +
15 + public IntentInstallationException(String message) {
16 + super(message);
17 + }
18 +
19 + public IntentInstallationException(String message, Throwable cause) {
20 + super(message, cause);
21 + }
22 +}
1 +package org.onlab.onos.net.intent.impl;
2 +
3 +import static com.google.common.base.Preconditions.checkNotNull;
4 +import static java.util.concurrent.Executors.newSingleThreadExecutor;
5 +import static org.onlab.onos.net.intent.IntentState.COMPILING;
6 +import static org.onlab.onos.net.intent.IntentState.FAILED;
7 +import static org.onlab.onos.net.intent.IntentState.INSTALLED;
8 +import static org.onlab.onos.net.intent.IntentState.INSTALLING;
9 +import static org.onlab.onos.net.intent.IntentState.RECOMPILING;
10 +import static org.onlab.onos.net.intent.IntentState.WITHDRAWING;
11 +import static org.onlab.onos.net.intent.IntentState.WITHDRAWN;
12 +import static org.onlab.util.Tools.namedThreads;
13 +import static org.slf4j.LoggerFactory.getLogger;
14 +
15 +import java.util.ArrayList;
16 +import java.util.List;
17 +import java.util.Map;
18 +import java.util.Objects;
19 +import java.util.concurrent.ConcurrentHashMap;
20 +import java.util.concurrent.ConcurrentMap;
21 +import java.util.concurrent.ExecutorService;
22 +
23 +import org.apache.felix.scr.annotations.Activate;
24 +import org.apache.felix.scr.annotations.Component;
25 +import org.apache.felix.scr.annotations.Deactivate;
26 +import org.apache.felix.scr.annotations.Reference;
27 +import org.apache.felix.scr.annotations.ReferenceCardinality;
28 +import org.apache.felix.scr.annotations.Service;
29 +import org.onlab.onos.event.AbstractListenerRegistry;
30 +import org.onlab.onos.event.EventDeliveryService;
31 +import org.onlab.onos.net.intent.InstallableIntent;
32 +import org.onlab.onos.net.intent.Intent;
33 +import org.onlab.onos.net.intent.IntentCompiler;
34 +import org.onlab.onos.net.intent.IntentEvent;
35 +import org.onlab.onos.net.intent.IntentException;
36 +import org.onlab.onos.net.intent.IntentExtensionService;
37 +import org.onlab.onos.net.intent.IntentId;
38 +import org.onlab.onos.net.intent.IntentInstaller;
39 +import org.onlab.onos.net.intent.IntentListener;
40 +import org.onlab.onos.net.intent.IntentOperations;
41 +import org.onlab.onos.net.intent.IntentService;
42 +import org.onlab.onos.net.intent.IntentState;
43 +import org.onlab.onos.net.intent.IntentStore;
44 +import org.onlab.onos.net.intent.IntentStoreDelegate;
45 +import org.slf4j.Logger;
46 +
47 +import com.google.common.collect.ImmutableMap;
48 +
49 +/**
50 + * An implementation of Intent Manager.
51 + */
52 +@Component(immediate = true)
53 +@Service
54 +public class IntentManager
55 + implements IntentService, IntentExtensionService {
56 + private final Logger log = getLogger(getClass());
57 +
58 + public static final String INTENT_NULL = "Intent cannot be null";
59 + public static final String INTENT_ID_NULL = "Intent ID cannot be null";
60 +
61 + // Collections for compiler, installer, and listener are ONOS instance local
62 + private final ConcurrentMap<Class<? extends Intent>,
63 + IntentCompiler<? extends Intent>> compilers = new ConcurrentHashMap<>();
64 + private final ConcurrentMap<Class<? extends InstallableIntent>,
65 + IntentInstaller<? extends InstallableIntent>> installers = new ConcurrentHashMap<>();
66 +
67 + private final AbstractListenerRegistry<IntentEvent, IntentListener>
68 + listenerRegistry = new AbstractListenerRegistry<>();
69 +
70 + private final ExecutorService executor = newSingleThreadExecutor(namedThreads("onos-intents"));
71 +
72 + private final IntentStoreDelegate delegate = new InternalStoreDelegate();
73 + private final TopologyChangeDelegate topoDelegate = new InternalTopoChangeDelegate();
74 +
75 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
76 + protected IntentStore store;
77 +
78 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
79 + protected ObjectiveTrackerService trackerService;
80 +
81 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
82 + protected EventDeliveryService eventDispatcher;
83 +
84 + @Activate
85 + public void activate() {
86 + store.setDelegate(delegate);
87 + trackerService.setDelegate(topoDelegate);
88 + eventDispatcher.addSink(IntentEvent.class, listenerRegistry);
89 + log.info("Started");
90 + }
91 +
92 + @Deactivate
93 + public void deactivate() {
94 + store.unsetDelegate(delegate);
95 + trackerService.unsetDelegate(topoDelegate);
96 + eventDispatcher.removeSink(IntentEvent.class);
97 + log.info("Stopped");
98 + }
99 +
100 + @Override
101 + public void submit(Intent intent) {
102 + checkNotNull(intent, INTENT_NULL);
103 + registerSubclassCompilerIfNeeded(intent);
104 + IntentEvent event = store.createIntent(intent);
105 + if (event != null) {
106 + eventDispatcher.post(event);
107 + executor.execute(new IntentTask(COMPILING, intent));
108 + }
109 + }
110 +
111 + @Override
112 + public void withdraw(Intent intent) {
113 + checkNotNull(intent, INTENT_NULL);
114 + executor.execute(new IntentTask(WITHDRAWING, intent));
115 + }
116 +
117 + // FIXME: implement this method
118 + @Override
119 + public void execute(IntentOperations operations) {
120 + throw new UnsupportedOperationException("execute() is not implemented yet");
121 + }
122 +
123 + @Override
124 + public Iterable<Intent> getIntents() {
125 + return store.getIntents();
126 + }
127 +
128 + @Override
129 + public long getIntentCount() {
130 + return store.getIntentCount();
131 + }
132 +
133 + @Override
134 + public Intent getIntent(IntentId id) {
135 + checkNotNull(id, INTENT_ID_NULL);
136 + return store.getIntent(id);
137 + }
138 +
139 + @Override
140 + public IntentState getIntentState(IntentId id) {
141 + checkNotNull(id, INTENT_ID_NULL);
142 + return store.getIntentState(id);
143 + }
144 +
145 + @Override
146 + public void addListener(IntentListener listener) {
147 + listenerRegistry.addListener(listener);
148 + }
149 +
150 + @Override
151 + public void removeListener(IntentListener listener) {
152 + listenerRegistry.removeListener(listener);
153 + }
154 +
155 + @Override
156 + public <T extends Intent> void registerCompiler(Class<T> cls, IntentCompiler<T> compiler) {
157 + compilers.put(cls, compiler);
158 + }
159 +
160 + @Override
161 + public <T extends Intent> void unregisterCompiler(Class<T> cls) {
162 + compilers.remove(cls);
163 + }
164 +
165 + @Override
166 + public Map<Class<? extends Intent>, IntentCompiler<? extends Intent>> getCompilers() {
167 + return ImmutableMap.copyOf(compilers);
168 + }
169 +
170 + @Override
171 + public <T extends InstallableIntent> void registerInstaller(Class<T> cls, IntentInstaller<T> installer) {
172 + installers.put(cls, installer);
173 + }
174 +
175 + @Override
176 + public <T extends InstallableIntent> void unregisterInstaller(Class<T> cls) {
177 + installers.remove(cls);
178 + }
179 +
180 + @Override
181 + public Map<Class<? extends InstallableIntent>, IntentInstaller<? extends InstallableIntent>> getInstallers() {
182 + return ImmutableMap.copyOf(installers);
183 + }
184 +
185 + /**
186 + * Returns the corresponding intent compiler to the specified intent.
187 + *
188 + * @param intent intent
189 + * @param <T> the type of intent
190 + * @return intent compiler corresponding to the specified intent
191 + */
192 + private <T extends Intent> IntentCompiler<T> getCompiler(T intent) {
193 + @SuppressWarnings("unchecked")
194 + IntentCompiler<T> compiler = (IntentCompiler<T>) compilers.get(intent.getClass());
195 + if (compiler == null) {
196 + throw new IntentException("no compiler for class " + intent.getClass());
197 + }
198 + return compiler;
199 + }
200 +
201 + /**
202 + * Returns the corresponding intent installer to the specified installable intent.
203 + *
204 + * @param intent intent
205 + * @param <T> the type of installable intent
206 + * @return intent installer corresponding to the specified installable intent
207 + */
208 + private <T extends InstallableIntent> IntentInstaller<T> getInstaller(T intent) {
209 + @SuppressWarnings("unchecked")
210 + IntentInstaller<T> installer = (IntentInstaller<T>) installers.get(intent.getClass());
211 + if (installer == null) {
212 + throw new IntentException("no installer for class " + intent.getClass());
213 + }
214 + return installer;
215 + }
216 +
217 + /**
218 + * Compiles the specified intent.
219 + *
220 + * @param intent intent to be compiled
221 + */
222 + private void executeCompilingPhase(Intent intent) {
223 + // Indicate that the intent is entering the compiling phase.
224 + store.setState(intent, COMPILING);
225 +
226 + try {
227 + // Compile the intent into installable derivatives.
228 + List<InstallableIntent> installable = compileIntent(intent);
229 +
230 + // If all went well, associate the resulting list of installable
231 + // intents with the top-level intent and proceed to install.
232 + store.addInstallableIntents(intent.id(), installable);
233 + executeInstallingPhase(intent);
234 +
235 + } catch (Exception e) {
236 + log.warn("Unable to compile intent {} due to: {}", intent.id(), e);
237 +
238 + // If compilation failed, mark the intent as failed.
239 + store.setState(intent, FAILED);
240 + }
241 + }
242 +
243 + // FIXME: To make SDN-IP workable ASAP, only single level compilation is implemented
244 + // TODO: implement compilation traversing tree structure
245 + private List<InstallableIntent> compileIntent(Intent intent) {
246 + List<InstallableIntent> installable = new ArrayList<>();
247 + for (Intent compiled : getCompiler(intent).compile(intent)) {
248 + InstallableIntent installableIntent = (InstallableIntent) compiled;
249 + installable.add(installableIntent);
250 + }
251 + return installable;
252 + }
253 +
254 + /**
255 + * Installs all installable intents associated with the specified top-level
256 + * intent.
257 + *
258 + * @param intent intent to be installed
259 + */
260 + private void executeInstallingPhase(Intent intent) {
261 + // Indicate that the intent is entering the installing phase.
262 + store.setState(intent, INSTALLING);
263 +
264 + try {
265 + List<InstallableIntent> installables = store.getInstallableIntents(intent.id());
266 + if (installables != null) {
267 + for (InstallableIntent installable : installables) {
268 + registerSubclassInstallerIfNeeded(installable);
269 + trackerService.addTrackedResources(intent.id(),
270 + installable.requiredLinks());
271 + getInstaller(installable).install(installable);
272 + }
273 + }
274 + eventDispatcher.post(store.setState(intent, INSTALLED));
275 +
276 + } catch (Exception e) {
277 + log.warn("Unable to install intent {} due to: {}", intent.id(), e);
278 + uninstallIntent(intent);
279 +
280 + // If compilation failed, kick off the recompiling phase.
281 + executeRecompilingPhase(intent);
282 + }
283 + }
284 +
285 + /**
286 + * Recompiles the specified intent.
287 + *
288 + * @param intent intent to be recompiled
289 + */
290 + private void executeRecompilingPhase(Intent intent) {
291 + // Indicate that the intent is entering the recompiling phase.
292 + store.setState(intent, RECOMPILING);
293 +
294 + try {
295 + // Compile the intent into installable derivatives.
296 + List<InstallableIntent> installable = compileIntent(intent);
297 +
298 + // If all went well, compare the existing list of installable
299 + // intents with the newly compiled list. If they are the same,
300 + // bail, out since the previous approach was determined not to
301 + // be viable.
302 + List<InstallableIntent> originalInstallable =
303 + store.getInstallableIntents(intent.id());
304 +
305 + if (Objects.equals(originalInstallable, installable)) {
306 + eventDispatcher.post(store.setState(intent, FAILED));
307 + } else {
308 + // Otherwise, re-associate the newly compiled installable intents
309 + // with the top-level intent and kick off installing phase.
310 + store.addInstallableIntents(intent.id(), installable);
311 + executeInstallingPhase(intent);
312 + }
313 + } catch (Exception e) {
314 + log.warn("Unable to recompile intent {} due to: {}", intent.id(), e);
315 +
316 + // If compilation failed, mark the intent as failed.
317 + eventDispatcher.post(store.setState(intent, FAILED));
318 + }
319 + }
320 +
321 + /**
322 + * Uninstalls the specified intent by uninstalling all of its associated
323 + * installable derivatives.
324 + *
325 + * @param intent intent to be installed
326 + */
327 + private void executeWithdrawingPhase(Intent intent) {
328 + // Indicate that the intent is being withdrawn.
329 + store.setState(intent, WITHDRAWING);
330 + uninstallIntent(intent);
331 +
332 + // If all went well, disassociate the top-level intent with its
333 + // installable derivatives and mark it as withdrawn.
334 + store.removeInstalledIntents(intent.id());
335 + eventDispatcher.post(store.setState(intent, WITHDRAWN));
336 + }
337 +
338 + /**
339 + * Uninstalls all installable intents associated with the given intent.
340 + *
341 + * @param intent intent to be uninstalled
342 + */
343 + private void uninstallIntent(Intent intent) {
344 + try {
345 + List<InstallableIntent> installables = store.getInstallableIntents(intent.id());
346 + if (installables != null) {
347 + for (InstallableIntent installable : installables) {
348 + getInstaller(installable).uninstall(installable);
349 + }
350 + }
351 + } catch (IntentException e) {
352 + log.warn("Unable to uninstall intent {} due to: {}", intent.id(), e);
353 + }
354 + }
355 +
356 + /**
357 + * Registers an intent compiler of the specified intent if an intent compiler
358 + * for the intent is not registered. This method traverses the class hierarchy of
359 + * the intent. Once an intent compiler for a parent type is found, this method
360 + * registers the found intent compiler.
361 + *
362 + * @param intent intent
363 + */
364 + private void registerSubclassCompilerIfNeeded(Intent intent) {
365 + if (!compilers.containsKey(intent.getClass())) {
366 + Class<?> cls = intent.getClass();
367 + while (cls != Object.class) {
368 + // As long as we're within the Intent class descendants
369 + if (Intent.class.isAssignableFrom(cls)) {
370 + IntentCompiler<?> compiler = compilers.get(cls);
371 + if (compiler != null) {
372 + compilers.put(intent.getClass(), compiler);
373 + return;
374 + }
375 + }
376 + cls = cls.getSuperclass();
377 + }
378 + }
379 + }
380 +
381 + /**
382 + * Registers an intent installer of the specified intent if an intent installer
383 + * for the intent is not registered. This method traverses the class hierarchy of
384 + * the intent. Once an intent installer for a parent type is found, this method
385 + * registers the found intent installer.
386 + *
387 + * @param intent intent
388 + */
389 + private void registerSubclassInstallerIfNeeded(InstallableIntent intent) {
390 + if (!installers.containsKey(intent.getClass())) {
391 + Class<?> cls = intent.getClass();
392 + while (cls != Object.class) {
393 + // As long as we're within the InstallableIntent class descendants
394 + if (InstallableIntent.class.isAssignableFrom(cls)) {
395 + IntentInstaller<?> installer = installers.get(cls);
396 + if (installer != null) {
397 + installers.put(intent.getClass(), installer);
398 + return;
399 + }
400 + }
401 + cls = cls.getSuperclass();
402 + }
403 + }
404 + }
405 +
406 + // Store delegate to re-post events emitted from the store.
407 + private class InternalStoreDelegate implements IntentStoreDelegate {
408 + @Override
409 + public void notify(IntentEvent event) {
410 + eventDispatcher.post(event);
411 + if (event.type() == IntentEvent.Type.SUBMITTED) {
412 + executor.execute(new IntentTask(COMPILING, event.subject()));
413 + }
414 + }
415 + }
416 +
417 + // Topology change delegate
418 + private class InternalTopoChangeDelegate implements TopologyChangeDelegate {
419 + @Override
420 + public void triggerCompile(Iterable<IntentId> intentIds,
421 + boolean compileAllFailed) {
422 + // Attempt recompilation of the specified intents first.
423 + for (IntentId intentId : intentIds) {
424 + Intent intent = getIntent(intentId);
425 + uninstallIntent(intent);
426 +
427 + executeRecompilingPhase(intent);
428 + }
429 +
430 + if (compileAllFailed) {
431 + // If required, compile all currently failed intents.
432 + for (Intent intent : getIntents()) {
433 + if (getIntentState(intent.id()) == FAILED) {
434 + executeCompilingPhase(intent);
435 + }
436 + }
437 + }
438 + }
439 + }
440 +
441 + // Auxiliary runnable to perform asynchronous tasks.
442 + private class IntentTask implements Runnable {
443 + private final IntentState state;
444 + private final Intent intent;
445 +
446 + public IntentTask(IntentState state, Intent intent) {
447 + this.state = state;
448 + this.intent = intent;
449 + }
450 +
451 + @Override
452 + public void run() {
453 + if (state == COMPILING) {
454 + executeCompilingPhase(intent);
455 + } else if (state == RECOMPILING) {
456 + executeRecompilingPhase(intent);
457 + } else if (state == WITHDRAWING) {
458 + executeWithdrawingPhase(intent);
459 + }
460 + }
461 + }
462 +
463 +}
1 +package org.onlab.onos.net.intent.impl;
2 +
3 +import org.onlab.onos.net.intent.IntentException;
4 +
5 +/**
6 + * An exception thrown when intent removal failed.
7 + */
8 +public class IntentRemovalException extends IntentException {
9 + private static final long serialVersionUID = -5259226322037891951L;
10 +
11 + public IntentRemovalException() {
12 + super();
13 + }
14 +
15 + public IntentRemovalException(String message) {
16 + super(message);
17 + }
18 +
19 + public IntentRemovalException(String message, Throwable cause) {
20 + super(message, cause);
21 + }
22 +}
1 +package org.onlab.onos.net.intent.impl;
2 +
3 +import com.google.common.collect.HashMultimap;
4 +import com.google.common.collect.SetMultimap;
5 +import org.apache.felix.scr.annotations.Activate;
6 +import org.apache.felix.scr.annotations.Component;
7 +import org.apache.felix.scr.annotations.Deactivate;
8 +import org.apache.felix.scr.annotations.Reference;
9 +import org.apache.felix.scr.annotations.ReferenceCardinality;
10 +import org.apache.felix.scr.annotations.Service;
11 +import org.onlab.onos.event.Event;
12 +import org.onlab.onos.net.Link;
13 +import org.onlab.onos.net.LinkKey;
14 +import org.onlab.onos.net.intent.IntentId;
15 +import org.onlab.onos.net.link.LinkEvent;
16 +import org.onlab.onos.net.topology.TopologyEvent;
17 +import org.onlab.onos.net.topology.TopologyListener;
18 +import org.onlab.onos.net.topology.TopologyService;
19 +import org.slf4j.Logger;
20 +
21 +import java.util.Collection;
22 +import java.util.HashSet;
23 +import java.util.Set;
24 +import java.util.concurrent.ExecutorService;
25 +
26 +import static com.google.common.base.Preconditions.checkArgument;
27 +import static com.google.common.base.Preconditions.checkNotNull;
28 +import static com.google.common.collect.Multimaps.synchronizedSetMultimap;
29 +import static java.util.concurrent.Executors.newSingleThreadExecutor;
30 +import static org.onlab.onos.net.link.LinkEvent.Type.LINK_REMOVED;
31 +import static org.onlab.util.Tools.namedThreads;
32 +import static org.slf4j.LoggerFactory.getLogger;
33 +
34 +/**
35 + * Entity responsible for tracking installed flows and for monitoring topology
36 + * events to determine what flows are affected by topology changes.
37 + */
38 +@Component
39 +@Service
40 +public class ObjectiveTracker implements ObjectiveTrackerService {
41 +
42 + private final Logger log = getLogger(getClass());
43 +
44 + private final SetMultimap<LinkKey, IntentId> intentsByLink =
45 + synchronizedSetMultimap(HashMultimap.<LinkKey, IntentId>create());
46 +
47 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
48 + protected TopologyService topologyService;
49 +
50 + private ExecutorService executorService =
51 + newSingleThreadExecutor(namedThreads("onos-flowtracker"));
52 +
53 + private TopologyListener listener = new InternalTopologyListener();
54 + private TopologyChangeDelegate delegate;
55 +
56 + @Activate
57 + public void activate() {
58 + topologyService.addListener(listener);
59 + log.info("Started");
60 + }
61 +
62 + @Deactivate
63 + public void deactivate() {
64 + topologyService.removeListener(listener);
65 + log.info("Stopped");
66 + }
67 +
68 + @Override
69 + public void setDelegate(TopologyChangeDelegate delegate) {
70 + checkNotNull(delegate, "Delegate cannot be null");
71 + checkArgument(this.delegate == null || this.delegate == delegate,
72 + "Another delegate already set");
73 + this.delegate = delegate;
74 + }
75 +
76 + @Override
77 + public void unsetDelegate(TopologyChangeDelegate delegate) {
78 + checkArgument(this.delegate == delegate, "Not the current delegate");
79 + this.delegate = null;
80 + }
81 +
82 + @Override
83 + public void addTrackedResources(IntentId intentId, Collection<Link> resources) {
84 + for (Link link : resources) {
85 + intentsByLink.put(new LinkKey(link), intentId);
86 + }
87 + }
88 +
89 + @Override
90 + public void removeTrackedResources(IntentId intentId, Collection<Link> resources) {
91 + for (Link link : resources) {
92 + intentsByLink.remove(new LinkKey(link), intentId);
93 + }
94 + }
95 +
96 + // Internal re-actor to topology change events.
97 + private class InternalTopologyListener implements TopologyListener {
98 + @Override
99 + public void event(TopologyEvent event) {
100 + executorService.execute(new TopologyChangeHandler(event));
101 + }
102 + }
103 +
104 + // Re-dispatcher of topology change events.
105 + private class TopologyChangeHandler implements Runnable {
106 +
107 + private final TopologyEvent event;
108 +
109 + TopologyChangeHandler(TopologyEvent event) {
110 + this.event = event;
111 + }
112 +
113 + @Override
114 + public void run() {
115 + if (event.reasons() == null) {
116 + delegate.triggerCompile(new HashSet<IntentId>(), true);
117 +
118 + } else {
119 + Set<IntentId> toBeRecompiled = new HashSet<>();
120 + boolean recompileOnly = true;
121 +
122 + // Scan through the list of reasons and keep accruing all
123 + // intents that need to be recompiled.
124 + for (Event reason : event.reasons()) {
125 + if (reason instanceof LinkEvent) {
126 + LinkEvent linkEvent = (LinkEvent) reason;
127 + if (linkEvent.type() == LINK_REMOVED) {
128 + Set<IntentId> intentIds = intentsByLink.get(new LinkKey(linkEvent.subject()));
129 + toBeRecompiled.addAll(intentIds);
130 + }
131 + recompileOnly = recompileOnly && linkEvent.type() == LINK_REMOVED;
132 + }
133 + }
134 +
135 + delegate.triggerCompile(toBeRecompiled, !recompileOnly);
136 + }
137 + }
138 + }
139 +
140 +}
1 +package org.onlab.onos.net.intent.impl;
2 +
3 +import org.onlab.onos.net.Link;
4 +import org.onlab.onos.net.intent.IntentId;
5 +
6 +import java.util.Collection;
7 +
8 +/**
9 + * Auxiliary service for tracking intent path flows and for notifying the
10 + * intent service of environment changes via topology change delegate.
11 + */
12 +public interface ObjectiveTrackerService {
13 +
14 + /**
15 + * Sets a topology change delegate.
16 + *
17 + * @param delegate topology change delegate
18 + */
19 + void setDelegate(TopologyChangeDelegate delegate);
20 +
21 + /**
22 + * Unsets topology change delegate.
23 + *
24 + * @param delegate topology change delegate
25 + */
26 + void unsetDelegate(TopologyChangeDelegate delegate);
27 +
28 + /**
29 + * Adds a path flow to be tracked.
30 + *
31 + * @param intentId intent identity on whose behalf the path is being tracked
32 + * @param resources resources to track
33 + */
34 + public void addTrackedResources(IntentId intentId, Collection<Link> resources);
35 +
36 + /**
37 + * Removes a path flow to be tracked.
38 + *
39 + * @param intentId intent identity on whose behalf the path is being tracked
40 + * @param resources resources to stop tracking
41 + */
42 + public void removeTrackedResources(IntentId intentId, Collection<Link> resources);
43 +
44 +}
1 +package org.onlab.onos.net.intent.impl;
2 +
3 +import static org.onlab.onos.net.flow.DefaultTrafficTreatment.builder;
4 +import static org.slf4j.LoggerFactory.getLogger;
5 +
6 +import java.util.Iterator;
7 +import java.util.List;
8 +import java.util.concurrent.ExecutionException;
9 +
10 +import org.apache.felix.scr.annotations.Activate;
11 +import org.apache.felix.scr.annotations.Component;
12 +import org.apache.felix.scr.annotations.Deactivate;
13 +import org.apache.felix.scr.annotations.Reference;
14 +import org.apache.felix.scr.annotations.ReferenceCardinality;
15 +import org.onlab.onos.ApplicationId;
16 +import org.onlab.onos.net.ConnectPoint;
17 +import org.onlab.onos.net.Link;
18 +import org.onlab.onos.net.flow.DefaultFlowRule;
19 +import org.onlab.onos.net.flow.DefaultTrafficSelector;
20 +import org.onlab.onos.net.flow.FlowRule;
21 +import org.onlab.onos.net.flow.FlowRuleBatchEntry;
22 +import org.onlab.onos.net.flow.FlowRuleBatchEntry.FlowRuleOperation;
23 +import org.onlab.onos.net.flow.FlowRuleBatchOperation;
24 +import org.onlab.onos.net.flow.FlowRuleService;
25 +import org.onlab.onos.net.flow.TrafficSelector;
26 +import org.onlab.onos.net.flow.TrafficTreatment;
27 +import org.onlab.onos.net.intent.IntentExtensionService;
28 +import org.onlab.onos.net.intent.IntentInstaller;
29 +import org.onlab.onos.net.intent.PathIntent;
30 +import org.slf4j.Logger;
31 +
32 +import com.google.common.collect.Lists;
33 +
34 +/**
35 + * Installer for {@link PathIntent path connectivity intents}.
36 + */
37 +@Component(immediate = true)
38 +public class PathIntentInstaller implements IntentInstaller<PathIntent> {
39 +
40 + private final Logger log = getLogger(getClass());
41 +
42 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
43 + protected IntentExtensionService intentManager;
44 +
45 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
46 + protected FlowRuleService flowRuleService;
47 +
48 + private final ApplicationId appId = ApplicationId.getAppId();
49 +
50 + @Activate
51 + public void activate() {
52 + intentManager.registerInstaller(PathIntent.class, this);
53 + }
54 +
55 + @Deactivate
56 + public void deactivate() {
57 + intentManager.unregisterInstaller(PathIntent.class);
58 + }
59 +
60 + @Override
61 + public void install(PathIntent intent) {
62 + TrafficSelector.Builder builder =
63 + DefaultTrafficSelector.builder(intent.selector());
64 + Iterator<Link> links = intent.path().links().iterator();
65 + ConnectPoint prev = links.next().dst();
66 + List<FlowRuleBatchEntry> rules = Lists.newLinkedList();
67 + while (links.hasNext()) {
68 + builder.matchInport(prev.port());
69 + Link link = links.next();
70 + TrafficTreatment treatment = builder()
71 + .setOutput(link.src().port()).build();
72 +
73 + FlowRule rule = new DefaultFlowRule(link.src().deviceId(),
74 + builder.build(), treatment,
75 + 123, appId, 600);
76 + rules.add(new FlowRuleBatchEntry(FlowRuleOperation.ADD, rule));
77 + //flowRuleService.applyFlowRules(rule);
78 + prev = link.dst();
79 + }
80 + FlowRuleBatchOperation batch = new FlowRuleBatchOperation(rules);
81 + try {
82 + flowRuleService.applyBatch(batch).get();
83 + } catch (InterruptedException | ExecutionException e) {
84 + // TODO Auto-generated catch block
85 + e.printStackTrace();
86 + }
87 + }
88 +
89 + @Override
90 + public void uninstall(PathIntent intent) {
91 + TrafficSelector.Builder builder =
92 + DefaultTrafficSelector.builder(intent.selector());
93 + Iterator<Link> links = intent.path().links().iterator();
94 + ConnectPoint prev = links.next().dst();
95 + List<FlowRuleBatchEntry> rules = Lists.newLinkedList();
96 +
97 + while (links.hasNext()) {
98 + builder.matchInport(prev.port());
99 + Link link = links.next();
100 + TrafficTreatment treatment = builder()
101 + .setOutput(link.src().port()).build();
102 + FlowRule rule = new DefaultFlowRule(link.src().deviceId(),
103 + builder.build(), treatment,
104 + 123, appId, 600);
105 + rules.add(new FlowRuleBatchEntry(FlowRuleOperation.REMOVE, rule));
106 + //flowRuleService.removeFlowRules(rule);
107 + prev = link.dst();
108 + }
109 + FlowRuleBatchOperation batch = new FlowRuleBatchOperation(rules);
110 + try {
111 + flowRuleService.applyBatch(batch).get();
112 + } catch (InterruptedException | ExecutionException e) {
113 + // TODO Auto-generated catch block
114 + e.printStackTrace();
115 + }
116 + }
117 +}
1 +package org.onlab.onos.net.intent.impl;
2 +
3 +import org.onlab.onos.net.intent.IntentException;
4 +
5 +/**
6 + * An exception thrown when a path is not found.
7 + */
8 +public class PathNotFoundException extends IntentException {
9 + private static final long serialVersionUID = -2087045731049914733L;
10 +
11 + public PathNotFoundException() {
12 + super();
13 + }
14 +
15 + public PathNotFoundException(String message) {
16 + super(message);
17 + }
18 +
19 + public PathNotFoundException(String message, Throwable cause) {
20 + super(message, cause);
21 + }
22 +}
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.intent.impl;
2 +
3 +import org.onlab.onos.net.intent.IntentId;
4 +
5 +/**
6 + * Auxiliary delegate for integration of intent manager and flow trackerService.
7 + */
8 +public interface TopologyChangeDelegate {
9 +
10 + /**
11 + * Notifies that topology has changed in such a way that the specified
12 + * intents should be recompiled. If the {@code compileAllFailed} parameter
13 + * is true, then all intents in {@link org.onlab.onos.net.intent.IntentState#FAILED}
14 + * state should be compiled as well.
15 + *
16 + * @param intentIds intents that should be recompiled
17 + * @param compileAllFailed true implies full compile of all failed intents
18 + * is required; false for selective recompile only
19 + */
20 + void triggerCompile(Iterable<IntentId> intentIds, boolean compileAllFailed);
21 +
22 +}
1 +package org.onlab.onos.net.intent.impl;
2 +
3 +/**
4 + * Represents that there is no available IDs.
5 + */
6 +public class UnavailableIdException extends RuntimeException {
7 +
8 + private static final long serialVersionUID = -2287403908433720122L;
9 +
10 + /**
11 + * Constructs an exception with no message and no underlying cause.
12 + */
13 + public UnavailableIdException() {
14 + }
15 +
16 + /**
17 + * Constructs an exception with the specified message.
18 + *
19 + * @param message the message describing the specific nature of the error
20 + */
21 + public UnavailableIdException(String message) {
22 + super(message);
23 + }
24 +
25 + /**
26 + * Constructs an exception with the specified message and the underlying cause.
27 + *
28 + * @param message the message describing the specific nature of the error
29 + * @param cause the underlying cause of this error
30 + */
31 + public UnavailableIdException(String message, Throwable cause) {
32 + super(message, cause);
33 + }
34 +}
1 +/**
2 + * Core subsystem for tracking high-level intents for treatment of selected
3 + * network traffic.
4 + */
5 +package org.onlab.onos.net.intent.impl;
...\ No newline at end of file ...\ No newline at end of file
...@@ -31,6 +31,8 @@ import org.onlab.onos.net.link.LinkEvent; ...@@ -31,6 +31,8 @@ import org.onlab.onos.net.link.LinkEvent;
31 import org.onlab.onos.net.link.LinkListener; 31 import org.onlab.onos.net.link.LinkListener;
32 import org.onlab.onos.net.link.LinkService; 32 import org.onlab.onos.net.link.LinkService;
33 import org.onlab.onos.net.packet.DefaultOutboundPacket; 33 import org.onlab.onos.net.packet.DefaultOutboundPacket;
34 +import org.onlab.onos.net.packet.InboundPacket;
35 +import org.onlab.onos.net.packet.PacketContext;
34 import org.onlab.onos.net.packet.PacketService; 36 import org.onlab.onos.net.packet.PacketService;
35 import org.onlab.onos.net.proxyarp.ProxyArpService; 37 import org.onlab.onos.net.proxyarp.ProxyArpService;
36 import org.onlab.packet.ARP; 38 import org.onlab.packet.ARP;
...@@ -43,7 +45,6 @@ import com.google.common.collect.HashMultimap; ...@@ -43,7 +45,6 @@ import com.google.common.collect.HashMultimap;
43 import com.google.common.collect.Lists; 45 import com.google.common.collect.Lists;
44 import com.google.common.collect.Multimap; 46 import com.google.common.collect.Multimap;
45 47
46 -
47 @Component(immediate = true) 48 @Component(immediate = true)
48 @Service 49 @Service
49 public class ProxyArpManager implements ProxyArpService { 50 public class ProxyArpManager implements ProxyArpService {
...@@ -93,14 +94,14 @@ public class ProxyArpManager implements ProxyArpService { ...@@ -93,14 +94,14 @@ public class ProxyArpManager implements ProxyArpService {
93 94
94 @Override 95 @Override
95 public boolean known(IpPrefix addr) { 96 public boolean known(IpPrefix addr) {
96 - checkNotNull(MAC_ADDR_NULL, addr); 97 + checkNotNull(addr, MAC_ADDR_NULL);
97 Set<Host> hosts = hostService.getHostsByIp(addr); 98 Set<Host> hosts = hostService.getHostsByIp(addr);
98 return !hosts.isEmpty(); 99 return !hosts.isEmpty();
99 } 100 }
100 101
101 @Override 102 @Override
102 public void reply(Ethernet eth) { 103 public void reply(Ethernet eth) {
103 - checkNotNull(REQUEST_NULL, eth); 104 + checkNotNull(eth, REQUEST_NULL);
104 checkArgument(eth.getEtherType() == Ethernet.TYPE_ARP, 105 checkArgument(eth.getEtherType() == Ethernet.TYPE_ARP,
105 REQUEST_NOT_ARP); 106 REQUEST_NOT_ARP);
106 ARP arp = (ARP) eth.getPayload(); 107 ARP arp = (ARP) eth.getPayload();
...@@ -128,7 +129,7 @@ public class ProxyArpManager implements ProxyArpService { ...@@ -128,7 +129,7 @@ public class ProxyArpManager implements ProxyArpService {
128 129
129 Ethernet arpReply = buildArpReply(dst, eth); 130 Ethernet arpReply = buildArpReply(dst, eth);
130 // TODO: check send status with host service. 131 // TODO: check send status with host service.
131 - TrafficTreatment.Builder builder = new DefaultTrafficTreatment.Builder(); 132 + TrafficTreatment.Builder builder = DefaultTrafficTreatment.builder();
132 builder.setOutput(src.location().port()); 133 builder.setOutput(src.location().port());
133 packetService.emit(new DefaultOutboundPacket(src.location().deviceId(), 134 packetService.emit(new DefaultOutboundPacket(src.location().deviceId(),
134 builder.build(), ByteBuffer.wrap(arpReply.serialize()))); 135 builder.build(), ByteBuffer.wrap(arpReply.serialize())));
...@@ -136,7 +137,7 @@ public class ProxyArpManager implements ProxyArpService { ...@@ -136,7 +137,7 @@ public class ProxyArpManager implements ProxyArpService {
136 137
137 @Override 138 @Override
138 public void forward(Ethernet eth) { 139 public void forward(Ethernet eth) {
139 - checkNotNull(REQUEST_NULL, eth); 140 + checkNotNull(eth, REQUEST_NULL);
140 checkArgument(eth.getEtherType() == Ethernet.TYPE_ARP, 141 checkArgument(eth.getEtherType() == Ethernet.TYPE_ARP,
141 REQUEST_NOT_ARP); 142 REQUEST_NOT_ARP);
142 ARP arp = (ARP) eth.getPayload(); 143 ARP arp = (ARP) eth.getPayload();
...@@ -148,7 +149,7 @@ public class ProxyArpManager implements ProxyArpService { ...@@ -148,7 +149,7 @@ public class ProxyArpManager implements ProxyArpService {
148 if (h == null) { 149 if (h == null) {
149 flood(eth); 150 flood(eth);
150 } else { 151 } else {
151 - TrafficTreatment.Builder builder = new DefaultTrafficTreatment.Builder(); 152 + TrafficTreatment.Builder builder = DefaultTrafficTreatment.builder();
152 builder.setOutput(h.location().port()); 153 builder.setOutput(h.location().port());
153 packetService.emit(new DefaultOutboundPacket(h.location().deviceId(), 154 packetService.emit(new DefaultOutboundPacket(h.location().deviceId(),
154 builder.build(), ByteBuffer.wrap(eth.serialize()))); 155 builder.build(), ByteBuffer.wrap(eth.serialize())));
...@@ -156,6 +157,23 @@ public class ProxyArpManager implements ProxyArpService { ...@@ -156,6 +157,23 @@ public class ProxyArpManager implements ProxyArpService {
156 157
157 } 158 }
158 159
160 + @Override
161 + public boolean handleArp(PacketContext context) {
162 + InboundPacket pkt = context.inPacket();
163 + Ethernet ethPkt = pkt.parsed();
164 + if (ethPkt.getEtherType() == Ethernet.TYPE_ARP) {
165 + ARP arp = (ARP) ethPkt.getPayload();
166 + if (arp.getOpCode() == ARP.OP_REPLY) {
167 + forward(ethPkt);
168 + } else if (arp.getOpCode() == ARP.OP_REQUEST) {
169 + reply(ethPkt);
170 + }
171 + context.block();
172 + return true;
173 + }
174 + return false;
175 + }
176 +
159 /** 177 /**
160 * Flood the arp request at all edges in the network. 178 * Flood the arp request at all edges in the network.
161 * @param request the arp request. 179 * @param request the arp request.
...@@ -166,7 +184,7 @@ public class ProxyArpManager implements ProxyArpService { ...@@ -166,7 +184,7 @@ public class ProxyArpManager implements ProxyArpService {
166 184
167 synchronized (externalPorts) { 185 synchronized (externalPorts) {
168 for (Entry<Device, PortNumber> entry : externalPorts.entries()) { 186 for (Entry<Device, PortNumber> entry : externalPorts.entries()) {
169 - builder = new DefaultTrafficTreatment.Builder(); 187 + builder = DefaultTrafficTreatment.builder();
170 builder.setOutput(entry.getValue()); 188 builder.setOutput(entry.getValue());
171 packetService.emit(new DefaultOutboundPacket(entry.getKey().id(), 189 packetService.emit(new DefaultOutboundPacket(entry.getKey().id(),
172 builder.build(), buf)); 190 builder.build(), buf));
...@@ -188,12 +206,12 @@ public class ProxyArpManager implements ProxyArpService { ...@@ -188,12 +206,12 @@ public class ProxyArpManager implements ProxyArpService {
188 for (Link l : links) { 206 for (Link l : links) {
189 // for each link, mark the concerned ports as internal 207 // for each link, mark the concerned ports as internal
190 // and the remaining ports are therefore external. 208 // and the remaining ports are therefore external.
191 - if (l.src().deviceId().equals(d) 209 + if (l.src().deviceId().equals(d.id())
192 && ports.contains(l.src().port())) { 210 && ports.contains(l.src().port())) {
193 ports.remove(l.src().port()); 211 ports.remove(l.src().port());
194 internalPorts.put(d, l.src().port()); 212 internalPorts.put(d, l.src().port());
195 } 213 }
196 - if (l.dst().deviceId().equals(d) 214 + if (l.dst().deviceId().equals(d.id())
197 && ports.contains(l.dst().port())) { 215 && ports.contains(l.dst().port())) {
198 ports.remove(l.dst().port()); 216 ports.remove(l.dst().port());
199 internalPorts.put(d, l.dst().port()); 217 internalPorts.put(d, l.dst().port());
...@@ -322,7 +340,6 @@ public class ProxyArpManager implements ProxyArpService { ...@@ -322,7 +340,6 @@ public class ProxyArpManager implements ProxyArpService {
322 340
323 } 341 }
324 342
325 -} 343 + }
326 -
327 344
328 } 345 }
......
...@@ -6,8 +6,15 @@ import org.junit.After; ...@@ -6,8 +6,15 @@ import org.junit.After;
6 import org.junit.Before; 6 import org.junit.Before;
7 import org.junit.Ignore; 7 import org.junit.Ignore;
8 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;
9 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;
10 import org.onlab.onos.cluster.NodeId; 16 import org.onlab.onos.cluster.NodeId;
17 +import org.onlab.onos.cluster.ControllerNode.State;
11 import org.onlab.onos.event.Event; 18 import org.onlab.onos.event.Event;
12 import org.onlab.onos.event.impl.TestEventDispatcher; 19 import org.onlab.onos.event.impl.TestEventDispatcher;
13 import org.onlab.onos.net.Device; 20 import org.onlab.onos.net.Device;
...@@ -28,7 +35,9 @@ import org.onlab.onos.net.device.DeviceService; ...@@ -28,7 +35,9 @@ import org.onlab.onos.net.device.DeviceService;
28 import org.onlab.onos.net.device.PortDescription; 35 import org.onlab.onos.net.device.PortDescription;
29 import org.onlab.onos.net.provider.AbstractProvider; 36 import org.onlab.onos.net.provider.AbstractProvider;
30 import org.onlab.onos.net.provider.ProviderId; 37 import org.onlab.onos.net.provider.ProviderId;
38 +import org.onlab.onos.store.ClockProviderService;
31 import org.onlab.onos.store.trivial.impl.SimpleDeviceStore; 39 import org.onlab.onos.store.trivial.impl.SimpleDeviceStore;
40 +import org.onlab.packet.IpPrefix;
32 41
33 import java.util.ArrayList; 42 import java.util.ArrayList;
34 import java.util.Iterator; 43 import java.util.Iterator;
...@@ -57,6 +66,8 @@ public class DeviceManagerTest { ...@@ -57,6 +66,8 @@ public class DeviceManagerTest {
57 private static final PortNumber P1 = PortNumber.portNumber(1); 66 private static final PortNumber P1 = PortNumber.portNumber(1);
58 private static final PortNumber P2 = PortNumber.portNumber(2); 67 private static final PortNumber P2 = PortNumber.portNumber(2);
59 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");
60 71
61 private DeviceManager mgr; 72 private DeviceManager mgr;
62 73
...@@ -76,6 +87,8 @@ public class DeviceManagerTest { ...@@ -76,6 +87,8 @@ public class DeviceManagerTest {
76 mgr.store = new SimpleDeviceStore(); 87 mgr.store = new SimpleDeviceStore();
77 mgr.eventDispatcher = new TestEventDispatcher(); 88 mgr.eventDispatcher = new TestEventDispatcher();
78 mgr.mastershipService = new TestMastershipService(); 89 mgr.mastershipService = new TestMastershipService();
90 + mgr.clusterService = new TestClusterService();
91 + mgr.clockProviderService = new TestClockProviderService();
79 mgr.activate(); 92 mgr.activate();
80 93
81 service.addListener(listener); 94 service.addListener(listener);
...@@ -275,6 +288,59 @@ public class DeviceManagerTest { ...@@ -275,6 +288,59 @@ public class DeviceManagerTest {
275 public MastershipRole requestRoleFor(DeviceId deviceId) { 288 public MastershipRole requestRoleFor(DeviceId deviceId) {
276 return MastershipRole.MASTER; 289 return MastershipRole.MASTER;
277 } 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;
317 + }
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;
278 } 327 }
279 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 + }
280 } 346 }
......
1 -package org.onlab.onos.net.device.impl;
2 -
3 -import com.google.common.collect.Iterables;
4 -import com.google.common.collect.Sets;
5 -import com.hazelcast.config.Config;
6 -import com.hazelcast.core.Hazelcast;
7 -
8 -import org.junit.After;
9 -import org.junit.Before;
10 -import org.junit.Test;
11 -import org.onlab.onos.cluster.DefaultControllerNode;
12 -import org.onlab.onos.cluster.MastershipServiceAdapter;
13 -import org.onlab.onos.cluster.NodeId;
14 -import org.onlab.onos.event.Event;
15 -import org.onlab.onos.event.EventDeliveryService;
16 -import org.onlab.onos.event.impl.TestEventDispatcher;
17 -import org.onlab.onos.net.Device;
18 -import org.onlab.onos.net.DeviceId;
19 -import org.onlab.onos.net.MastershipRole;
20 -import org.onlab.onos.net.Port;
21 -import org.onlab.onos.net.PortNumber;
22 -import org.onlab.onos.net.device.DefaultDeviceDescription;
23 -import org.onlab.onos.net.device.DefaultPortDescription;
24 -import org.onlab.onos.net.device.DeviceAdminService;
25 -import org.onlab.onos.net.device.DeviceDescription;
26 -import org.onlab.onos.net.device.DeviceEvent;
27 -import org.onlab.onos.net.device.DeviceListener;
28 -import org.onlab.onos.net.device.DeviceProvider;
29 -import org.onlab.onos.net.device.DeviceProviderRegistry;
30 -import org.onlab.onos.net.device.DeviceProviderService;
31 -import org.onlab.onos.net.device.DeviceService;
32 -import org.onlab.onos.net.device.PortDescription;
33 -import org.onlab.onos.net.provider.AbstractProvider;
34 -import org.onlab.onos.net.provider.ProviderId;
35 -import org.onlab.onos.store.common.StoreManager;
36 -import org.onlab.onos.store.common.StoreService;
37 -import org.onlab.onos.store.common.TestStoreManager;
38 -import org.onlab.onos.store.device.impl.DistributedDeviceStore;
39 -import org.onlab.onos.store.serializers.KryoSerializationManager;
40 -import org.onlab.onos.store.serializers.KryoSerializationService;
41 -import org.onlab.packet.IpPrefix;
42 -
43 -import java.util.ArrayList;
44 -import java.util.HashSet;
45 -import java.util.Iterator;
46 -import java.util.List;
47 -import java.util.Map.Entry;
48 -import java.util.Set;
49 -import java.util.concurrent.BlockingQueue;
50 -import java.util.concurrent.ConcurrentHashMap;
51 -import java.util.concurrent.ConcurrentMap;
52 -import java.util.concurrent.LinkedBlockingQueue;
53 -import java.util.concurrent.TimeUnit;
54 -
55 -import static org.junit.Assert.*;
56 -import static org.onlab.onos.net.Device.Type.SWITCH;
57 -import static org.onlab.onos.net.DeviceId.deviceId;
58 -import static org.onlab.onos.net.device.DeviceEvent.Type.*;
59 -
60 -// FIXME This test is slow starting up Hazelcast on each test cases.
61 -// FIXME DistributedDeviceStore should have it's own test cases.
62 -
63 -/**
64 - * Test codifying the device service & device provider service contracts.
65 - */
66 -public class DistributedDeviceManagerTest {
67 -
68 - private static final ProviderId PID = new ProviderId("of", "foo");
69 - private static final DeviceId DID1 = deviceId("of:foo");
70 - private static final DeviceId DID2 = deviceId("of:bar");
71 - private static final String MFR = "whitebox";
72 - private static final String HW = "1.1.x";
73 - private static final String SW1 = "3.8.1";
74 - private static final String SW2 = "3.9.5";
75 - private static final String SN = "43311-12345";
76 -
77 - private static final PortNumber P1 = PortNumber.portNumber(1);
78 - private static final PortNumber P2 = PortNumber.portNumber(2);
79 - private static final PortNumber P3 = PortNumber.portNumber(3);
80 -
81 - private static final DefaultControllerNode SELF
82 - = new DefaultControllerNode(new NodeId("foobar"),
83 - IpPrefix.valueOf("127.0.0.1"));
84 -
85 -
86 - private DeviceManager mgr;
87 -
88 - protected StoreManager storeManager;
89 - protected DeviceService service;
90 - protected DeviceAdminService admin;
91 - protected DeviceProviderRegistry registry;
92 - protected DeviceProviderService providerService;
93 - protected TestProvider provider;
94 - protected TestListener listener = new TestListener();
95 - private DistributedDeviceStore dstore;
96 - private TestMastershipManager masterManager;
97 - private EventDeliveryService eventService;
98 - private KryoSerializationManager serializationMgr;
99 -
100 - @Before
101 - public void setUp() {
102 - mgr = new DeviceManager();
103 - service = mgr;
104 - admin = mgr;
105 - registry = mgr;
106 - // TODO should find a way to clean Hazelcast instance without shutdown.
107 - Config config = TestStoreManager.getTestConfig();
108 -
109 - masterManager = new TestMastershipManager();
110 -
111 - storeManager = new TestStoreManager(Hazelcast.newHazelcastInstance(config));
112 - storeManager.activate();
113 -
114 - serializationMgr = new KryoSerializationManager();
115 - serializationMgr.activate();
116 -
117 - dstore = new TestDistributedDeviceStore(storeManager, serializationMgr);
118 - dstore.activate();
119 -
120 - mgr.store = dstore;
121 - eventService = new TestEventDispatcher();
122 - mgr.eventDispatcher = eventService;
123 - mgr.mastershipService = masterManager;
124 - mgr.activate();
125 -
126 - service.addListener(listener);
127 -
128 - provider = new TestProvider();
129 - providerService = registry.register(provider);
130 - assertTrue("provider should be registered",
131 - registry.getProviders().contains(provider.id()));
132 - }
133 -
134 - @After
135 - public void tearDown() {
136 - registry.unregister(provider);
137 - assertFalse("provider should not be registered",
138 - registry.getProviders().contains(provider.id()));
139 - service.removeListener(listener);
140 - mgr.deactivate();
141 -
142 - dstore.deactivate();
143 - serializationMgr.deactivate();
144 - storeManager.deactivate();
145 - }
146 -
147 - private void connectDevice(DeviceId deviceId, String swVersion) {
148 - DeviceDescription description =
149 - new DefaultDeviceDescription(deviceId.uri(), SWITCH, MFR,
150 - HW, swVersion, SN);
151 - providerService.deviceConnected(deviceId, description);
152 - assertNotNull("device should be found", service.getDevice(DID1));
153 - }
154 -
155 - @Test
156 - public void deviceConnected() {
157 - assertNull("device should not be found", service.getDevice(DID1));
158 - connectDevice(DID1, SW1);
159 - validateEvents(DEVICE_ADDED);
160 -
161 - assertEquals("only one device expected", 1, Iterables.size(service.getDevices()));
162 - Iterator<Device> it = service.getDevices().iterator();
163 - assertNotNull("one device expected", it.next());
164 - assertFalse("only one device expected", it.hasNext());
165 -
166 - assertEquals("incorrect device count", 1, service.getDeviceCount());
167 - assertTrue("device should be available", service.isAvailable(DID1));
168 - }
169 -
170 - @Test
171 - public void deviceDisconnected() {
172 - connectDevice(DID1, SW1);
173 - connectDevice(DID2, SW1);
174 - validateEvents(DEVICE_ADDED, DEVICE_ADDED);
175 - assertTrue("device should be available", service.isAvailable(DID1));
176 -
177 - // Disconnect
178 - providerService.deviceDisconnected(DID1);
179 - assertNotNull("device should not be found", service.getDevice(DID1));
180 - assertFalse("device should not be available", service.isAvailable(DID1));
181 - validateEvents(DEVICE_AVAILABILITY_CHANGED);
182 -
183 - // Reconnect
184 - connectDevice(DID1, SW1);
185 - validateEvents(DEVICE_AVAILABILITY_CHANGED);
186 -
187 - assertEquals("incorrect device count", 2, service.getDeviceCount());
188 - }
189 -
190 - @Test
191 - public void deviceUpdated() {
192 - connectDevice(DID1, SW1);
193 - validateEvents(DEVICE_ADDED);
194 -
195 - connectDevice(DID1, SW2);
196 - validateEvents(DEVICE_UPDATED);
197 - }
198 -
199 - @Test
200 - public void getRole() {
201 - connectDevice(DID1, SW1);
202 - assertEquals("incorrect role", MastershipRole.MASTER, service.getRole(DID1));
203 - }
204 -
205 - @Test
206 - public void updatePorts() {
207 - connectDevice(DID1, SW1);
208 - List<PortDescription> pds = new ArrayList<>();
209 - pds.add(new DefaultPortDescription(P1, true));
210 - pds.add(new DefaultPortDescription(P2, true));
211 - pds.add(new DefaultPortDescription(P3, true));
212 - providerService.updatePorts(DID1, pds);
213 - validateEvents(DEVICE_ADDED, PORT_ADDED, PORT_ADDED, PORT_ADDED);
214 - pds.clear();
215 -
216 - pds.add(new DefaultPortDescription(P1, false));
217 - pds.add(new DefaultPortDescription(P3, true));
218 - providerService.updatePorts(DID1, pds);
219 - validateEvents(PORT_UPDATED, PORT_REMOVED);
220 - }
221 -
222 - @Test
223 - public void updatePortStatus() {
224 - connectDevice(DID1, SW1);
225 - List<PortDescription> pds = new ArrayList<>();
226 - pds.add(new DefaultPortDescription(P1, true));
227 - pds.add(new DefaultPortDescription(P2, true));
228 - providerService.updatePorts(DID1, pds);
229 - validateEvents(DEVICE_ADDED, PORT_ADDED, PORT_ADDED);
230 -
231 - providerService.portStatusChanged(DID1, new DefaultPortDescription(P1, false));
232 - validateEvents(PORT_UPDATED);
233 - providerService.portStatusChanged(DID1, new DefaultPortDescription(P1, false));
234 - assertTrue("no events expected", listener.events.isEmpty());
235 - }
236 -
237 - @Test
238 - public void getPorts() {
239 - connectDevice(DID1, SW1);
240 - List<PortDescription> pds = new ArrayList<>();
241 - pds.add(new DefaultPortDescription(P1, true));
242 - pds.add(new DefaultPortDescription(P2, true));
243 - providerService.updatePorts(DID1, pds);
244 - validateEvents(DEVICE_ADDED, PORT_ADDED, PORT_ADDED);
245 - assertEquals("wrong port count", 2, service.getPorts(DID1).size());
246 -
247 - Port port = service.getPort(DID1, P1);
248 - assertEquals("incorrect port", P1, service.getPort(DID1, P1).number());
249 - assertEquals("incorrect state", true, service.getPort(DID1, P1).isEnabled());
250 - }
251 -
252 - @Test
253 - public void removeDevice() {
254 - connectDevice(DID1, SW1);
255 - connectDevice(DID2, SW2);
256 - assertEquals("incorrect device count", 2, service.getDeviceCount());
257 - admin.removeDevice(DID1);
258 - validateEvents(DEVICE_ADDED, DEVICE_ADDED, DEVICE_REMOVED);
259 - assertNull("device should not be found", service.getDevice(DID1));
260 - assertNotNull("device should be found", service.getDevice(DID2));
261 - assertEquals("incorrect device count", 1, service.getDeviceCount());
262 - }
263 -
264 - protected void validateEvents(Enum... types) {
265 - for (Enum type : types) {
266 - try {
267 - Event event = listener.events.poll(1, TimeUnit.SECONDS);
268 - assertNotNull("Timed out waiting for " + event, event);
269 - assertEquals("incorrect event type", type, event.type());
270 - } catch (InterruptedException e) {
271 - fail("Unexpected interrupt");
272 - }
273 - }
274 - assertTrue("Unexpected events left", listener.events.isEmpty());
275 - listener.events.clear();
276 - }
277 -
278 -
279 - private class TestProvider extends AbstractProvider implements DeviceProvider {
280 - private Device deviceReceived;
281 - private MastershipRole roleReceived;
282 -
283 - public TestProvider() {
284 - super(PID);
285 - }
286 -
287 - @Override
288 - public void triggerProbe(Device device) {
289 - }
290 -
291 - @Override
292 - public void roleChanged(Device device, MastershipRole newRole) {
293 - deviceReceived = device;
294 - roleReceived = newRole;
295 - }
296 - }
297 -
298 - private static class TestListener implements DeviceListener {
299 - final BlockingQueue<DeviceEvent> events = new LinkedBlockingQueue<>();
300 -
301 - @Override
302 - public void event(DeviceEvent event) {
303 - events.add(event);
304 - }
305 - }
306 -
307 - private class TestDistributedDeviceStore extends DistributedDeviceStore {
308 -
309 - public TestDistributedDeviceStore(StoreService storeService,
310 - KryoSerializationService kryoSerializationService) {
311 - this.storeService = storeService;
312 - this.kryoSerializationService = kryoSerializationService;
313 - }
314 - }
315 -
316 - private static class TestMastershipManager extends MastershipServiceAdapter {
317 -
318 - private ConcurrentMap<DeviceId, NodeId> masters = new ConcurrentHashMap<>();
319 -
320 - public TestMastershipManager() {
321 - // SELF master of all initially
322 - masters.put(DID1, SELF.id());
323 - masters.put(DID1, SELF.id());
324 - }
325 - @Override
326 - public MastershipRole getLocalRole(DeviceId deviceId) {
327 - return MastershipRole.MASTER;
328 - }
329 -
330 - @Override
331 - public Set<DeviceId> getDevicesOf(NodeId nodeId) {
332 - HashSet<DeviceId> set = Sets.newHashSet();
333 - for (Entry<DeviceId, NodeId> e : masters.entrySet()) {
334 - if (e.getValue().equals(nodeId)) {
335 - set.add(e.getKey());
336 - }
337 - }
338 - return set;
339 - }
340 -
341 - @Override
342 - public MastershipRole requestRoleFor(DeviceId deviceId) {
343 - if (SELF.id().equals(masters.get(deviceId))) {
344 - return MastershipRole.MASTER;
345 - } else {
346 - return MastershipRole.STANDBY;
347 - }
348 - }
349 -
350 - @Override
351 - public void relinquishMastership(DeviceId deviceId) {
352 - masters.remove(deviceId, SELF.id());
353 - }
354 - }
355 -}
...@@ -12,6 +12,7 @@ import java.util.ArrayList; ...@@ -12,6 +12,7 @@ import java.util.ArrayList;
12 import java.util.Collections; 12 import java.util.Collections;
13 import java.util.List; 13 import java.util.List;
14 import java.util.Set; 14 import java.util.Set;
15 +import java.util.concurrent.Future;
15 16
16 import org.junit.After; 17 import org.junit.After;
17 import org.junit.Before; 18 import org.junit.Before;
...@@ -27,9 +28,12 @@ import org.onlab.onos.net.Port; ...@@ -27,9 +28,12 @@ import org.onlab.onos.net.Port;
27 import org.onlab.onos.net.PortNumber; 28 import org.onlab.onos.net.PortNumber;
28 import org.onlab.onos.net.device.DeviceListener; 29 import org.onlab.onos.net.device.DeviceListener;
29 import org.onlab.onos.net.device.DeviceService; 30 import org.onlab.onos.net.device.DeviceService;
31 +import org.onlab.onos.net.flow.DefaultFlowEntry;
30 import org.onlab.onos.net.flow.DefaultFlowRule; 32 import org.onlab.onos.net.flow.DefaultFlowRule;
33 +import org.onlab.onos.net.flow.FlowEntry;
34 +import org.onlab.onos.net.flow.FlowEntry.FlowEntryState;
31 import org.onlab.onos.net.flow.FlowRule; 35 import org.onlab.onos.net.flow.FlowRule;
32 -import org.onlab.onos.net.flow.FlowRule.FlowRuleState; 36 +import org.onlab.onos.net.flow.FlowRuleBatchEntry;
33 import org.onlab.onos.net.flow.FlowRuleEvent; 37 import org.onlab.onos.net.flow.FlowRuleEvent;
34 import org.onlab.onos.net.flow.FlowRuleListener; 38 import org.onlab.onos.net.flow.FlowRuleListener;
35 import org.onlab.onos.net.flow.FlowRuleProvider; 39 import org.onlab.onos.net.flow.FlowRuleProvider;
...@@ -40,6 +44,7 @@ import org.onlab.onos.net.flow.TrafficSelector; ...@@ -40,6 +44,7 @@ import org.onlab.onos.net.flow.TrafficSelector;
40 import org.onlab.onos.net.flow.TrafficTreatment; 44 import org.onlab.onos.net.flow.TrafficTreatment;
41 import org.onlab.onos.net.flow.criteria.Criterion; 45 import org.onlab.onos.net.flow.criteria.Criterion;
42 import org.onlab.onos.net.flow.instructions.Instruction; 46 import org.onlab.onos.net.flow.instructions.Instruction;
47 +import org.onlab.onos.net.intent.BatchOperation;
43 import org.onlab.onos.net.provider.AbstractProvider; 48 import org.onlab.onos.net.provider.AbstractProvider;
44 import org.onlab.onos.net.provider.ProviderId; 49 import org.onlab.onos.net.provider.ProviderId;
45 import org.onlab.onos.store.trivial.impl.SimpleFlowRuleStore; 50 import org.onlab.onos.store.trivial.impl.SimpleFlowRuleStore;
...@@ -100,12 +105,9 @@ public class FlowRuleManagerTest { ...@@ -100,12 +105,9 @@ public class FlowRuleManagerTest {
100 private FlowRule flowRule(int tsval, int trval) { 105 private FlowRule flowRule(int tsval, int trval) {
101 TestSelector ts = new TestSelector(tsval); 106 TestSelector ts = new TestSelector(tsval);
102 TestTreatment tr = new TestTreatment(trval); 107 TestTreatment tr = new TestTreatment(trval);
103 - return new DefaultFlowRule(DID, ts, tr, 0, appId, TIMEOUT); 108 + return new DefaultFlowRule(DID, ts, tr, 10, appId, TIMEOUT);
104 } 109 }
105 110
106 - private FlowRule flowRule(FlowRule rule, FlowRuleState state) {
107 - return new DefaultFlowRule(rule, state);
108 - }
109 111
110 private FlowRule addFlowRule(int hval) { 112 private FlowRule addFlowRule(int hval) {
111 FlowRule rule = flowRule(hval, hval); 113 FlowRule rule = flowRule(hval, hval);
...@@ -143,24 +145,26 @@ public class FlowRuleManagerTest { ...@@ -143,24 +145,26 @@ public class FlowRuleManagerTest {
143 FlowRule f1 = addFlowRule(1); 145 FlowRule f1 = addFlowRule(1);
144 FlowRule f2 = addFlowRule(2); 146 FlowRule f2 = addFlowRule(2);
145 147
148 + FlowEntry fe1 = new DefaultFlowEntry(f1);
149 + FlowEntry fe2 = new DefaultFlowEntry(f2);
146 assertEquals("2 rules should exist", 2, flowCount()); 150 assertEquals("2 rules should exist", 2, flowCount());
147 151
148 - providerService.pushFlowMetrics(DID, ImmutableList.of(f1, f2)); 152 + providerService.pushFlowMetrics(DID, ImmutableList.of(fe1, fe2));
149 validateEvents(RULE_ADDED, RULE_ADDED); 153 validateEvents(RULE_ADDED, RULE_ADDED);
150 154
151 addFlowRule(1); 155 addFlowRule(1);
152 assertEquals("should still be 2 rules", 2, flowCount()); 156 assertEquals("should still be 2 rules", 2, flowCount());
153 157
154 - providerService.pushFlowMetrics(DID, ImmutableList.of(f1)); 158 + providerService.pushFlowMetrics(DID, ImmutableList.of(fe1));
155 validateEvents(RULE_UPDATED); 159 validateEvents(RULE_UPDATED);
156 } 160 }
157 161
158 162
159 //backing store is sensitive to the order of additions/removals 163 //backing store is sensitive to the order of additions/removals
160 - private boolean validateState(FlowRuleState... state) { 164 + private boolean validateState(FlowEntryState... state) {
161 - Iterable<FlowRule> rules = service.getFlowEntries(DID); 165 + Iterable<FlowEntry> rules = service.getFlowEntries(DID);
162 int i = 0; 166 int i = 0;
163 - for (FlowRule f : rules) { 167 + for (FlowEntry f : rules) {
164 if (f.state() != state[i]) { 168 if (f.state() != state[i]) {
165 return false; 169 return false;
166 } 170 }
...@@ -181,8 +185,8 @@ public class FlowRuleManagerTest { ...@@ -181,8 +185,8 @@ public class FlowRuleManagerTest {
181 mgr.applyFlowRules(r1, r2, r3); 185 mgr.applyFlowRules(r1, r2, r3);
182 assertEquals("3 rules should exist", 3, flowCount()); 186 assertEquals("3 rules should exist", 3, flowCount());
183 assertTrue("Entries should be pending add.", 187 assertTrue("Entries should be pending add.",
184 - validateState(FlowRuleState.PENDING_ADD, FlowRuleState.PENDING_ADD, 188 + validateState(FlowEntryState.PENDING_ADD, FlowEntryState.PENDING_ADD,
185 - FlowRuleState.PENDING_ADD)); 189 + FlowEntryState.PENDING_ADD));
186 } 190 }
187 191
188 @Test 192 @Test
...@@ -192,20 +196,21 @@ public class FlowRuleManagerTest { ...@@ -192,20 +196,21 @@ public class FlowRuleManagerTest {
192 FlowRule f3 = addFlowRule(3); 196 FlowRule f3 = addFlowRule(3);
193 assertEquals("3 rules should exist", 3, flowCount()); 197 assertEquals("3 rules should exist", 3, flowCount());
194 198
195 - providerService.pushFlowMetrics(DID, ImmutableList.of(f1, f2, f3)); 199 + FlowEntry fe1 = new DefaultFlowEntry(f1);
200 + FlowEntry fe2 = new DefaultFlowEntry(f2);
201 + FlowEntry fe3 = new DefaultFlowEntry(f3);
202 + providerService.pushFlowMetrics(DID, ImmutableList.of(fe1, fe2, fe3));
196 validateEvents(RULE_ADDED, RULE_ADDED, RULE_ADDED); 203 validateEvents(RULE_ADDED, RULE_ADDED, RULE_ADDED);
197 204
198 - FlowRule rem1 = flowRule(f1, FlowRuleState.REMOVED); 205 + mgr.removeFlowRules(f1, f2);
199 - FlowRule rem2 = flowRule(f2, FlowRuleState.REMOVED);
200 - mgr.removeFlowRules(rem1, rem2);
201 //removing from north, so no events generated 206 //removing from north, so no events generated
202 validateEvents(); 207 validateEvents();
203 assertEquals("3 rule should exist", 3, flowCount()); 208 assertEquals("3 rule should exist", 3, flowCount());
204 assertTrue("Entries should be pending remove.", 209 assertTrue("Entries should be pending remove.",
205 - validateState(FlowRuleState.CREATED, FlowRuleState.PENDING_REMOVE, 210 + validateState(FlowEntryState.PENDING_REMOVE, FlowEntryState.PENDING_REMOVE,
206 - FlowRuleState.PENDING_REMOVE)); 211 + FlowEntryState.ADDED));
207 212
208 - mgr.removeFlowRules(rem1); 213 + mgr.removeFlowRules(f1);
209 assertEquals("3 rule should still exist", 3, flowCount()); 214 assertEquals("3 rule should still exist", 3, flowCount());
210 } 215 }
211 216
...@@ -213,21 +218,24 @@ public class FlowRuleManagerTest { ...@@ -213,21 +218,24 @@ public class FlowRuleManagerTest {
213 public void flowRemoved() { 218 public void flowRemoved() {
214 FlowRule f1 = addFlowRule(1); 219 FlowRule f1 = addFlowRule(1);
215 FlowRule f2 = addFlowRule(2); 220 FlowRule f2 = addFlowRule(2);
216 - providerService.pushFlowMetrics(f1.deviceId(), ImmutableList.of(f1, f2)); 221 + FlowEntry fe1 = new DefaultFlowEntry(f1);
222 + FlowEntry fe2 = new DefaultFlowEntry(f2);
223 + providerService.pushFlowMetrics(DID, ImmutableList.of(fe1, fe2));
217 service.removeFlowRules(f1); 224 service.removeFlowRules(f1);
218 - FlowRule rem1 = flowRule(f1, FlowRuleState.REMOVED); 225 + fe1.setState(FlowEntryState.REMOVED);
219 - providerService.flowRemoved(rem1); 226 + providerService.flowRemoved(fe1);
220 validateEvents(RULE_ADDED, RULE_ADDED, RULE_REMOVED); 227 validateEvents(RULE_ADDED, RULE_ADDED, RULE_REMOVED);
221 228
222 - providerService.flowRemoved(rem1); 229 + providerService.flowRemoved(fe1);
223 validateEvents(); 230 validateEvents();
224 231
225 FlowRule f3 = flowRule(3, 3); 232 FlowRule f3 = flowRule(3, 3);
233 + FlowEntry fe3 = new DefaultFlowEntry(f3);
226 service.applyFlowRules(f3); 234 service.applyFlowRules(f3);
227 - providerService.pushFlowMetrics(f3.deviceId(), Collections.singletonList(f3)); 235 + providerService.pushFlowMetrics(DID, Collections.singletonList(fe3));
228 validateEvents(RULE_ADDED); 236 validateEvents(RULE_ADDED);
229 237
230 - providerService.flowRemoved(f3); 238 + providerService.flowRemoved(fe3);
231 validateEvents(); 239 validateEvents();
232 } 240 }
233 241
...@@ -237,17 +245,20 @@ public class FlowRuleManagerTest { ...@@ -237,17 +245,20 @@ public class FlowRuleManagerTest {
237 FlowRule f2 = flowRule(2, 2); 245 FlowRule f2 = flowRule(2, 2);
238 FlowRule f3 = flowRule(3, 3); 246 FlowRule f3 = flowRule(3, 3);
239 247
248 + mgr.applyFlowRules(f1, f2, f3);
240 249
250 + FlowEntry fe1 = new DefaultFlowEntry(f1);
251 + FlowEntry fe2 = new DefaultFlowEntry(f2);
241 252
242 - mgr.applyFlowRules(f1, f2, f3);
243 - FlowRule updatedF1 = flowRule(f1, FlowRuleState.ADDED);
244 - FlowRule updatedF2 = flowRule(f2, FlowRuleState.ADDED);
245 253
246 - providerService.pushFlowMetrics(DID, Lists.newArrayList(updatedF1, updatedF2)); 254 + //FlowRule updatedF1 = flowRule(f1, FlowRuleState.ADDED);
255 + //FlowRule updatedF2 = flowRule(f2, FlowRuleState.ADDED);
256 +
257 + providerService.pushFlowMetrics(DID, Lists.newArrayList(fe1, fe2));
247 258
248 assertTrue("Entries should be added.", 259 assertTrue("Entries should be added.",
249 - validateState(FlowRuleState.PENDING_ADD, FlowRuleState.ADDED, 260 + validateState(FlowEntryState.ADDED, FlowEntryState.ADDED,
250 - FlowRuleState.ADDED)); 261 + FlowEntryState.PENDING_ADD));
251 262
252 validateEvents(RULE_ADDED, RULE_ADDED); 263 validateEvents(RULE_ADDED, RULE_ADDED);
253 } 264 }
...@@ -259,11 +270,15 @@ public class FlowRuleManagerTest { ...@@ -259,11 +270,15 @@ public class FlowRuleManagerTest {
259 FlowRule f3 = flowRule(3, 3); 270 FlowRule f3 = flowRule(3, 3);
260 mgr.applyFlowRules(f1, f2); 271 mgr.applyFlowRules(f1, f2);
261 272
262 - FlowRule updatedF1 = flowRule(f1, FlowRuleState.ADDED); 273 +// FlowRule updatedF1 = flowRule(f1, FlowRuleState.ADDED);
263 - FlowRule updatedF2 = flowRule(f2, FlowRuleState.ADDED); 274 +// FlowRule updatedF2 = flowRule(f2, FlowRuleState.ADDED);
264 - FlowRule updatedF3 = flowRule(f3, FlowRuleState.ADDED); 275 +// FlowRule updatedF3 = flowRule(f3, FlowRuleState.ADDED);
276 + FlowEntry fe1 = new DefaultFlowEntry(f1);
277 + FlowEntry fe2 = new DefaultFlowEntry(f2);
278 + FlowEntry fe3 = new DefaultFlowEntry(f3);
279 +
265 280
266 - providerService.pushFlowMetrics(DID, Lists.newArrayList(updatedF1, updatedF2, updatedF3)); 281 + providerService.pushFlowMetrics(DID, Lists.newArrayList(fe1, fe2, fe3));
267 282
268 validateEvents(RULE_ADDED, RULE_ADDED); 283 validateEvents(RULE_ADDED, RULE_ADDED);
269 284
...@@ -279,13 +294,16 @@ public class FlowRuleManagerTest { ...@@ -279,13 +294,16 @@ public class FlowRuleManagerTest {
279 FlowRule f2 = flowRule(2, 2); 294 FlowRule f2 = flowRule(2, 2);
280 FlowRule f3 = flowRule(3, 3); 295 FlowRule f3 = flowRule(3, 3);
281 296
282 - FlowRule updatedF1 = flowRule(f1, FlowRuleState.ADDED); 297 +// FlowRule updatedF1 = flowRule(f1, FlowRuleState.ADDED);
283 - FlowRule updatedF2 = flowRule(f2, FlowRuleState.ADDED); 298 +// FlowRule updatedF2 = flowRule(f2, FlowRuleState.ADDED);
299 +
300 + FlowEntry fe1 = new DefaultFlowEntry(f1);
301 + FlowEntry fe2 = new DefaultFlowEntry(f2);
284 mgr.applyFlowRules(f1, f2, f3); 302 mgr.applyFlowRules(f1, f2, f3);
285 303
286 mgr.removeFlowRules(f3); 304 mgr.removeFlowRules(f3);
287 305
288 - providerService.pushFlowMetrics(DID, Lists.newArrayList(updatedF1, updatedF2)); 306 + providerService.pushFlowMetrics(DID, Lists.newArrayList(fe1, fe2));
289 307
290 validateEvents(RULE_ADDED, RULE_ADDED, RULE_REMOVED); 308 validateEvents(RULE_ADDED, RULE_ADDED, RULE_REMOVED);
291 309
...@@ -312,7 +330,7 @@ public class FlowRuleManagerTest { ...@@ -312,7 +330,7 @@ public class FlowRuleManagerTest {
312 330
313 //only check that we are in pending remove. Events and actual remove state will 331 //only check that we are in pending remove. Events and actual remove state will
314 // be set by flowRemoved call. 332 // be set by flowRemoved call.
315 - validateState(FlowRuleState.PENDING_REMOVE, FlowRuleState.PENDING_REMOVE); 333 + validateState(FlowEntryState.PENDING_REMOVE, FlowEntryState.PENDING_REMOVE);
316 } 334 }
317 335
318 private static class TestListener implements FlowRuleListener { 336 private static class TestListener implements FlowRuleListener {
...@@ -389,6 +407,13 @@ public class FlowRuleManagerTest { ...@@ -389,6 +407,13 @@ public class FlowRuleManagerTest {
389 public void removeRulesById(ApplicationId id, FlowRule... flowRules) { 407 public void removeRulesById(ApplicationId id, FlowRule... flowRules) {
390 } 408 }
391 409
410 + @Override
411 + public Future<Void> executeBatch(
412 + BatchOperation<FlowRuleBatchEntry> batch) {
413 + // TODO Auto-generated method stub
414 + return null;
415 + }
416 +
392 417
393 } 418 }
394 419
......
1 +package org.onlab.onos.net.host.impl;
2 +
3 +import static org.easymock.EasyMock.createMock;
4 +import static org.easymock.EasyMock.expect;
5 +import static org.easymock.EasyMock.expectLastCall;
6 +import static org.easymock.EasyMock.replay;
7 +import static org.easymock.EasyMock.verify;
8 +import static org.junit.Assert.assertTrue;
9 +
10 +import java.util.ArrayList;
11 +import java.util.Arrays;
12 +import java.util.Collections;
13 +import java.util.List;
14 +import java.util.Set;
15 +
16 +import org.junit.Ignore;
17 +import org.junit.Test;
18 +import org.onlab.onos.net.ConnectPoint;
19 +import org.onlab.onos.net.Device;
20 +import org.onlab.onos.net.DeviceId;
21 +import org.onlab.onos.net.Host;
22 +import org.onlab.onos.net.MastershipRole;
23 +import org.onlab.onos.net.Port;
24 +import org.onlab.onos.net.PortNumber;
25 +import org.onlab.onos.net.device.DeviceListener;
26 +import org.onlab.onos.net.device.DeviceService;
27 +import org.onlab.onos.net.flow.instructions.Instruction;
28 +import org.onlab.onos.net.flow.instructions.Instructions.OutputInstruction;
29 +import org.onlab.onos.net.host.HostProvider;
30 +import org.onlab.onos.net.host.PortAddresses;
31 +import org.onlab.onos.net.packet.OutboundPacket;
32 +import org.onlab.onos.net.packet.PacketProcessor;
33 +import org.onlab.onos.net.packet.PacketService;
34 +import org.onlab.onos.net.provider.ProviderId;
35 +import org.onlab.packet.ARP;
36 +import org.onlab.packet.Ethernet;
37 +import org.onlab.packet.IpAddress;
38 +import org.onlab.packet.IpPrefix;
39 +import org.onlab.packet.MacAddress;
40 +
41 +import com.google.common.collect.HashMultimap;
42 +import com.google.common.collect.Lists;
43 +import com.google.common.collect.Multimap;
44 +
45 +public class HostMonitorTest {
46 +
47 + private IpAddress targetIpAddress = IpAddress.valueOf("10.0.0.1");
48 + private IpPrefix targetIpPrefix = IpPrefix.valueOf(targetIpAddress.toOctets());
49 +
50 + private IpPrefix sourcePrefix = IpPrefix.valueOf("10.0.0.99/24");
51 + private MacAddress sourceMac = MacAddress.valueOf(1L);
52 +
53 + private HostMonitor hostMonitor;
54 +
55 + @Test
56 + @Ignore
57 + public void testMonitorHostExists() throws Exception {
58 + ProviderId id = new ProviderId("fake://", "id");
59 +
60 + Host host = createMock(Host.class);
61 + expect(host.providerId()).andReturn(id);
62 + replay(host);
63 +
64 + HostManager hostManager = createMock(HostManager.class);
65 + expect(hostManager.getHostsByIp(targetIpPrefix))
66 + .andReturn(Collections.singleton(host));
67 + replay(hostManager);
68 +
69 + HostProvider hostProvider = createMock(HostProvider.class);
70 + expect(hostProvider.id()).andReturn(id).anyTimes();
71 + hostProvider.triggerProbe(host);
72 + expectLastCall().once();
73 + replay(hostProvider);
74 +
75 + hostMonitor = new HostMonitor(null, null, hostManager);
76 +
77 + hostMonitor.registerHostProvider(hostProvider);
78 + hostMonitor.addMonitoringFor(targetIpAddress);
79 +
80 + hostMonitor.run(null);
81 +
82 + verify(hostProvider);
83 + }
84 +
85 + @Test
86 + @Ignore
87 + public void testMonitorHostDoesNotExist() throws Exception {
88 + HostManager hostManager = createMock(HostManager.class);
89 +
90 + DeviceId devId = DeviceId.deviceId("fake");
91 +
92 + Device device = createMock(Device.class);
93 + expect(device.id()).andReturn(devId).anyTimes();
94 + replay(device);
95 +
96 + PortNumber portNum = PortNumber.portNumber(1L);
97 +
98 + Port port = createMock(Port.class);
99 + expect(port.number()).andReturn(portNum).anyTimes();
100 + replay(port);
101 +
102 + TestDeviceService deviceService = new TestDeviceService();
103 + deviceService.addDevice(device, Collections.singleton(port));
104 +
105 + ConnectPoint cp = new ConnectPoint(devId, portNum);
106 + PortAddresses pa = new PortAddresses(cp, Collections.singleton(sourcePrefix),
107 + sourceMac);
108 +
109 + expect(hostManager.getHostsByIp(targetIpPrefix))
110 + .andReturn(Collections.<Host>emptySet()).anyTimes();
111 + expect(hostManager.getAddressBindingsForPort(cp))
112 + .andReturn(pa).anyTimes();
113 + replay(hostManager);
114 +
115 + TestPacketService packetService = new TestPacketService();
116 +
117 +
118 + // Run the test
119 + hostMonitor = new HostMonitor(deviceService, packetService, hostManager);
120 +
121 + hostMonitor.addMonitoringFor(targetIpAddress);
122 + hostMonitor.run(null);
123 +
124 +
125 + // Check that a packet was sent to our PacketService and that it has
126 + // the properties we expect
127 + assertTrue(packetService.packets.size() == 1);
128 + OutboundPacket packet = packetService.packets.get(0);
129 +
130 + // Check the output port is correct
131 + assertTrue(packet.treatment().instructions().size() == 1);
132 + Instruction instruction = packet.treatment().instructions().get(0);
133 + assertTrue(instruction instanceof OutputInstruction);
134 + OutputInstruction oi = (OutputInstruction) instruction;
135 + assertTrue(oi.port().equals(portNum));
136 +
137 + // Check the output packet is correct (well the important bits anyway)
138 + Ethernet eth = new Ethernet();
139 + eth.deserialize(packet.data().array(), 0, packet.data().array().length);
140 + ARP arp = (ARP) eth.getPayload();
141 + assertTrue(Arrays.equals(arp.getSenderProtocolAddress(), sourcePrefix.toOctets()));
142 + assertTrue(Arrays.equals(arp.getSenderHardwareAddress(), sourceMac.toBytes()));
143 + assertTrue(Arrays.equals(arp.getTargetProtocolAddress(), targetIpPrefix.toOctets()));
144 + }
145 +
146 + class TestPacketService implements PacketService {
147 +
148 + List<OutboundPacket> packets = new ArrayList<>();
149 +
150 + @Override
151 + public void addProcessor(PacketProcessor processor, int priority) {
152 + }
153 +
154 + @Override
155 + public void removeProcessor(PacketProcessor processor) {
156 + }
157 +
158 + @Override
159 + public void emit(OutboundPacket packet) {
160 + packets.add(packet);
161 + }
162 + }
163 +
164 + class TestDeviceService implements DeviceService {
165 +
166 + List<Device> devices = Lists.newArrayList();
167 + Multimap<DeviceId, Port> devicePorts = HashMultimap.create();
168 +
169 + void addDevice(Device device, Set<Port> ports) {
170 + devices.add(device);
171 + for (Port p : ports) {
172 + devicePorts.put(device.id(), p);
173 + }
174 + }
175 +
176 + @Override
177 + public int getDeviceCount() {
178 + return 0;
179 + }
180 +
181 + @Override
182 + public Iterable<Device> getDevices() {
183 + return devices;
184 + }
185 +
186 + @Override
187 + public Device getDevice(DeviceId deviceId) {
188 + return null;
189 + }
190 +
191 + @Override
192 + public MastershipRole getRole(DeviceId deviceId) {
193 + return null;
194 + }
195 +
196 + @Override
197 + public List<Port> getPorts(DeviceId deviceId) {
198 + List<Port> ports = Lists.newArrayList();
199 + for (Port p : devicePorts.get(deviceId)) {
200 + ports.add(p);
201 + }
202 + return ports;
203 + }
204 +
205 + @Override
206 + public Port getPort(DeviceId deviceId, PortNumber portNumber) {
207 + return null;
208 + }
209 +
210 + @Override
211 + public boolean isAvailable(DeviceId deviceId) {
212 + return false;
213 + }
214 +
215 + @Override
216 + public void addListener(DeviceListener listener) {
217 + }
218 +
219 + @Override
220 + public void removeListener(DeviceListener listener) {
221 + }
222 + }
223 +}
1 -package org.onlab.onos.store.cluster.messaging.impl; 1 +package org.onlab.onos.store.cluster.impl;
2 2
3 import org.onlab.onos.store.cluster.messaging.MessageSubject; 3 import org.onlab.onos.store.cluster.messaging.MessageSubject;
4 4
5 -public final class ClusterMessageSubjects { 5 +public final class ClusterManagementMessageSubjects {
6 - private ClusterMessageSubjects() {} 6 + // avoid instantiation
7 + private ClusterManagementMessageSubjects() {}
8 +
7 public static final MessageSubject CLUSTER_MEMBERSHIP_EVENT = new MessageSubject("CLUSTER_MEMBERSHIP_EVENT"); 9 public static final MessageSubject CLUSTER_MEMBERSHIP_EVENT = new MessageSubject("CLUSTER_MEMBERSHIP_EVENT");
8 } 10 }
......
1 -package org.onlab.onos.store.cluster.messaging.impl; 1 +package org.onlab.onos.store.cluster.impl;
2 2
3 import org.onlab.onos.cluster.ControllerNode; 3 import org.onlab.onos.cluster.ControllerNode;
4 4
......
1 -package org.onlab.onos.store.cluster.messaging.impl; 1 +package org.onlab.onos.store.cluster.impl;
2 2
3 public enum ClusterMembershipEventType { 3 public enum ClusterMembershipEventType {
4 NEW_MEMBER, 4 NEW_MEMBER,
......
...@@ -7,11 +7,9 @@ import com.google.common.cache.RemovalNotification; ...@@ -7,11 +7,9 @@ import com.google.common.cache.RemovalNotification;
7 import com.google.common.collect.ImmutableSet; 7 import com.google.common.collect.ImmutableSet;
8 8
9 import org.apache.felix.scr.annotations.Activate; 9 import org.apache.felix.scr.annotations.Activate;
10 -import org.apache.felix.scr.annotations.Component;
11 import org.apache.felix.scr.annotations.Deactivate; 10 import org.apache.felix.scr.annotations.Deactivate;
12 import org.apache.felix.scr.annotations.Reference; 11 import org.apache.felix.scr.annotations.Reference;
13 import org.apache.felix.scr.annotations.ReferenceCardinality; 12 import org.apache.felix.scr.annotations.ReferenceCardinality;
14 -import org.apache.felix.scr.annotations.Service;
15 import org.onlab.onos.cluster.ClusterEvent; 13 import org.onlab.onos.cluster.ClusterEvent;
16 import org.onlab.onos.cluster.ClusterStore; 14 import org.onlab.onos.cluster.ClusterStore;
17 import org.onlab.onos.cluster.ClusterStoreDelegate; 15 import org.onlab.onos.cluster.ClusterStoreDelegate;
...@@ -37,8 +35,8 @@ import static org.onlab.packet.IpPrefix.valueOf; ...@@ -37,8 +35,8 @@ import static org.onlab.packet.IpPrefix.valueOf;
37 /** 35 /**
38 * Distributed implementation of the cluster nodes store. 36 * Distributed implementation of the cluster nodes store.
39 */ 37 */
40 -@Component(immediate = true) 38 +//@Component(immediate = true)
41 -@Service 39 +//@Service
42 public class DistributedClusterStore 40 public class DistributedClusterStore
43 extends AbstractStore<ClusterEvent, ClusterStoreDelegate> 41 extends AbstractStore<ClusterEvent, ClusterStoreDelegate>
44 implements ClusterStore { 42 implements ClusterStore {
......
...@@ -3,6 +3,8 @@ package org.onlab.onos.store.cluster.messaging; ...@@ -3,6 +3,8 @@ package org.onlab.onos.store.cluster.messaging;
3 import org.onlab.onos.cluster.ControllerNode; 3 import org.onlab.onos.cluster.ControllerNode;
4 import org.onlab.onos.store.cluster.impl.ClusterNodesDelegate; 4 import org.onlab.onos.store.cluster.impl.ClusterNodesDelegate;
5 5
6 +// TODO: This service interface can be removed, once we properly start
7 +// using ClusterService
6 /** 8 /**
7 * Service for administering communications manager. 9 * Service for administering communications manager.
8 */ 10 */
......
...@@ -2,6 +2,7 @@ package org.onlab.onos.store.cluster.messaging; ...@@ -2,6 +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: Should payload type be ByteBuffer?
5 /** 6 /**
6 * Base message for cluster-wide communications. 7 * Base message for cluster-wide communications.
7 */ 8 */
...@@ -9,14 +10,14 @@ public class ClusterMessage { ...@@ -9,14 +10,14 @@ public class ClusterMessage {
9 10
10 private final NodeId sender; 11 private final NodeId sender;
11 private final MessageSubject subject; 12 private final MessageSubject subject;
12 - private final Object payload; 13 + private final byte[] payload;
13 14
14 /** 15 /**
15 * Creates a cluster message. 16 * Creates a cluster message.
16 * 17 *
17 * @param subject message subject 18 * @param subject message subject
18 */ 19 */
19 - public ClusterMessage(NodeId sender, MessageSubject subject, Object payload) { 20 + public ClusterMessage(NodeId sender, MessageSubject subject, byte[] payload) {
20 this.sender = sender; 21 this.sender = sender;
21 this.subject = subject; 22 this.subject = subject;
22 this.payload = payload; 23 this.payload = payload;
...@@ -45,7 +46,7 @@ public class ClusterMessage { ...@@ -45,7 +46,7 @@ public class ClusterMessage {
45 * 46 *
46 * @return message payload. 47 * @return message payload.
47 */ 48 */
48 - public Object payload() { 49 + public byte[] payload() {
49 return payload; 50 return payload;
50 } 51 }
51 } 52 }
......
1 package org.onlab.onos.store.cluster.messaging; 1 package org.onlab.onos.store.cluster.messaging;
2 2
3 +import static com.google.common.base.Preconditions.checkNotNull;
4 +
5 +import java.util.Objects;
6 +
3 /** 7 /**
4 * Representation of a message subject. 8 * Representation of a message subject.
5 * Cluster messages have associated subjects that dictate how they get handled 9 * Cluster messages have associated subjects that dictate how they get handled
...@@ -10,7 +14,7 @@ public class MessageSubject { ...@@ -10,7 +14,7 @@ public class MessageSubject {
10 private final String value; 14 private final String value;
11 15
12 public MessageSubject(String value) { 16 public MessageSubject(String value) {
13 - this.value = value; 17 + this.value = checkNotNull(value);
14 } 18 }
15 19
16 public String value() { 20 public String value() {
...@@ -21,4 +25,29 @@ public class MessageSubject { ...@@ -21,4 +25,29 @@ public class MessageSubject {
21 public String toString() { 25 public String toString() {
22 return value; 26 return value;
23 } 27 }
28 +
29 + @Override
30 + public int hashCode() {
31 + return value.hashCode();
32 + }
33 +
34 + @Override
35 + public boolean equals(Object obj) {
36 + if (this == obj) {
37 + return true;
38 + }
39 + if (obj == null) {
40 + return false;
41 + }
42 + if (getClass() != obj.getClass()) {
43 + return false;
44 + }
45 + MessageSubject that = (MessageSubject) obj;
46 + return Objects.equals(this.value, that.value);
47 + }
48 +
49 + // for serializer
50 + protected MessageSubject() {
51 + this.value = "";
52 + }
24 } 53 }
......
1 package org.onlab.onos.store.cluster.messaging; 1 package org.onlab.onos.store.cluster.messaging;
2 2
3 /** 3 /**
4 - * Service for encoding &amp; decoding intra-cluster messages. 4 + * Service for encoding &amp; decoding intra-cluster message payload.
5 */ 5 */
6 public interface SerializationService { 6 public interface SerializationService {
7 7
...@@ -11,7 +11,7 @@ public interface SerializationService { ...@@ -11,7 +11,7 @@ public interface SerializationService {
11 * @param buffer byte buffer with message(s) 11 * @param buffer byte buffer with message(s)
12 * @return parsed message 12 * @return parsed message
13 */ 13 */
14 - Object decode(byte[] data); 14 + <T> T decode(byte[] data);
15 15
16 /** 16 /**
17 * Encodes the specified message into the given byte buffer. 17 * Encodes the specified message into the given byte buffer.
......
...@@ -3,30 +3,36 @@ package org.onlab.onos.store.cluster.messaging.impl; ...@@ -3,30 +3,36 @@ 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;
15 import org.apache.felix.scr.annotations.Reference; 12 import org.apache.felix.scr.annotations.Reference;
16 import org.apache.felix.scr.annotations.ReferenceCardinality; 13 import org.apache.felix.scr.annotations.ReferenceCardinality;
17 import org.apache.felix.scr.annotations.Service; 14 import org.apache.felix.scr.annotations.Service;
15 +import org.onlab.onos.cluster.ClusterService;
18 import org.onlab.onos.cluster.ControllerNode; 16 import org.onlab.onos.cluster.ControllerNode;
19 import org.onlab.onos.cluster.NodeId; 17 import org.onlab.onos.cluster.NodeId;
18 +import org.onlab.onos.store.cluster.impl.ClusterMembershipEvent;
19 +import org.onlab.onos.store.cluster.impl.ClusterMembershipEventType;
20 import org.onlab.onos.store.cluster.impl.ClusterNodesDelegate; 20 import org.onlab.onos.store.cluster.impl.ClusterNodesDelegate;
21 import org.onlab.onos.store.cluster.messaging.ClusterCommunicationAdminService; 21 import org.onlab.onos.store.cluster.messaging.ClusterCommunicationAdminService;
22 import org.onlab.onos.store.cluster.messaging.ClusterCommunicationService; 22 import org.onlab.onos.store.cluster.messaging.ClusterCommunicationService;
23 import org.onlab.onos.store.cluster.messaging.ClusterMessage; 23 import org.onlab.onos.store.cluster.messaging.ClusterMessage;
24 import org.onlab.onos.store.cluster.messaging.ClusterMessageHandler; 24 import org.onlab.onos.store.cluster.messaging.ClusterMessageHandler;
25 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;
27 +import org.onlab.onos.store.serializers.KryoPoolUtil;
28 +import org.onlab.onos.store.serializers.KryoSerializer;
29 +import org.onlab.onos.store.serializers.MessageSubjectSerializer;
30 +import org.onlab.util.KryoPool;
26 import org.onlab.netty.Endpoint; 31 import org.onlab.netty.Endpoint;
27 import org.onlab.netty.Message; 32 import org.onlab.netty.Message;
28 import org.onlab.netty.MessageHandler; 33 import org.onlab.netty.MessageHandler;
29 import org.onlab.netty.MessagingService; 34 import org.onlab.netty.MessagingService;
35 +import org.onlab.netty.NettyMessagingService;
30 import org.slf4j.Logger; 36 import org.slf4j.Logger;
31 import org.slf4j.LoggerFactory; 37 import org.slf4j.LoggerFactory;
32 38
...@@ -38,28 +44,57 @@ public class ClusterCommunicationManager ...@@ -38,28 +44,57 @@ public class ClusterCommunicationManager
38 private final Logger log = LoggerFactory.getLogger(getClass()); 44 private final Logger log = LoggerFactory.getLogger(getClass());
39 45
40 private ControllerNode localNode; 46 private ControllerNode localNode;
47 +
48 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
49 + private ClusterService clusterService;
50 +
41 private ClusterNodesDelegate nodesDelegate; 51 private ClusterNodesDelegate nodesDelegate;
42 - private Map<NodeId, ControllerNode> members = new HashMap<>();
43 private final Timer timer = new Timer("onos-controller-heatbeats"); 52 private final Timer timer = new Timer("onos-controller-heatbeats");
44 public static final long HEART_BEAT_INTERVAL_MILLIS = 1000L; 53 public static final long HEART_BEAT_INTERVAL_MILLIS = 1000L;
45 54
46 - @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) 55 + // TODO: This probably should not be a OSGi service.
47 private MessagingService messagingService; 56 private MessagingService messagingService;
48 57
58 + private static final KryoSerializer SERIALIZER = new KryoSerializer() {
59 + @Override
60 + protected void setupKryoPool() {
61 + serializerPool = KryoPool.newBuilder()
62 + .register(KryoPoolUtil.API)
63 + .register(ClusterMessage.class, new ClusterMessageSerializer())
64 + .register(ClusterMembershipEvent.class)
65 + .register(byte[].class)
66 + .register(MessageSubject.class, new MessageSubjectSerializer())
67 + .build()
68 + .populate(1);
69 + }
70 +
71 + };
72 +
49 @Activate 73 @Activate
50 public void activate() { 74 public void activate() {
75 + localNode = clusterService.getLocalNode();
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;
51 log.info("Started"); 85 log.info("Started");
52 } 86 }
53 87
54 @Deactivate 88 @Deactivate
55 public void deactivate() { 89 public void deactivate() {
90 + // TODO: cleanup messageingService if needed.
56 log.info("Stopped"); 91 log.info("Stopped");
57 } 92 }
58 93
59 @Override 94 @Override
60 public boolean broadcast(ClusterMessage message) { 95 public boolean broadcast(ClusterMessage message) {
61 boolean ok = true; 96 boolean ok = true;
62 - for (ControllerNode node : members.values()) { 97 + for (ControllerNode node : clusterService.getNodes()) {
63 if (!node.equals(localNode)) { 98 if (!node.equals(localNode)) {
64 ok = unicast(message, node.id()) && ok; 99 ok = unicast(message, node.id()) && ok;
65 } 100 }
...@@ -80,11 +115,12 @@ public class ClusterCommunicationManager ...@@ -80,11 +115,12 @@ public class ClusterCommunicationManager
80 115
81 @Override 116 @Override
82 public boolean unicast(ClusterMessage message, NodeId toNodeId) { 117 public boolean unicast(ClusterMessage message, NodeId toNodeId) {
83 - ControllerNode node = members.get(toNodeId); 118 + ControllerNode node = clusterService.getNode(toNodeId);
84 checkArgument(node != null, "Unknown nodeId: %s", toNodeId); 119 checkArgument(node != null, "Unknown nodeId: %s", toNodeId);
85 Endpoint nodeEp = new Endpoint(node.ip().toString(), node.tcpPort()); 120 Endpoint nodeEp = new Endpoint(node.ip().toString(), node.tcpPort());
86 try { 121 try {
87 - messagingService.sendAsync(nodeEp, message.subject().value(), message); 122 + messagingService.sendAsync(nodeEp,
123 + message.subject().value(), SERIALIZER.encode(message));
88 return true; 124 return true;
89 } catch (IOException e) { 125 } catch (IOException e) {
90 log.error("Failed to send cluster message to nodeId: " + toNodeId, e); 126 log.error("Failed to send cluster message to nodeId: " + toNodeId, e);
...@@ -110,7 +146,7 @@ public class ClusterCommunicationManager ...@@ -110,7 +146,7 @@ public class ClusterCommunicationManager
110 146
111 @Override 147 @Override
112 public void addNode(ControllerNode node) { 148 public void addNode(ControllerNode node) {
113 - members.put(node.id(), node); 149 + //members.put(node.id(), node);
114 } 150 }
115 151
116 @Override 152 @Override
...@@ -118,8 +154,8 @@ public class ClusterCommunicationManager ...@@ -118,8 +154,8 @@ public class ClusterCommunicationManager
118 broadcast(new ClusterMessage( 154 broadcast(new ClusterMessage(
119 localNode.id(), 155 localNode.id(),
120 new MessageSubject("CLUSTER_MEMBERSHIP_EVENT"), 156 new MessageSubject("CLUSTER_MEMBERSHIP_EVENT"),
121 - new ClusterMembershipEvent(ClusterMembershipEventType.LEAVING_MEMBER, node))); 157 + SERIALIZER.encode(new ClusterMembershipEvent(ClusterMembershipEventType.LEAVING_MEMBER, node))));
122 - members.remove(node.id()); 158 + //members.remove(node.id());
123 } 159 }
124 160
125 // Sends a heart beat to all peers. 161 // Sends a heart beat to all peers.
...@@ -130,7 +166,7 @@ public class ClusterCommunicationManager ...@@ -130,7 +166,7 @@ public class ClusterCommunicationManager
130 broadcast(new ClusterMessage( 166 broadcast(new ClusterMessage(
131 localNode.id(), 167 localNode.id(),
132 new MessageSubject("CLUSTER_MEMBERSHIP_EVENT"), 168 new MessageSubject("CLUSTER_MEMBERSHIP_EVENT"),
133 - new ClusterMembershipEvent(ClusterMembershipEventType.HEART_BEAT, localNode))); 169 + SERIALIZER.encode(new ClusterMembershipEvent(ClusterMembershipEventType.HEART_BEAT, localNode))));
134 } 170 }
135 } 171 }
136 172
...@@ -139,7 +175,7 @@ public class ClusterCommunicationManager ...@@ -139,7 +175,7 @@ public class ClusterCommunicationManager
139 @Override 175 @Override
140 public void handle(ClusterMessage message) { 176 public void handle(ClusterMessage message) {
141 177
142 - ClusterMembershipEvent event = (ClusterMembershipEvent) message.payload(); 178 + ClusterMembershipEvent event = SERIALIZER.decode(message.payload());
143 ControllerNode node = event.node(); 179 ControllerNode node = event.node();
144 if (event.type() == ClusterMembershipEventType.HEART_BEAT) { 180 if (event.type() == ClusterMembershipEventType.HEART_BEAT) {
145 log.info("Node {} sent a hearbeat", node.id()); 181 log.info("Node {} sent a hearbeat", node.id());
...@@ -154,7 +190,7 @@ public class ClusterCommunicationManager ...@@ -154,7 +190,7 @@ public class ClusterCommunicationManager
154 } 190 }
155 } 191 }
156 192
157 - private static class InternalClusterMessageHandler implements MessageHandler { 193 + private final class InternalClusterMessageHandler implements MessageHandler {
158 194
159 private final ClusterMessageHandler handler; 195 private final ClusterMessageHandler handler;
160 196
...@@ -164,7 +200,13 @@ public class ClusterCommunicationManager ...@@ -164,7 +200,13 @@ public class ClusterCommunicationManager
164 200
165 @Override 201 @Override
166 public void handle(Message message) { 202 public void handle(Message message) {
167 - handler.handle((ClusterMessage) message.payload()); 203 + try {
204 + ClusterMessage clusterMessage = SERIALIZER.decode(message.payload());
205 + handler.handle(clusterMessage);
206 + } catch (Exception e) {
207 + log.error("Exception caught during ClusterMessageHandler", e);
208 + throw e;
209 + }
168 } 210 }
169 } 211 }
170 } 212 }
......
1 -package org.onlab.onos.store.cluster.impl; 1 +package org.onlab.onos.store.cluster.messaging.impl;
2 2
3 -import de.javakaffee.kryoserializers.URISerializer;
4 import org.apache.felix.scr.annotations.Activate; 3 import org.apache.felix.scr.annotations.Activate;
5 import org.apache.felix.scr.annotations.Component; 4 import org.apache.felix.scr.annotations.Component;
6 import org.apache.felix.scr.annotations.Deactivate; 5 import org.apache.felix.scr.annotations.Deactivate;
7 import org.apache.felix.scr.annotations.Service; 6 import org.apache.felix.scr.annotations.Service;
8 -import org.onlab.onos.cluster.ControllerNode;
9 -import org.onlab.onos.cluster.DefaultControllerNode;
10 -import org.onlab.onos.cluster.NodeId;
11 -import org.onlab.onos.net.ConnectPoint;
12 -import org.onlab.onos.net.DefaultDevice;
13 -import org.onlab.onos.net.DefaultLink;
14 -import org.onlab.onos.net.DefaultPort;
15 -import org.onlab.onos.net.Device;
16 -import org.onlab.onos.net.DeviceId;
17 -import org.onlab.onos.net.Element;
18 -import org.onlab.onos.net.Link;
19 -import org.onlab.onos.net.LinkKey;
20 -import org.onlab.onos.net.MastershipRole;
21 -import org.onlab.onos.net.Port;
22 -import org.onlab.onos.net.PortNumber;
23 -import org.onlab.onos.net.provider.ProviderId;
24 import org.onlab.onos.store.cluster.messaging.MessageSubject; 7 import org.onlab.onos.store.cluster.messaging.MessageSubject;
25 import org.onlab.onos.store.cluster.messaging.SerializationService; 8 import org.onlab.onos.store.cluster.messaging.SerializationService;
26 -import org.onlab.onos.store.serializers.ConnectPointSerializer; 9 +import org.onlab.onos.store.serializers.KryoPoolUtil;
27 -import org.onlab.onos.store.serializers.DefaultLinkSerializer;
28 -import org.onlab.onos.store.serializers.DefaultPortSerializer;
29 -import org.onlab.onos.store.serializers.DeviceIdSerializer;
30 -import org.onlab.onos.store.serializers.IpPrefixSerializer;
31 -import org.onlab.onos.store.serializers.LinkKeySerializer;
32 -import org.onlab.onos.store.serializers.NodeIdSerializer;
33 -import org.onlab.onos.store.serializers.PortNumberSerializer;
34 -import org.onlab.onos.store.serializers.ProviderIdSerializer;
35 -import org.onlab.packet.IpPrefix;
36 import org.onlab.util.KryoPool; 10 import org.onlab.util.KryoPool;
37 import org.slf4j.Logger; 11 import org.slf4j.Logger;
38 import org.slf4j.LoggerFactory; 12 import org.slf4j.LoggerFactory;
39 13
40 -import java.net.URI;
41 -import java.util.ArrayList;
42 -import java.util.HashMap;
43 -
44 /** 14 /**
45 * Factory for parsing messages sent between cluster members. 15 * Factory for parsing messages sent between cluster members.
46 */ 16 */
...@@ -72,41 +42,17 @@ public class MessageSerializer implements SerializationService { ...@@ -72,41 +42,17 @@ public class MessageSerializer implements SerializationService {
72 * Sets up the common serialzers pool. 42 * Sets up the common serialzers pool.
73 */ 43 */
74 protected void setupKryoPool() { 44 protected void setupKryoPool() {
75 - // FIXME Slice out types used in common to separate pool/namespace.
76 serializerPool = KryoPool.newBuilder() 45 serializerPool = KryoPool.newBuilder()
77 - .register(ArrayList.class, 46 + .register(KryoPoolUtil.API)
78 - HashMap.class, 47 + // TODO: Should MessageSubject be in API bundle?
79 - 48 + .register(MessageSubject.class)
80 - ControllerNode.State.class,
81 - Device.Type.class,
82 -
83 - DefaultControllerNode.class,
84 - DefaultDevice.class,
85 - MastershipRole.class,
86 - Port.class,
87 - Element.class,
88 -
89 - Link.Type.class,
90 -
91 - MessageSubject.class
92 - )
93 - .register(IpPrefix.class, new IpPrefixSerializer())
94 - .register(URI.class, new URISerializer())
95 - .register(NodeId.class, new NodeIdSerializer())
96 - .register(ProviderId.class, new ProviderIdSerializer())
97 - .register(DeviceId.class, new DeviceIdSerializer())
98 - .register(PortNumber.class, new PortNumberSerializer())
99 - .register(DefaultPort.class, new DefaultPortSerializer())
100 - .register(LinkKey.class, new LinkKeySerializer())
101 - .register(ConnectPoint.class, new ConnectPointSerializer())
102 - .register(DefaultLink.class, new DefaultLinkSerializer())
103 .build() 49 .build()
104 .populate(1); 50 .populate(1);
105 } 51 }
106 52
107 53
108 @Override 54 @Override
109 - public Object decode(byte[] data) { 55 + public <T> T decode(byte[] data) {
110 return serializerPool.deserialize(data); 56 return serializerPool.deserialize(data);
111 } 57 }
112 58
......
1 +/**
2 + * Implementation of the cluster messaging mechanism.
3 + */
4 +package org.onlab.onos.store.cluster.messaging.impl;
...\ No newline at end of file ...\ No newline at end of file
1 -package org.onlab.onos.store.cluster.messaging; 1 +package org.onlab.onos.store.common.impl;
2 2
3 import java.util.Map; 3 import java.util.Map;
4 4
......
1 -package org.onlab.onos.store.cluster.messaging; 1 +package org.onlab.onos.store.common.impl;
2 2
3 import java.util.Map; 3 import java.util.Map;
4 import java.util.Set; 4 import java.util.Set;
......
1 -package org.onlab.onos.store.impl; 1 +package org.onlab.onos.store.common.impl;
2 2
3 import static com.google.common.base.Preconditions.checkArgument; 3 import static com.google.common.base.Preconditions.checkArgument;
4 4
...@@ -9,12 +9,11 @@ import org.onlab.onos.store.Timestamp; ...@@ -9,12 +9,11 @@ import org.onlab.onos.store.Timestamp;
9 import com.google.common.base.MoreObjects; 9 import com.google.common.base.MoreObjects;
10 import com.google.common.collect.ComparisonChain; 10 import com.google.common.collect.ComparisonChain;
11 11
12 -// If it is store specific, implement serializable interfaces?
13 /** 12 /**
14 * Default implementation of Timestamp. 13 * Default implementation of Timestamp.
15 * TODO: Better documentation. 14 * TODO: Better documentation.
16 */ 15 */
17 -public final class OnosTimestamp implements Timestamp { 16 +public final class MastershipBasedTimestamp implements Timestamp {
18 17
19 private final int termNumber; 18 private final int termNumber;
20 private final int sequenceNumber; 19 private final int sequenceNumber;
...@@ -25,15 +24,16 @@ public final class OnosTimestamp implements Timestamp { ...@@ -25,15 +24,16 @@ public final class OnosTimestamp implements Timestamp {
25 * @param termNumber the mastership termNumber 24 * @param termNumber the mastership termNumber
26 * @param sequenceNumber the sequenceNumber number within the termNumber 25 * @param sequenceNumber the sequenceNumber number within the termNumber
27 */ 26 */
28 - public OnosTimestamp(int termNumber, int sequenceNumber) { 27 + public MastershipBasedTimestamp(int termNumber, int sequenceNumber) {
29 this.termNumber = termNumber; 28 this.termNumber = termNumber;
30 this.sequenceNumber = sequenceNumber; 29 this.sequenceNumber = sequenceNumber;
31 } 30 }
32 31
33 @Override 32 @Override
34 public int compareTo(Timestamp o) { 33 public int compareTo(Timestamp o) {
35 - checkArgument(o instanceof OnosTimestamp, "Must be OnosTimestamp", o); 34 + checkArgument(o instanceof MastershipBasedTimestamp,
36 - OnosTimestamp that = (OnosTimestamp) o; 35 + "Must be MastershipBasedTimestamp", o);
36 + MastershipBasedTimestamp that = (MastershipBasedTimestamp) o;
37 37
38 return ComparisonChain.start() 38 return ComparisonChain.start()
39 .compare(this.termNumber, that.termNumber) 39 .compare(this.termNumber, that.termNumber)
...@@ -51,10 +51,10 @@ public final class OnosTimestamp implements Timestamp { ...@@ -51,10 +51,10 @@ public final class OnosTimestamp implements Timestamp {
51 if (this == obj) { 51 if (this == obj) {
52 return true; 52 return true;
53 } 53 }
54 - if (!(obj instanceof OnosTimestamp)) { 54 + if (!(obj instanceof MastershipBasedTimestamp)) {
55 return false; 55 return false;
56 } 56 }
57 - OnosTimestamp that = (OnosTimestamp) obj; 57 + MastershipBasedTimestamp that = (MastershipBasedTimestamp) obj;
58 return Objects.equals(this.termNumber, that.termNumber) && 58 return Objects.equals(this.termNumber, that.termNumber) &&
59 Objects.equals(this.sequenceNumber, that.sequenceNumber); 59 Objects.equals(this.sequenceNumber, that.sequenceNumber);
60 } 60 }
...@@ -84,4 +84,11 @@ public final class OnosTimestamp implements Timestamp { ...@@ -84,4 +84,11 @@ public final class OnosTimestamp implements Timestamp {
84 public int sequenceNumber() { 84 public int sequenceNumber() {
85 return sequenceNumber; 85 return sequenceNumber;
86 } 86 }
87 +
88 + // Default constructor for serialization
89 + @Deprecated
90 + protected MastershipBasedTimestamp() {
91 + this.termNumber = -1;
92 + this.sequenceNumber = -1;
93 + }
87 } 94 }
......
1 +package org.onlab.onos.store.common.impl;
2 +
3 +import static com.google.common.base.Preconditions.checkNotNull;
4 +
5 +import java.util.Objects;
6 +
7 +import org.onlab.onos.store.Timestamp;
8 +
9 +import com.google.common.base.MoreObjects;
10 +
11 +/**
12 + * Wrapper class to store Timestamped value.
13 + * @param <T>
14 + */
15 +public final class Timestamped<T> {
16 +
17 + private final Timestamp timestamp;
18 + private final T value;
19 +
20 + /**
21 + * Creates a time stamped value.
22 + *
23 + * @param value to be timestamp
24 + * @param timestamp the timestamp
25 + */
26 + public Timestamped(T value, Timestamp timestamp) {
27 + this.value = checkNotNull(value);
28 + this.timestamp = checkNotNull(timestamp);
29 + }
30 +
31 + /**
32 + * Returns the value.
33 + * @return value
34 + */
35 + public T value() {
36 + return value;
37 + }
38 +
39 + /**
40 + * Returns the time stamp.
41 + * @return time stamp
42 + */
43 + public Timestamp timestamp() {
44 + return timestamp;
45 + }
46 +
47 + /**
48 + * Tests if this timestamped value is newer than the other.
49 + *
50 + * @param other timestamped value
51 + * @return true if this instance is newer.
52 + */
53 + public boolean isNewer(Timestamped<T> other) {
54 + return this.timestamp.compareTo(checkNotNull(other).timestamp()) > 0;
55 + }
56 +
57 + @Override
58 + public int hashCode() {
59 + return timestamp.hashCode();
60 + }
61 +
62 + @Override
63 + public boolean equals(Object obj) {
64 + if (this == obj) {
65 + return true;
66 + }
67 + if (!(obj instanceof Timestamped)) {
68 + return false;
69 + }
70 + @SuppressWarnings("unchecked")
71 + Timestamped<T> that = (Timestamped<T>) obj;
72 + return Objects.equals(this.timestamp, that.timestamp);
73 + }
74 +
75 + @Override
76 + public String toString() {
77 + return MoreObjects.toStringHelper(getClass())
78 + .add("timestamp", timestamp)
79 + .add("value", value)
80 + .toString();
81 + }
82 +
83 + // Default constructor for serialization
84 + @Deprecated
85 + private Timestamped() {
86 + this.value = null;
87 + this.timestamp = null;
88 + }
89 +}
1 +/**
2 + * Common abstractions and facilities for implementing distributed store
3 + * using gossip protocol.
4 + */
5 +package org.onlab.onos.store.common.impl;
...@@ -8,7 +8,7 @@ import org.onlab.onos.cluster.NodeId; ...@@ -8,7 +8,7 @@ import org.onlab.onos.cluster.NodeId;
8 import org.onlab.onos.net.Device; 8 import org.onlab.onos.net.Device;
9 import org.onlab.onos.net.DeviceId; 9 import org.onlab.onos.net.DeviceId;
10 import org.onlab.onos.store.Timestamp; 10 import org.onlab.onos.store.Timestamp;
11 -import org.onlab.onos.store.cluster.messaging.AntiEntropyAdvertisement; 11 +import org.onlab.onos.store.common.impl.AntiEntropyAdvertisement;
12 12
13 // TODO DeviceID needs to be changed to something like (ProviderID, DeviceID) 13 // TODO DeviceID needs to be changed to something like (ProviderID, DeviceID)
14 // TODO: Handle Port as part of these messages, or separate messages for Ports? 14 // TODO: Handle Port as part of these messages, or separate messages for Ports?
......
...@@ -10,7 +10,7 @@ import org.onlab.onos.cluster.NodeId; ...@@ -10,7 +10,7 @@ import org.onlab.onos.cluster.NodeId;
10 import org.onlab.onos.net.Device; 10 import org.onlab.onos.net.Device;
11 import org.onlab.onos.net.DeviceId; 11 import org.onlab.onos.net.DeviceId;
12 import org.onlab.onos.store.Timestamp; 12 import org.onlab.onos.store.Timestamp;
13 -import org.onlab.onos.store.cluster.messaging.AntiEntropyReply; 13 +import org.onlab.onos.store.common.impl.AntiEntropyReply;
14 14
15 import com.google.common.collect.ImmutableMap; 15 import com.google.common.collect.ImmutableMap;
16 import com.google.common.collect.ImmutableSet; 16 import com.google.common.collect.ImmutableSet;
......
...@@ -12,14 +12,18 @@ import org.apache.felix.scr.annotations.Deactivate; ...@@ -12,14 +12,18 @@ import org.apache.felix.scr.annotations.Deactivate;
12 import org.apache.felix.scr.annotations.Service; 12 import org.apache.felix.scr.annotations.Service;
13 import org.onlab.onos.cluster.MastershipTerm; 13 import org.onlab.onos.cluster.MastershipTerm;
14 import org.onlab.onos.net.DeviceId; 14 import org.onlab.onos.net.DeviceId;
15 +import org.onlab.onos.store.ClockProviderService;
15 import org.onlab.onos.store.ClockService; 16 import org.onlab.onos.store.ClockService;
16 import org.onlab.onos.store.Timestamp; 17 import org.onlab.onos.store.Timestamp;
17 -import org.onlab.onos.store.impl.OnosTimestamp; 18 +import org.onlab.onos.store.common.impl.MastershipBasedTimestamp;
18 import org.slf4j.Logger; 19 import org.slf4j.Logger;
19 20
21 +/**
22 + * Clock service to issue Timestamp based on Device Mastership.
23 + */
20 @Component(immediate = true) 24 @Component(immediate = true)
21 @Service 25 @Service
22 -public class OnosClockService implements ClockService { 26 +public class DeviceClockManager implements ClockService, ClockProviderService {
23 27
24 private final Logger log = getLogger(getClass()); 28 private final Logger log = getLogger(getClass());
25 29
...@@ -43,7 +47,7 @@ public class OnosClockService implements ClockService { ...@@ -43,7 +47,7 @@ public class OnosClockService implements ClockService {
43 if (term == null) { 47 if (term == null) {
44 throw new IllegalStateException("Requesting timestamp for a deviceId without mastership"); 48 throw new IllegalStateException("Requesting timestamp for a deviceId without mastership");
45 } 49 }
46 - return new OnosTimestamp(term.termNumber(), ticker.incrementAndGet()); 50 + return new MastershipBasedTimestamp(term.termNumber(), ticker.incrementAndGet());
47 } 51 }
48 52
49 @Override 53 @Override
......
1 +package org.onlab.onos.store.device.impl;
2 +
3 +import com.google.common.collect.FluentIterable;
4 +import com.google.common.collect.ImmutableList;
5 +import com.google.common.collect.Maps;
6 +import com.google.common.collect.Sets;
7 +
8 +import org.apache.commons.lang3.concurrent.ConcurrentException;
9 +import org.apache.commons.lang3.concurrent.ConcurrentInitializer;
10 +import org.apache.felix.scr.annotations.Activate;
11 +import org.apache.felix.scr.annotations.Component;
12 +import org.apache.felix.scr.annotations.Deactivate;
13 +import org.apache.felix.scr.annotations.Reference;
14 +import org.apache.felix.scr.annotations.ReferenceCardinality;
15 +import org.apache.felix.scr.annotations.Service;
16 +import org.onlab.onos.cluster.ClusterService;
17 +import org.onlab.onos.net.AnnotationsUtil;
18 +import org.onlab.onos.net.DefaultAnnotations;
19 +import org.onlab.onos.net.DefaultDevice;
20 +import org.onlab.onos.net.DefaultPort;
21 +import org.onlab.onos.net.Device;
22 +import org.onlab.onos.net.Device.Type;
23 +import org.onlab.onos.net.DeviceId;
24 +import org.onlab.onos.net.Port;
25 +import org.onlab.onos.net.PortNumber;
26 +import org.onlab.onos.net.SparseAnnotations;
27 +import org.onlab.onos.net.device.DefaultDeviceDescription;
28 +import org.onlab.onos.net.device.DefaultPortDescription;
29 +import org.onlab.onos.net.device.DeviceDescription;
30 +import org.onlab.onos.net.device.DeviceEvent;
31 +import org.onlab.onos.net.device.DeviceStore;
32 +import org.onlab.onos.net.device.DeviceStoreDelegate;
33 +import org.onlab.onos.net.device.PortDescription;
34 +import org.onlab.onos.net.provider.ProviderId;
35 +import org.onlab.onos.store.AbstractStore;
36 +import org.onlab.onos.store.ClockService;
37 +import org.onlab.onos.store.Timestamp;
38 +import org.onlab.onos.store.cluster.messaging.ClusterCommunicationService;
39 +import org.onlab.onos.store.cluster.messaging.ClusterMessage;
40 +import org.onlab.onos.store.cluster.messaging.ClusterMessageHandler;
41 +import org.onlab.onos.store.common.impl.MastershipBasedTimestamp;
42 +import org.onlab.onos.store.common.impl.Timestamped;
43 +import org.onlab.onos.store.serializers.KryoPoolUtil;
44 +import org.onlab.onos.store.serializers.KryoSerializer;
45 +import org.onlab.onos.store.serializers.MastershipBasedTimestampSerializer;
46 +import org.onlab.util.KryoPool;
47 +import org.onlab.util.NewConcurrentHashMap;
48 +import org.slf4j.Logger;
49 +
50 +import java.io.IOException;
51 +import java.util.ArrayList;
52 +import java.util.Collections;
53 +import java.util.HashSet;
54 +import java.util.Iterator;
55 +import java.util.List;
56 +import java.util.Map;
57 +import java.util.Map.Entry;
58 +import java.util.Objects;
59 +import java.util.Set;
60 +import java.util.concurrent.ConcurrentHashMap;
61 +import java.util.concurrent.ConcurrentMap;
62 +import java.util.concurrent.atomic.AtomicReference;
63 +
64 +import static com.google.common.base.Preconditions.checkArgument;
65 +import static com.google.common.base.Preconditions.checkNotNull;
66 +import static com.google.common.base.Predicates.notNull;
67 +import static org.onlab.onos.net.device.DeviceEvent.Type.*;
68 +import static org.slf4j.LoggerFactory.getLogger;
69 +import static org.apache.commons.lang3.concurrent.ConcurrentUtils.createIfAbsentUnchecked;
70 +import static org.onlab.onos.net.DefaultAnnotations.merge;
71 +import static org.onlab.onos.net.DefaultAnnotations.union;
72 +import static com.google.common.base.Verify.verify;
73 +
74 +// TODO: give me a better name
75 +/**
76 + * Manages inventory of infrastructure devices using gossip protocol to distribute
77 + * information.
78 + */
79 +@Component(immediate = true)
80 +@Service
81 +public class GossipDeviceStore
82 + extends AbstractStore<DeviceEvent, DeviceStoreDelegate>
83 + implements DeviceStore {
84 +
85 + private final Logger log = getLogger(getClass());
86 +
87 + public static final String DEVICE_NOT_FOUND = "Device with ID %s not found";
88 +
89 + // TODO: Check if inner Map can be replaced with plain Map
90 + // innerMap is used to lock a Device, thus instance should never be replaced.
91 + // collection of Description given from various providers
92 + private final ConcurrentMap<DeviceId,
93 + ConcurrentMap<ProviderId, DeviceDescriptions>>
94 + deviceDescs = Maps.newConcurrentMap();
95 +
96 + // cache of Device and Ports generated by compositing descriptions from providers
97 + private final ConcurrentMap<DeviceId, Device> devices = Maps.newConcurrentMap();
98 + private final ConcurrentMap<DeviceId, ConcurrentMap<PortNumber, Port>> devicePorts = Maps.newConcurrentMap();
99 +
100 + // to be updated under Device lock
101 + private final Map<DeviceId, Timestamp> offline = Maps.newHashMap();
102 + private final Map<DeviceId, Timestamp> removalRequest = Maps.newHashMap();
103 +
104 + // available(=UP) devices
105 + private final Set<DeviceId> availableDevices = Sets.newConcurrentHashSet();
106 +
107 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
108 + protected ClockService clockService;
109 +
110 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
111 + protected ClusterCommunicationService clusterCommunicator;
112 +
113 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
114 + protected ClusterService clusterService;
115 +
116 + private static final KryoSerializer SERIALIZER = new KryoSerializer() {
117 + @Override
118 + protected void setupKryoPool() {
119 + serializerPool = KryoPool.newBuilder()
120 + .register(KryoPoolUtil.API)
121 + .register(InternalDeviceEvent.class, new InternalDeviceEventSerializer())
122 + .register(InternalDeviceOfflineEvent.class, new InternalDeviceOfflineEventSerializer())
123 + .register(InternalDeviceRemovedEvent.class)
124 + .register(InternalPortEvent.class, new InternalPortEventSerializer())
125 + .register(InternalPortStatusEvent.class, new InternalPortStatusEventSerializer())
126 + .register(Timestamp.class)
127 + .register(Timestamped.class)
128 + .register(MastershipBasedTimestamp.class, new MastershipBasedTimestampSerializer())
129 + .build()
130 + .populate(1);
131 + }
132 +
133 + };
134 +
135 + @Activate
136 + public void activate() {
137 + clusterCommunicator.addSubscriber(
138 + GossipDeviceStoreMessageSubjects.DEVICE_UPDATE, new InternalDeviceEventListener());
139 + clusterCommunicator.addSubscriber(
140 + GossipDeviceStoreMessageSubjects.DEVICE_OFFLINE, new InternalDeviceOfflineEventListener());
141 + clusterCommunicator.addSubscriber(
142 + GossipDeviceStoreMessageSubjects.DEVICE_REMOVED, new InternalDeviceRemovedEventListener());
143 + clusterCommunicator.addSubscriber(
144 + GossipDeviceStoreMessageSubjects.PORT_UPDATE, new InternalPortEventListener());
145 + clusterCommunicator.addSubscriber(
146 + GossipDeviceStoreMessageSubjects.PORT_STATUS_UPDATE, new InternalPortStatusEventListener());
147 + log.info("Started");
148 + }
149 +
150 + @Deactivate
151 + public void deactivate() {
152 + deviceDescs.clear();
153 + devices.clear();
154 + devicePorts.clear();
155 + availableDevices.clear();
156 + log.info("Stopped");
157 + }
158 +
159 + @Override
160 + public int getDeviceCount() {
161 + return devices.size();
162 + }
163 +
164 + @Override
165 + public Iterable<Device> getDevices() {
166 + return Collections.unmodifiableCollection(devices.values());
167 + }
168 +
169 + @Override
170 + public Device getDevice(DeviceId deviceId) {
171 + return devices.get(deviceId);
172 + }
173 +
174 + @Override
175 + public synchronized DeviceEvent createOrUpdateDevice(ProviderId providerId,
176 + DeviceId deviceId,
177 + DeviceDescription deviceDescription) {
178 + Timestamp newTimestamp = clockService.getTimestamp(deviceId);
179 + final Timestamped<DeviceDescription> deltaDesc = new Timestamped<>(deviceDescription, newTimestamp);
180 + DeviceEvent event = createOrUpdateDeviceInternal(providerId, deviceId, deltaDesc);
181 + if (event != null) {
182 + log.info("Notifying peers of a device update topology event for providerId: {} and deviceId: {}",
183 + providerId, deviceId);
184 + try {
185 + notifyPeers(new InternalDeviceEvent(providerId, deviceId, deltaDesc));
186 + } catch (IOException e) {
187 + log.error("Failed to notify peers of a device update topology event for providerId: "
188 + + providerId + " and deviceId: " + deviceId, e);
189 + }
190 + }
191 + return event;
192 + }
193 +
194 + private DeviceEvent createOrUpdateDeviceInternal(ProviderId providerId,
195 + DeviceId deviceId,
196 + Timestamped<DeviceDescription> deltaDesc) {
197 +
198 + // Collection of DeviceDescriptions for a Device
199 + ConcurrentMap<ProviderId, DeviceDescriptions> providerDescs
200 + = getDeviceDescriptions(deviceId);
201 +
202 + synchronized (providerDescs) {
203 + // locking per device
204 +
205 + if (isDeviceRemoved(deviceId, deltaDesc.timestamp())) {
206 + log.debug("Ignoring outdated event: {}", deltaDesc);
207 + return null;
208 + }
209 +
210 + DeviceDescriptions descs
211 + = createIfAbsentUnchecked(providerDescs, providerId,
212 + new InitDeviceDescs(deltaDesc));
213 +
214 + final Device oldDevice = devices.get(deviceId);
215 + final Device newDevice;
216 +
217 + if (deltaDesc == descs.getDeviceDesc() ||
218 + deltaDesc.isNewer(descs.getDeviceDesc())) {
219 + // on new device or valid update
220 + descs.putDeviceDesc(deltaDesc);
221 + newDevice = composeDevice(deviceId, providerDescs);
222 + } else {
223 + // outdated event, ignored.
224 + return null;
225 + }
226 + if (oldDevice == null) {
227 + // ADD
228 + return createDevice(providerId, newDevice, deltaDesc.timestamp());
229 + } else {
230 + // UPDATE or ignore (no change or stale)
231 + return updateDevice(providerId, oldDevice, newDevice, deltaDesc.timestamp());
232 + }
233 + }
234 + }
235 +
236 + // Creates the device and returns the appropriate event if necessary.
237 + // Guarded by deviceDescs value (=Device lock)
238 + private DeviceEvent createDevice(ProviderId providerId,
239 + Device newDevice, Timestamp timestamp) {
240 +
241 + // update composed device cache
242 + Device oldDevice = devices.putIfAbsent(newDevice.id(), newDevice);
243 + verify(oldDevice == null,
244 + "Unexpected Device in cache. PID:%s [old=%s, new=%s]",
245 + providerId, oldDevice, newDevice);
246 +
247 + if (!providerId.isAncillary()) {
248 + markOnline(newDevice.id(), timestamp);
249 + }
250 +
251 + return new DeviceEvent(DeviceEvent.Type.DEVICE_ADDED, newDevice, null);
252 + }
253 +
254 + // Updates the device and returns the appropriate event if necessary.
255 + // Guarded by deviceDescs value (=Device lock)
256 + private DeviceEvent updateDevice(ProviderId providerId,
257 + Device oldDevice,
258 + Device newDevice, Timestamp newTimestamp) {
259 +
260 + // We allow only certain attributes to trigger update
261 + if (!Objects.equals(oldDevice.hwVersion(), newDevice.hwVersion()) ||
262 + !Objects.equals(oldDevice.swVersion(), newDevice.swVersion()) ||
263 + !AnnotationsUtil.isEqual(oldDevice.annotations(), newDevice.annotations())) {
264 +
265 + boolean replaced = devices.replace(newDevice.id(), oldDevice, newDevice);
266 + if (!replaced) {
267 + verify(replaced,
268 + "Replacing devices cache failed. PID:%s [expected:%s, found:%s, new=%s]",
269 + providerId, oldDevice, devices.get(newDevice.id())
270 + , newDevice);
271 + }
272 + if (!providerId.isAncillary()) {
273 + markOnline(newDevice.id(), newTimestamp);
274 + }
275 + return new DeviceEvent(DeviceEvent.Type.DEVICE_UPDATED, newDevice, null);
276 + }
277 +
278 + // Otherwise merely attempt to change availability if primary provider
279 + if (!providerId.isAncillary()) {
280 + boolean added = markOnline(newDevice.id(), newTimestamp);
281 + return !added ? null :
282 + new DeviceEvent(DEVICE_AVAILABILITY_CHANGED, newDevice, null);
283 + }
284 + return null;
285 + }
286 +
287 + @Override
288 + public DeviceEvent markOffline(DeviceId deviceId) {
289 + Timestamp timestamp = clockService.getTimestamp(deviceId);
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;
302 + }
303 +
304 + private DeviceEvent markOfflineInternal(DeviceId deviceId, Timestamp timestamp) {
305 +
306 + Map<ProviderId, DeviceDescriptions> providerDescs
307 + = getDeviceDescriptions(deviceId);
308 +
309 + // locking device
310 + synchronized (providerDescs) {
311 +
312 + // accept off-line if given timestamp is newer than
313 + // the latest Timestamp from Primary provider
314 + DeviceDescriptions primDescs = getPrimaryDescriptions(providerDescs);
315 + Timestamp lastTimestamp = primDescs.getLatestTimestamp();
316 + if (timestamp.compareTo(lastTimestamp) <= 0) {
317 + // outdated event ignore
318 + return null;
319 + }
320 +
321 + offline.put(deviceId, timestamp);
322 +
323 + Device device = devices.get(deviceId);
324 + if (device == null) {
325 + return null;
326 + }
327 + boolean removed = availableDevices.remove(deviceId);
328 + if (removed) {
329 + // TODO: broadcast ... DOWN only?
330 + return new DeviceEvent(DEVICE_AVAILABILITY_CHANGED, device, null);
331 + }
332 + return null;
333 + }
334 + }
335 +
336 + /**
337 + * Marks the device as available if the given timestamp is not outdated,
338 + * compared to the time the device has been marked offline.
339 + *
340 + * @param deviceId identifier of the device
341 + * @param timestamp of the event triggering this change.
342 + * @return true if availability change request was accepted and changed the state
343 + */
344 + // Guarded by deviceDescs value (=Device lock)
345 + private boolean markOnline(DeviceId deviceId, Timestamp timestamp) {
346 + // accept on-line if given timestamp is newer than
347 + // the latest offline request Timestamp
348 + Timestamp offlineTimestamp = offline.get(deviceId);
349 + if (offlineTimestamp == null ||
350 + offlineTimestamp.compareTo(timestamp) < 0) {
351 +
352 + offline.remove(deviceId);
353 + return availableDevices.add(deviceId);
354 + }
355 + return false;
356 + }
357 +
358 + @Override
359 + public synchronized List<DeviceEvent> updatePorts(ProviderId providerId,
360 + DeviceId deviceId,
361 + List<PortDescription> portDescriptions) {
362 + Timestamp newTimestamp = clockService.getTimestamp(deviceId);
363 +
364 + Timestamped<List<PortDescription>> timestampedPortDescriptions =
365 + new Timestamped<>(portDescriptions, newTimestamp);
366 +
367 + List<DeviceEvent> events = updatePortsInternal(providerId, deviceId, timestampedPortDescriptions);
368 + if (!events.isEmpty()) {
369 + log.info("Notifying peers of a port update topology event for providerId: {} and deviceId: {}",
370 + providerId, deviceId);
371 + try {
372 + notifyPeers(new InternalPortEvent(providerId, deviceId, timestampedPortDescriptions));
373 + } catch (IOException e) {
374 + log.error("Failed to notify peers of a port update topology event or providerId: "
375 + + providerId + " and deviceId: " + deviceId, e);
376 + }
377 + }
378 + return events;
379 + }
380 +
381 + private List<DeviceEvent> updatePortsInternal(ProviderId providerId,
382 + DeviceId deviceId,
383 + Timestamped<List<PortDescription>> portDescriptions) {
384 +
385 + Device device = devices.get(deviceId);
386 + checkArgument(device != null, DEVICE_NOT_FOUND, deviceId);
387 +
388 + ConcurrentMap<ProviderId, DeviceDescriptions> descsMap = deviceDescs.get(deviceId);
389 + checkArgument(descsMap != null, DEVICE_NOT_FOUND, deviceId);
390 +
391 + List<DeviceEvent> events = new ArrayList<>();
392 + synchronized (descsMap) {
393 +
394 + if (isDeviceRemoved(deviceId, portDescriptions.timestamp())) {
395 + log.debug("Ignoring outdated events: {}", portDescriptions);
396 + return null;
397 + }
398 +
399 + DeviceDescriptions descs = descsMap.get(providerId);
400 + // every provider must provide DeviceDescription.
401 + checkArgument(descs != null,
402 + "Device description for Device ID %s from Provider %s was not found",
403 + deviceId, providerId);
404 +
405 + Map<PortNumber, Port> ports = getPortMap(deviceId);
406 +
407 + final Timestamp newTimestamp = portDescriptions.timestamp();
408 +
409 + // Add new ports
410 + Set<PortNumber> processed = new HashSet<>();
411 + for (PortDescription portDescription : portDescriptions.value()) {
412 + final PortNumber number = portDescription.portNumber();
413 + processed.add(number);
414 +
415 + final Port oldPort = ports.get(number);
416 + final Port newPort;
417 +
418 +
419 + final Timestamped<PortDescription> existingPortDesc = descs.getPortDesc(number);
420 + if (existingPortDesc == null ||
421 + newTimestamp.compareTo(existingPortDesc.timestamp()) >= 0) {
422 + // on new port or valid update
423 + // update description
424 + descs.putPortDesc(new Timestamped<>(portDescription,
425 + portDescriptions.timestamp()));
426 + newPort = composePort(device, number, descsMap);
427 + } else {
428 + // outdated event, ignored.
429 + continue;
430 + }
431 +
432 + events.add(oldPort == null ?
433 + createPort(device, newPort, ports) :
434 + updatePort(device, oldPort, newPort, ports));
435 + }
436 +
437 + events.addAll(pruneOldPorts(device, ports, processed));
438 + }
439 + return FluentIterable.from(events).filter(notNull()).toList();
440 + }
441 +
442 + // Creates a new port based on the port description adds it to the map and
443 + // Returns corresponding event.
444 + // Guarded by deviceDescs value (=Device lock)
445 + private DeviceEvent createPort(Device device, Port newPort,
446 + Map<PortNumber, Port> ports) {
447 + ports.put(newPort.number(), newPort);
448 + return new DeviceEvent(PORT_ADDED, device, newPort);
449 + }
450 +
451 + // Checks if the specified port requires update and if so, it replaces the
452 + // existing entry in the map and returns corresponding event.
453 + // Guarded by deviceDescs value (=Device lock)
454 + private DeviceEvent updatePort(Device device, Port oldPort,
455 + Port newPort,
456 + Map<PortNumber, Port> ports) {
457 + if (oldPort.isEnabled() != newPort.isEnabled() ||
458 + !AnnotationsUtil.isEqual(oldPort.annotations(), newPort.annotations())) {
459 +
460 + ports.put(oldPort.number(), newPort);
461 + return new DeviceEvent(PORT_UPDATED, device, newPort);
462 + }
463 + return null;
464 + }
465 +
466 + // Prunes the specified list of ports based on which ports are in the
467 + // processed list and returns list of corresponding events.
468 + // Guarded by deviceDescs value (=Device lock)
469 + private List<DeviceEvent> pruneOldPorts(Device device,
470 + Map<PortNumber, Port> ports,
471 + Set<PortNumber> processed) {
472 + List<DeviceEvent> events = new ArrayList<>();
473 + Iterator<PortNumber> iterator = ports.keySet().iterator();
474 + while (iterator.hasNext()) {
475 + PortNumber portNumber = iterator.next();
476 + if (!processed.contains(portNumber)) {
477 + events.add(new DeviceEvent(PORT_REMOVED, device,
478 + ports.get(portNumber)));
479 + iterator.remove();
480 + }
481 + }
482 + return events;
483 + }
484 +
485 + // Gets the map of ports for the specified device; if one does not already
486 + // exist, it creates and registers a new one.
487 + private ConcurrentMap<PortNumber, Port> getPortMap(DeviceId deviceId) {
488 + return createIfAbsentUnchecked(devicePorts, deviceId,
489 + NewConcurrentHashMap.<PortNumber, Port>ifNeeded());
490 + }
491 +
492 + private ConcurrentMap<ProviderId, DeviceDescriptions> getDeviceDescriptions(
493 + DeviceId deviceId) {
494 + return createIfAbsentUnchecked(deviceDescs, deviceId,
495 + NewConcurrentHashMap.<ProviderId, DeviceDescriptions>ifNeeded());
496 + }
497 +
498 + @Override
499 + public synchronized DeviceEvent updatePortStatus(ProviderId providerId, DeviceId deviceId,
500 + PortDescription portDescription) {
501 + Timestamp newTimestamp = clockService.getTimestamp(deviceId);
502 + final Timestamped<PortDescription> deltaDesc = new Timestamped<>(portDescription, newTimestamp);
503 + DeviceEvent event = updatePortStatusInternal(providerId, deviceId, deltaDesc);
504 + if (event != null) {
505 + log.info("Notifying peers of a port status update topology event for providerId: {} and deviceId: {}",
506 + providerId, deviceId);
507 + try {
508 + notifyPeers(new InternalPortStatusEvent(providerId, deviceId, deltaDesc));
509 + } catch (IOException e) {
510 + log.error("Failed to notify peers of a port status update topology event or providerId: "
511 + + providerId + " and deviceId: " + deviceId, e);
512 + }
513 + }
514 + return event;
515 + }
516 +
517 + private DeviceEvent updatePortStatusInternal(ProviderId providerId, DeviceId deviceId,
518 + Timestamped<PortDescription> deltaDesc) {
519 +
520 + Device device = devices.get(deviceId);
521 + checkArgument(device != null, DEVICE_NOT_FOUND, deviceId);
522 +
523 + ConcurrentMap<ProviderId, DeviceDescriptions> descsMap = deviceDescs.get(deviceId);
524 + checkArgument(descsMap != null, DEVICE_NOT_FOUND, deviceId);
525 +
526 + synchronized (descsMap) {
527 +
528 + if (isDeviceRemoved(deviceId, deltaDesc.timestamp())) {
529 + log.debug("Ignoring outdated event: {}", deltaDesc);
530 + return null;
531 + }
532 +
533 + DeviceDescriptions descs = descsMap.get(providerId);
534 + // assuming all providers must to give DeviceDescription
535 + checkArgument(descs != null,
536 + "Device description for Device ID %s from Provider %s was not found",
537 + deviceId, providerId);
538 +
539 + ConcurrentMap<PortNumber, Port> ports = getPortMap(deviceId);
540 + final PortNumber number = deltaDesc.value().portNumber();
541 + final Port oldPort = ports.get(number);
542 + final Port newPort;
543 +
544 + final Timestamped<PortDescription> existingPortDesc = descs.getPortDesc(number);
545 + if (existingPortDesc == null ||
546 + deltaDesc == existingPortDesc ||
547 + deltaDesc.isNewer(existingPortDesc)) {
548 + // on new port or valid update
549 + // update description
550 + descs.putPortDesc(deltaDesc);
551 + newPort = composePort(device, number, descsMap);
552 + } else {
553 + // outdated event, ignored.
554 + return null;
555 + }
556 +
557 + if (oldPort == null) {
558 + return createPort(device, newPort, ports);
559 + } else {
560 + return updatePort(device, oldPort, newPort, ports);
561 + }
562 + }
563 + }
564 +
565 + @Override
566 + public List<Port> getPorts(DeviceId deviceId) {
567 + Map<PortNumber, Port> ports = devicePorts.get(deviceId);
568 + if (ports == null) {
569 + return Collections.emptyList();
570 + }
571 + return ImmutableList.copyOf(ports.values());
572 + }
573 +
574 + @Override
575 + public Port getPort(DeviceId deviceId, PortNumber portNumber) {
576 + Map<PortNumber, Port> ports = devicePorts.get(deviceId);
577 + return ports == null ? null : ports.get(portNumber);
578 + }
579 +
580 + @Override
581 + public boolean isAvailable(DeviceId deviceId) {
582 + return availableDevices.contains(deviceId);
583 + }
584 +
585 + @Override
586 + public synchronized DeviceEvent removeDevice(DeviceId deviceId) {
587 + Timestamp timestamp = clockService.getTimestamp(deviceId);
588 + DeviceEvent event = removeDeviceInternal(deviceId, timestamp);
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 + }
599 + return event;
600 + }
601 +
602 + private DeviceEvent removeDeviceInternal(DeviceId deviceId,
603 + Timestamp timestamp) {
604 +
605 + Map<ProviderId, DeviceDescriptions> descs = getDeviceDescriptions(deviceId);
606 + synchronized (descs) {
607 + // accept removal request if given timestamp is newer than
608 + // the latest Timestamp from Primary provider
609 + DeviceDescriptions primDescs = getPrimaryDescriptions(descs);
610 + Timestamp lastTimestamp = primDescs.getLatestTimestamp();
611 + if (timestamp.compareTo(lastTimestamp) <= 0) {
612 + // outdated event ignore
613 + return null;
614 + }
615 + removalRequest.put(deviceId, timestamp);
616 +
617 + Device device = devices.remove(deviceId);
618 + // should DEVICE_REMOVED carry removed ports?
619 + Map<PortNumber, Port> ports = devicePorts.get(deviceId);
620 + if (ports != null) {
621 + ports.clear();
622 + }
623 + markOfflineInternal(deviceId, timestamp);
624 + descs.clear();
625 + return device == null ? null :
626 + new DeviceEvent(DEVICE_REMOVED, device, null);
627 + }
628 + }
629 +
630 + private boolean isDeviceRemoved(DeviceId deviceId, Timestamp timestampToCheck) {
631 + Timestamp removalTimestamp = removalRequest.get(deviceId);
632 + if (removalTimestamp != null &&
633 + removalTimestamp.compareTo(timestampToCheck) >= 0) {
634 + // removalRequest is more recent
635 + return true;
636 + }
637 + return false;
638 + }
639 +
640 + /**
641 + * Returns a Device, merging description given from multiple Providers.
642 + *
643 + * @param deviceId device identifier
644 + * @param providerDescs Collection of Descriptions from multiple providers
645 + * @return Device instance
646 + */
647 + private Device composeDevice(DeviceId deviceId,
648 + ConcurrentMap<ProviderId, DeviceDescriptions> providerDescs) {
649 +
650 + checkArgument(!providerDescs.isEmpty(), "No Device descriptions supplied");
651 +
652 + ProviderId primary = pickPrimaryPID(providerDescs);
653 +
654 + DeviceDescriptions desc = providerDescs.get(primary);
655 +
656 + final DeviceDescription base = desc.getDeviceDesc().value();
657 + Type type = base.type();
658 + String manufacturer = base.manufacturer();
659 + String hwVersion = base.hwVersion();
660 + String swVersion = base.swVersion();
661 + String serialNumber = base.serialNumber();
662 + DefaultAnnotations annotations = DefaultAnnotations.builder().build();
663 + annotations = merge(annotations, base.annotations());
664 +
665 + for (Entry<ProviderId, DeviceDescriptions> e : providerDescs.entrySet()) {
666 + if (e.getKey().equals(primary)) {
667 + continue;
668 + }
669 + // TODO: should keep track of Description timestamp
670 + // and only merge conflicting keys when timestamp is newer
671 + // Currently assuming there will never be a key conflict between
672 + // providers
673 +
674 + // annotation merging. not so efficient, should revisit later
675 + annotations = merge(annotations, e.getValue().getDeviceDesc().value().annotations());
676 + }
677 +
678 + return new DefaultDevice(primary, deviceId , type, manufacturer,
679 + hwVersion, swVersion, serialNumber, annotations);
680 + }
681 +
682 + /**
683 + * Returns a Port, merging description given from multiple Providers.
684 + *
685 + * @param device device the port is on
686 + * @param number port number
687 + * @param providerDescs Collection of Descriptions from multiple providers
688 + * @return Port instance
689 + */
690 + private Port composePort(Device device, PortNumber number,
691 + ConcurrentMap<ProviderId, DeviceDescriptions> providerDescs) {
692 +
693 + ProviderId primary = pickPrimaryPID(providerDescs);
694 + DeviceDescriptions primDescs = providerDescs.get(primary);
695 + // if no primary, assume not enabled
696 + // TODO: revisit this default port enabled/disabled behavior
697 + boolean isEnabled = false;
698 + DefaultAnnotations annotations = DefaultAnnotations.builder().build();
699 +
700 + final Timestamped<PortDescription> portDesc = primDescs.getPortDesc(number);
701 + if (portDesc != null) {
702 + isEnabled = portDesc.value().isEnabled();
703 + annotations = merge(annotations, portDesc.value().annotations());
704 + }
705 +
706 + for (Entry<ProviderId, DeviceDescriptions> e : providerDescs.entrySet()) {
707 + if (e.getKey().equals(primary)) {
708 + continue;
709 + }
710 + // TODO: should keep track of Description timestamp
711 + // and only merge conflicting keys when timestamp is newer
712 + // Currently assuming there will never be a key conflict between
713 + // providers
714 +
715 + // annotation merging. not so efficient, should revisit later
716 + final Timestamped<PortDescription> otherPortDesc = e.getValue().getPortDesc(number);
717 + if (otherPortDesc != null) {
718 + annotations = merge(annotations, otherPortDesc.value().annotations());
719 + }
720 + }
721 +
722 + return new DefaultPort(device, number, isEnabled, annotations);
723 + }
724 +
725 + /**
726 + * @return primary ProviderID, or randomly chosen one if none exists
727 + */
728 + private ProviderId pickPrimaryPID(
729 + Map<ProviderId, DeviceDescriptions> providerDescs) {
730 + ProviderId fallBackPrimary = null;
731 + for (Entry<ProviderId, DeviceDescriptions> e : providerDescs.entrySet()) {
732 + if (!e.getKey().isAncillary()) {
733 + return e.getKey();
734 + } else if (fallBackPrimary == null) {
735 + // pick randomly as a fallback in case there is no primary
736 + fallBackPrimary = e.getKey();
737 + }
738 + }
739 + return fallBackPrimary;
740 + }
741 +
742 + private DeviceDescriptions getPrimaryDescriptions(
743 + Map<ProviderId, DeviceDescriptions> providerDescs) {
744 + ProviderId pid = pickPrimaryPID(providerDescs);
745 + return providerDescs.get(pid);
746 + }
747 +
748 + public static final class InitDeviceDescs
749 + implements ConcurrentInitializer<DeviceDescriptions> {
750 +
751 + private final Timestamped<DeviceDescription> deviceDesc;
752 +
753 + public InitDeviceDescs(Timestamped<DeviceDescription> deviceDesc) {
754 + this.deviceDesc = checkNotNull(deviceDesc);
755 + }
756 + @Override
757 + public DeviceDescriptions get() throws ConcurrentException {
758 + return new DeviceDescriptions(deviceDesc);
759 + }
760 + }
761 +
762 +
763 + /**
764 + * Collection of Description of a Device and it's Ports given from a Provider.
765 + */
766 + public static class DeviceDescriptions {
767 +
768 + private final AtomicReference<Timestamped<DeviceDescription>> deviceDesc;
769 + private final ConcurrentMap<PortNumber, Timestamped<PortDescription>> portDescs;
770 +
771 + public DeviceDescriptions(Timestamped<DeviceDescription> desc) {
772 + this.deviceDesc = new AtomicReference<>(checkNotNull(desc));
773 + this.portDescs = new ConcurrentHashMap<>();
774 + }
775 +
776 + Timestamp getLatestTimestamp() {
777 + Timestamp latest = deviceDesc.get().timestamp();
778 + for (Timestamped<PortDescription> desc : portDescs.values()) {
779 + if (desc.timestamp().compareTo(latest) > 0) {
780 + latest = desc.timestamp();
781 + }
782 + }
783 + return latest;
784 + }
785 +
786 + public Timestamped<DeviceDescription> getDeviceDesc() {
787 + return deviceDesc.get();
788 + }
789 +
790 + public Timestamped<PortDescription> getPortDesc(PortNumber number) {
791 + return portDescs.get(number);
792 + }
793 +
794 + /**
795 + * Puts DeviceDescription, merging annotations as necessary.
796 + *
797 + * @param newDesc new DeviceDescription
798 + * @return previous DeviceDescription
799 + */
800 + public synchronized Timestamped<DeviceDescription> putDeviceDesc(Timestamped<DeviceDescription> newDesc) {
801 + Timestamped<DeviceDescription> oldOne = deviceDesc.get();
802 + Timestamped<DeviceDescription> newOne = newDesc;
803 + if (oldOne != null) {
804 + SparseAnnotations merged = union(oldOne.value().annotations(),
805 + newDesc.value().annotations());
806 + newOne = new Timestamped<DeviceDescription>(
807 + new DefaultDeviceDescription(newDesc.value(), merged),
808 + newDesc.timestamp());
809 + }
810 + return deviceDesc.getAndSet(newOne);
811 + }
812 +
813 + /**
814 + * Puts PortDescription, merging annotations as necessary.
815 + *
816 + * @param newDesc new PortDescription
817 + * @return previous PortDescription
818 + */
819 + public synchronized Timestamped<PortDescription> putPortDesc(Timestamped<PortDescription> newDesc) {
820 + Timestamped<PortDescription> oldOne = portDescs.get(newDesc.value().portNumber());
821 + Timestamped<PortDescription> newOne = newDesc;
822 + if (oldOne != null) {
823 + SparseAnnotations merged = union(oldOne.value().annotations(),
824 + newDesc.value().annotations());
825 + newOne = new Timestamped<PortDescription>(
826 + new DefaultPortDescription(newDesc.value(), merged),
827 + newDesc.timestamp());
828 + }
829 + return portDescs.put(newOne.value().portNumber(), newOne);
830 + }
831 + }
832 +
833 + private void notifyPeers(InternalDeviceEvent event) throws IOException {
834 + ClusterMessage message = new ClusterMessage(
835 + clusterService.getLocalNode().id(),
836 + GossipDeviceStoreMessageSubjects.DEVICE_UPDATE,
837 + SERIALIZER.encode(event));
838 + clusterCommunicator.broadcast(message);
839 + }
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 +
857 + private void notifyPeers(InternalPortEvent event) throws IOException {
858 + ClusterMessage message = new ClusterMessage(
859 + clusterService.getLocalNode().id(),
860 + GossipDeviceStoreMessageSubjects.PORT_UPDATE,
861 + SERIALIZER.encode(event));
862 + clusterCommunicator.broadcast(message);
863 + }
864 +
865 + private void notifyPeers(InternalPortStatusEvent event) throws IOException {
866 + ClusterMessage message = new ClusterMessage(
867 + clusterService.getLocalNode().id(),
868 + GossipDeviceStoreMessageSubjects.PORT_STATUS_UPDATE,
869 + SERIALIZER.encode(event));
870 + clusterCommunicator.broadcast(message);
871 + }
872 +
873 + private class InternalDeviceEventListener implements ClusterMessageHandler {
874 + @Override
875 + public void handle(ClusterMessage message) {
876 +
877 + log.info("Received device update event from peer: {}", message.sender());
878 + InternalDeviceEvent event = (InternalDeviceEvent) SERIALIZER.decode(message.payload());
879 +
880 + ProviderId providerId = event.providerId();
881 + DeviceId deviceId = event.deviceId();
882 + Timestamped<DeviceDescription> deviceDescription = event.deviceDescription();
883 +
884 + createOrUpdateDeviceInternal(providerId, deviceId, deviceDescription);
885 + }
886 + }
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 +
916 + private class InternalPortEventListener implements ClusterMessageHandler {
917 + @Override
918 + public void handle(ClusterMessage message) {
919 +
920 + log.info("Received port update event from peer: {}", message.sender());
921 + InternalPortEvent event = (InternalPortEvent) SERIALIZER.decode(message.payload());
922 +
923 + ProviderId providerId = event.providerId();
924 + DeviceId deviceId = event.deviceId();
925 + Timestamped<List<PortDescription>> portDescriptions = event.portDescriptions();
926 +
927 + updatePortsInternal(providerId, deviceId, portDescriptions);
928 + }
929 + }
930 +
931 + private class InternalPortStatusEventListener implements ClusterMessageHandler {
932 + @Override
933 + public void handle(ClusterMessage message) {
934 +
935 + log.info("Received port status update event from peer: {}", message.sender());
936 + InternalPortStatusEvent event = (InternalPortStatusEvent) SERIALIZER.decode(message.payload());
937 +
938 + ProviderId providerId = event.providerId();
939 + DeviceId deviceId = event.deviceId();
940 + Timestamped<PortDescription> portDescription = event.portDescription();
941 +
942 + updatePortStatusInternal(providerId, deviceId, portDescription);
943 + }
944 + }
945 +}
1 +package org.onlab.onos.store.device.impl;
2 +
3 +import org.onlab.onos.store.cluster.messaging.MessageSubject;
4 +
5 +/**
6 + * MessageSubjects used by GossipDeviceStore peer-peer communication.
7 + */
8 +public final class GossipDeviceStoreMessageSubjects {
9 +
10 + private GossipDeviceStoreMessageSubjects() {}
11 +
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");
15 + public static final MessageSubject PORT_UPDATE = new MessageSubject("peer-port-update");
16 + public static final MessageSubject PORT_STATUS_UPDATE = new MessageSubject("peer-port-status-update");
17 +}
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 +/**
9 + * Information published by GossipDeviceStore to notify peers of a device
10 + * change event.
11 + */
12 +public class InternalDeviceEvent {
13 +
14 + private final ProviderId providerId;
15 + private final DeviceId deviceId;
16 + private final Timestamped<DeviceDescription> deviceDescription;
17 +
18 + protected InternalDeviceEvent(
19 + ProviderId providerId,
20 + DeviceId deviceId,
21 + Timestamped<DeviceDescription> deviceDescription) {
22 + this.providerId = providerId;
23 + this.deviceId = deviceId;
24 + this.deviceDescription = deviceDescription;
25 + }
26 +
27 + public DeviceId deviceId() {
28 + return deviceId;
29 + }
30 +
31 + public ProviderId providerId() {
32 + return providerId;
33 + }
34 +
35 + public Timestamped<DeviceDescription> deviceDescription() {
36 + return deviceDescription;
37 + }
38 +
39 + // for serializer
40 + protected InternalDeviceEvent() {
41 + this.providerId = null;
42 + this.deviceId = null;
43 + this.deviceDescription = null;
44 + }
45 +}
1 +package org.onlab.onos.store.device.impl;
2 +
3 +import org.onlab.onos.net.DeviceId;
4 +import org.onlab.onos.net.device.DeviceDescription;
5 +import org.onlab.onos.net.provider.ProviderId;
6 +import org.onlab.onos.store.common.impl.Timestamped;
7 +
8 +import com.esotericsoftware.kryo.Kryo;
9 +import com.esotericsoftware.kryo.Serializer;
10 +import com.esotericsoftware.kryo.io.Input;
11 +import com.esotericsoftware.kryo.io.Output;
12 +
13 +/**
14 + * Kryo Serializer for {@link InternalDeviceEvent}.
15 + */
16 +public class InternalDeviceEventSerializer extends Serializer<InternalDeviceEvent> {
17 +
18 + /**
19 + * Creates a serializer for {@link InternalDeviceEvent}.
20 + */
21 + public InternalDeviceEventSerializer() {
22 + // does not accept null
23 + super(false);
24 + }
25 +
26 + @Override
27 + public void write(Kryo kryo, Output output, InternalDeviceEvent event) {
28 + kryo.writeClassAndObject(output, event.providerId());
29 + kryo.writeClassAndObject(output, event.deviceId());
30 + kryo.writeClassAndObject(output, event.deviceDescription());
31 + }
32 +
33 + @Override
34 + public InternalDeviceEvent read(Kryo kryo, Input input,
35 + Class<InternalDeviceEvent> type) {
36 + ProviderId providerId = (ProviderId) kryo.readClassAndObject(input);
37 + DeviceId deviceId = (DeviceId) kryo.readClassAndObject(input);
38 + Timestamped<DeviceDescription> deviceDescription
39 + = (Timestamped<DeviceDescription>) kryo.readClassAndObject(input);
40 +
41 + return new InternalDeviceEvent(providerId, deviceId, deviceDescription);
42 + }
43 +}
1 +package org.onlab.onos.store.device.impl;
2 +
3 +import org.onlab.onos.net.DeviceId;
4 +import org.onlab.onos.store.Timestamp;
5 +
6 +/**
7 + * Information published by GossipDeviceStore to notify peers of a device
8 + * going offline.
9 + */
10 +public class InternalDeviceOfflineEvent {
11 +
12 + private final DeviceId deviceId;
13 + private final Timestamp timestamp;
14 +
15 + /**
16 + * Creates a InternalDeviceOfflineEvent.
17 + * @param deviceId identifier of device going offline.
18 + * @param timestamp timestamp of when the device went offline.
19 + */
20 + public InternalDeviceOfflineEvent(DeviceId deviceId, Timestamp timestamp) {
21 + this.deviceId = deviceId;
22 + this.timestamp = timestamp;
23 + }
24 +
25 + public DeviceId deviceId() {
26 + return deviceId;
27 + }
28 +
29 + public Timestamp timestamp() {
30 + return timestamp;
31 + }
32 +
33 + // for serializer
34 + @SuppressWarnings("unused")
35 + private InternalDeviceOfflineEvent() {
36 + deviceId = null;
37 + timestamp = null;
38 + }
39 +}
1 +package org.onlab.onos.store.device.impl;
2 +
3 +import org.onlab.onos.net.DeviceId;
4 +import org.onlab.onos.store.Timestamp;
5 +
6 +import com.esotericsoftware.kryo.Kryo;
7 +import com.esotericsoftware.kryo.Serializer;
8 +import com.esotericsoftware.kryo.io.Input;
9 +import com.esotericsoftware.kryo.io.Output;
10 +
11 +/**
12 + * Kryo Serializer for {@link InternalDeviceOfflineEvent}.
13 + */
14 +public class InternalDeviceOfflineEventSerializer extends Serializer<InternalDeviceOfflineEvent> {
15 +
16 + /**
17 + * Creates a serializer for {@link InternalDeviceOfflineEvent}.
18 + */
19 + public InternalDeviceOfflineEventSerializer() {
20 + // does not accept null
21 + super(false);
22 + }
23 +
24 + @Override
25 + public void write(Kryo kryo, Output output, InternalDeviceOfflineEvent event) {
26 + kryo.writeClassAndObject(output, event.deviceId());
27 + kryo.writeClassAndObject(output, event.timestamp());
28 + }
29 +
30 + @Override
31 + public InternalDeviceOfflineEvent read(Kryo kryo, Input input,
32 + Class<InternalDeviceOfflineEvent> type) {
33 + DeviceId deviceId = (DeviceId) kryo.readClassAndObject(input);
34 + Timestamp timestamp = (Timestamp) kryo.readClassAndObject(input);
35 +
36 + return new InternalDeviceOfflineEvent(deviceId, timestamp);
37 + }
38 +}
1 +package org.onlab.onos.store.device.impl;
2 +
3 +import org.onlab.onos.net.DeviceId;
4 +import org.onlab.onos.store.Timestamp;
5 +
6 +/**
7 + * Information published by GossipDeviceStore to notify peers of a device
8 + * being administratively removed.
9 + */
10 +public class InternalDeviceRemovedEvent {
11 +
12 + private final DeviceId deviceId;
13 + private final Timestamp timestamp;
14 +
15 + /**
16 + * Creates a InternalDeviceRemovedEvent.
17 + * @param deviceId identifier of the removed device.
18 + * @param timestamp timestamp of when the device was administratively removed.
19 + */
20 + public InternalDeviceRemovedEvent(DeviceId deviceId, Timestamp timestamp) {
21 + this.deviceId = deviceId;
22 + this.timestamp = timestamp;
23 + }
24 +
25 + public DeviceId deviceId() {
26 + return deviceId;
27 + }
28 +
29 + public Timestamp timestamp() {
30 + return timestamp;
31 + }
32 +
33 + // for serializer
34 + @SuppressWarnings("unused")
35 + private InternalDeviceRemovedEvent() {
36 + deviceId = null;
37 + timestamp = null;
38 + }
39 +}
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 +/**
11 + * Information published by GossipDeviceStore to notify peers of a port
12 + * change event.
13 + */
14 +public class InternalPortEvent {
15 +
16 + private final ProviderId providerId;
17 + private final DeviceId deviceId;
18 + private final Timestamped<List<PortDescription>> portDescriptions;
19 +
20 + protected InternalPortEvent(
21 + ProviderId providerId,
22 + DeviceId deviceId,
23 + Timestamped<List<PortDescription>> portDescriptions) {
24 + this.providerId = providerId;
25 + this.deviceId = deviceId;
26 + this.portDescriptions = portDescriptions;
27 + }
28 +
29 + public DeviceId deviceId() {
30 + return deviceId;
31 + }
32 +
33 + public ProviderId providerId() {
34 + return providerId;
35 + }
36 +
37 + public Timestamped<List<PortDescription>> portDescriptions() {
38 + return portDescriptions;
39 + }
40 +
41 + // for serializer
42 + protected InternalPortEvent() {
43 + this.providerId = null;
44 + this.deviceId = null;
45 + this.portDescriptions = null;
46 + }
47 +}
1 +package org.onlab.onos.store.device.impl;
2 +
3 +import java.util.List;
4 +
5 +import org.onlab.onos.net.DeviceId;
6 +import org.onlab.onos.net.device.PortDescription;
7 +import org.onlab.onos.net.provider.ProviderId;
8 +import org.onlab.onos.store.common.impl.Timestamped;
9 +
10 +import com.esotericsoftware.kryo.Kryo;
11 +import com.esotericsoftware.kryo.Serializer;
12 +import com.esotericsoftware.kryo.io.Input;
13 +import com.esotericsoftware.kryo.io.Output;
14 +
15 +/**
16 + * Kryo Serializer for {@link InternalPortEvent}.
17 + */
18 +public class InternalPortEventSerializer extends Serializer<InternalPortEvent> {
19 +
20 + /**
21 + * Creates a serializer for {@link InternalPortEvent}.
22 + */
23 + public InternalPortEventSerializer() {
24 + // does not accept null
25 + super(false);
26 + }
27 +
28 + @Override
29 + public void write(Kryo kryo, Output output, InternalPortEvent event) {
30 + kryo.writeClassAndObject(output, event.providerId());
31 + kryo.writeClassAndObject(output, event.deviceId());
32 + kryo.writeClassAndObject(output, event.portDescriptions());
33 + }
34 +
35 + @Override
36 + public InternalPortEvent read(Kryo kryo, Input input,
37 + Class<InternalPortEvent> type) {
38 + ProviderId providerId = (ProviderId) kryo.readClassAndObject(input);
39 + DeviceId deviceId = (DeviceId) kryo.readClassAndObject(input);
40 + Timestamped<List<PortDescription>> portDescriptions
41 + = (Timestamped<List<PortDescription>>) kryo.readClassAndObject(input);
42 +
43 + return new InternalPortEvent(providerId, deviceId, portDescriptions);
44 + }
45 +}
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 +/**
9 + * Information published by GossipDeviceStore to notify peers of a port
10 + * status change event.
11 + */
12 +public class InternalPortStatusEvent {
13 +
14 + private final ProviderId providerId;
15 + private final DeviceId deviceId;
16 + private final Timestamped<PortDescription> portDescription;
17 +
18 + protected InternalPortStatusEvent(
19 + ProviderId providerId,
20 + DeviceId deviceId,
21 + Timestamped<PortDescription> portDescription) {
22 + this.providerId = providerId;
23 + this.deviceId = deviceId;
24 + this.portDescription = portDescription;
25 + }
26 +
27 + public DeviceId deviceId() {
28 + return deviceId;
29 + }
30 +
31 + public ProviderId providerId() {
32 + return providerId;
33 + }
34 +
35 + public Timestamped<PortDescription> portDescription() {
36 + return portDescription;
37 + }
38 +
39 + // for serializer
40 + protected InternalPortStatusEvent() {
41 + this.providerId = null;
42 + this.deviceId = null;
43 + this.portDescription = null;
44 + }
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 +}
1 -package org.onlab.onos.store.device.impl;
2 -
3 -import static com.google.common.base.Predicates.notNull;
4 -import static com.google.common.base.Preconditions.checkState;
5 -
6 -import com.google.common.collect.FluentIterable;
7 -import com.google.common.collect.ImmutableSet;
8 -import com.google.common.collect.ImmutableSet.Builder;
9 -
10 -import org.apache.felix.scr.annotations.Activate;
11 -import org.apache.felix.scr.annotations.Component;
12 -import org.apache.felix.scr.annotations.Deactivate;
13 -import org.apache.felix.scr.annotations.Reference;
14 -import org.apache.felix.scr.annotations.ReferenceCardinality;
15 -import org.apache.felix.scr.annotations.Service;
16 -import org.onlab.onos.net.DefaultDevice;
17 -import org.onlab.onos.net.DefaultPort;
18 -import org.onlab.onos.net.Device;
19 -import org.onlab.onos.net.DeviceId;
20 -import org.onlab.onos.net.Port;
21 -import org.onlab.onos.net.PortNumber;
22 -import org.onlab.onos.net.device.DeviceDescription;
23 -import org.onlab.onos.net.device.DeviceEvent;
24 -import org.onlab.onos.net.device.DeviceStore;
25 -import org.onlab.onos.net.device.DeviceStoreDelegate;
26 -import org.onlab.onos.net.device.PortDescription;
27 -import org.onlab.onos.net.provider.ProviderId;
28 -import org.onlab.onos.store.AbstractStore;
29 -import org.onlab.onos.store.ClockService;
30 -import org.onlab.onos.store.Timestamp;
31 -import org.slf4j.Logger;
32 -
33 -import java.util.ArrayList;
34 -import java.util.Collections;
35 -import java.util.HashMap;
36 -import java.util.HashSet;
37 -import java.util.Iterator;
38 -import java.util.List;
39 -import java.util.Map;
40 -import java.util.Objects;
41 -import java.util.Set;
42 -import java.util.concurrent.ConcurrentHashMap;
43 -import java.util.concurrent.ConcurrentMap;
44 -
45 -import static com.google.common.base.Preconditions.checkArgument;
46 -import static org.onlab.onos.net.device.DeviceEvent.Type.*;
47 -import static org.slf4j.LoggerFactory.getLogger;
48 -
49 -/**
50 - * Manages inventory of infrastructure devices using a protocol that takes into consideration
51 - * the order in which device events occur.
52 - */
53 -@Component(immediate = true)
54 -@Service
55 -public class OnosDistributedDeviceStore
56 - extends AbstractStore<DeviceEvent, DeviceStoreDelegate>
57 - implements DeviceStore {
58 -
59 - private final Logger log = getLogger(getClass());
60 -
61 - public static final String DEVICE_NOT_FOUND = "Device with ID %s not found";
62 -
63 - private ConcurrentMap<DeviceId, VersionedValue<Device>> devices;
64 - private ConcurrentMap<DeviceId, Map<PortNumber, VersionedValue<Port>>> devicePorts;
65 -
66 - @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
67 - protected ClockService clockService;
68 -
69 - @Activate
70 - public void activate() {
71 -
72 - devices = new ConcurrentHashMap<>();
73 - devicePorts = new ConcurrentHashMap<>();
74 -
75 - log.info("Started");
76 - }
77 -
78 - @Deactivate
79 - public void deactivate() {
80 - log.info("Stopped");
81 - }
82 -
83 - @Override
84 - public int getDeviceCount() {
85 - return devices.size();
86 - }
87 -
88 - @Override
89 - public Iterable<Device> getDevices() {
90 - Builder<Device> builder = ImmutableSet.builder();
91 - synchronized (this) {
92 - for (VersionedValue<Device> device : devices.values()) {
93 - builder.add(device.entity());
94 - }
95 - return builder.build();
96 - }
97 - }
98 -
99 - @Override
100 - public Device getDevice(DeviceId deviceId) {
101 - VersionedValue<Device> device = devices.get(deviceId);
102 - checkArgument(device != null, DEVICE_NOT_FOUND, deviceId);
103 - return device.entity();
104 - }
105 -
106 - @Override
107 - public DeviceEvent createOrUpdateDevice(ProviderId providerId, DeviceId deviceId,
108 - DeviceDescription deviceDescription) {
109 - Timestamp newTimestamp = clockService.getTimestamp(deviceId);
110 - VersionedValue<Device> device = devices.get(deviceId);
111 -
112 - if (device == null) {
113 - return createDevice(providerId, deviceId, deviceDescription, newTimestamp);
114 - }
115 -
116 - checkState(newTimestamp.compareTo(device.timestamp()) > 0,
117 - "Existing device has a timestamp in the future!");
118 -
119 - return updateDevice(providerId, device.entity(), deviceDescription, newTimestamp);
120 - }
121 -
122 - // Creates the device and returns the appropriate event if necessary.
123 - private DeviceEvent createDevice(ProviderId providerId, DeviceId deviceId,
124 - DeviceDescription desc, Timestamp timestamp) {
125 - Device device = new DefaultDevice(providerId, deviceId, desc.type(),
126 - desc.manufacturer(),
127 - desc.hwVersion(), desc.swVersion(),
128 - desc.serialNumber());
129 -
130 - devices.put(deviceId, new VersionedValue<>(device, true, timestamp));
131 - // TODO,FIXME: broadcast a message telling peers of a device event.
132 - return new DeviceEvent(DEVICE_ADDED, device, null);
133 - }
134 -
135 - // Updates the device and returns the appropriate event if necessary.
136 - private DeviceEvent updateDevice(ProviderId providerId, Device device,
137 - DeviceDescription desc, Timestamp timestamp) {
138 - // We allow only certain attributes to trigger update
139 - if (!Objects.equals(device.hwVersion(), desc.hwVersion()) ||
140 - !Objects.equals(device.swVersion(), desc.swVersion())) {
141 -
142 - Device updated = new DefaultDevice(providerId, device.id(),
143 - desc.type(),
144 - desc.manufacturer(),
145 - desc.hwVersion(),
146 - desc.swVersion(),
147 - desc.serialNumber());
148 - devices.put(device.id(), new VersionedValue<Device>(updated, true, timestamp));
149 - // FIXME: broadcast a message telling peers of a device event.
150 - return new DeviceEvent(DeviceEvent.Type.DEVICE_UPDATED, updated, null);
151 - }
152 -
153 - // Otherwise merely attempt to change availability
154 - Device updated = new DefaultDevice(providerId, device.id(),
155 - desc.type(),
156 - desc.manufacturer(),
157 - desc.hwVersion(),
158 - desc.swVersion(),
159 - desc.serialNumber());
160 -
161 - VersionedValue<Device> oldDevice = devices.put(device.id(),
162 - new VersionedValue<Device>(updated, true, timestamp));
163 - if (!oldDevice.isUp()) {
164 - return new DeviceEvent(DEVICE_AVAILABILITY_CHANGED, device, null);
165 - } else {
166 - return null;
167 - }
168 - }
169 -
170 - @Override
171 - public DeviceEvent markOffline(DeviceId deviceId) {
172 - VersionedValue<Device> device = devices.get(deviceId);
173 - boolean willRemove = device != null && device.isUp();
174 - if (!willRemove) {
175 - return null;
176 - }
177 - Timestamp timestamp = clockService.getTimestamp(deviceId);
178 - if (replaceIfLatest(device.entity(), false, timestamp)) {
179 - return new DeviceEvent(DEVICE_AVAILABILITY_CHANGED, device.entity(), null);
180 - }
181 - return null;
182 - }
183 -
184 - // Replace existing value if its timestamp is older.
185 - private synchronized boolean replaceIfLatest(Device device, boolean isUp, Timestamp timestamp) {
186 - VersionedValue<Device> existingValue = devices.get(device.id());
187 - if (timestamp.compareTo(existingValue.timestamp()) > 0) {
188 - devices.put(device.id(), new VersionedValue<Device>(device, isUp, timestamp));
189 - return true;
190 - }
191 - return false;
192 - }
193 -
194 - @Override
195 - public List<DeviceEvent> updatePorts(ProviderId providerId, DeviceId deviceId,
196 - List<PortDescription> portDescriptions) {
197 - List<DeviceEvent> events = new ArrayList<>();
198 - synchronized (this) {
199 - VersionedValue<Device> device = devices.get(deviceId);
200 - checkArgument(device != null, DEVICE_NOT_FOUND, deviceId);
201 - Map<PortNumber, VersionedValue<Port>> ports = getPortMap(deviceId);
202 - Timestamp newTimestamp = clockService.getTimestamp(deviceId);
203 -
204 - // Add new ports
205 - Set<PortNumber> processed = new HashSet<>();
206 - for (PortDescription portDescription : portDescriptions) {
207 - VersionedValue<Port> port = ports.get(portDescription.portNumber());
208 - if (port == null) {
209 - events.add(createPort(device, portDescription, ports, newTimestamp));
210 - }
211 - checkState(newTimestamp.compareTo(port.timestamp()) > 0,
212 - "Existing port state has a timestamp in the future!");
213 - events.add(updatePort(device.entity(), port.entity(), portDescription, ports, newTimestamp));
214 - processed.add(portDescription.portNumber());
215 - }
216 -
217 - updatePortMap(deviceId, ports);
218 -
219 - events.addAll(pruneOldPorts(device.entity(), ports, processed));
220 - }
221 - return FluentIterable.from(events).filter(notNull()).toList();
222 - }
223 -
224 - // Creates a new port based on the port description adds it to the map and
225 - // Returns corresponding event.
226 - //@GuardedBy("this")
227 - private DeviceEvent createPort(VersionedValue<Device> device, PortDescription portDescription,
228 - Map<PortNumber, VersionedValue<Port>> ports, Timestamp timestamp) {
229 - Port port = new DefaultPort(device.entity(), portDescription.portNumber(),
230 - portDescription.isEnabled());
231 - ports.put(port.number(), new VersionedValue<Port>(port, true, timestamp));
232 - updatePortMap(device.entity().id(), ports);
233 - return new DeviceEvent(PORT_ADDED, device.entity(), port);
234 - }
235 -
236 - // Checks if the specified port requires update and if so, it replaces the
237 - // existing entry in the map and returns corresponding event.
238 - //@GuardedBy("this")
239 - private DeviceEvent updatePort(Device device, Port port,
240 - PortDescription portDescription,
241 - Map<PortNumber, VersionedValue<Port>> ports,
242 - Timestamp timestamp) {
243 - if (port.isEnabled() != portDescription.isEnabled()) {
244 - VersionedValue<Port> updatedPort = new VersionedValue<Port>(
245 - new DefaultPort(device, portDescription.portNumber(),
246 - portDescription.isEnabled()),
247 - portDescription.isEnabled(),
248 - timestamp);
249 - ports.put(port.number(), updatedPort);
250 - updatePortMap(device.id(), ports);
251 - return new DeviceEvent(PORT_UPDATED, device, updatedPort.entity());
252 - }
253 - return null;
254 - }
255 -
256 - // Prunes the specified list of ports based on which ports are in the
257 - // processed list and returns list of corresponding events.
258 - //@GuardedBy("this")
259 - private List<DeviceEvent> pruneOldPorts(Device device,
260 - Map<PortNumber, VersionedValue<Port>> ports,
261 - Set<PortNumber> processed) {
262 - List<DeviceEvent> events = new ArrayList<>();
263 - Iterator<PortNumber> iterator = ports.keySet().iterator();
264 - while (iterator.hasNext()) {
265 - PortNumber portNumber = iterator.next();
266 - if (!processed.contains(portNumber)) {
267 - events.add(new DeviceEvent(PORT_REMOVED, device,
268 - ports.get(portNumber).entity()));
269 - iterator.remove();
270 - }
271 - }
272 - if (!events.isEmpty()) {
273 - updatePortMap(device.id(), ports);
274 - }
275 - return events;
276 - }
277 -
278 - // Gets the map of ports for the specified device; if one does not already
279 - // exist, it creates and registers a new one.
280 - // WARN: returned value is a copy, changes made to the Map
281 - // needs to be written back using updatePortMap
282 - //@GuardedBy("this")
283 - private Map<PortNumber, VersionedValue<Port>> getPortMap(DeviceId deviceId) {
284 - Map<PortNumber, VersionedValue<Port>> ports = devicePorts.get(deviceId);
285 - if (ports == null) {
286 - ports = new HashMap<>();
287 - // this probably is waste of time in most cases.
288 - updatePortMap(deviceId, ports);
289 - }
290 - return ports;
291 - }
292 -
293 - //@GuardedBy("this")
294 - private void updatePortMap(DeviceId deviceId, Map<PortNumber, VersionedValue<Port>> ports) {
295 - devicePorts.put(deviceId, ports);
296 - }
297 -
298 - @Override
299 - public DeviceEvent updatePortStatus(ProviderId providerId, DeviceId deviceId,
300 - PortDescription portDescription) {
301 - VersionedValue<Device> device = devices.get(deviceId);
302 - checkArgument(device != null, DEVICE_NOT_FOUND, deviceId);
303 - Map<PortNumber, VersionedValue<Port>> ports = getPortMap(deviceId);
304 - VersionedValue<Port> port = ports.get(portDescription.portNumber());
305 - Timestamp timestamp = clockService.getTimestamp(deviceId);
306 - return updatePort(device.entity(), port.entity(), portDescription, ports, timestamp);
307 - }
308 -
309 - @Override
310 - public List<Port> getPorts(DeviceId deviceId) {
311 - Map<PortNumber, VersionedValue<Port>> versionedPorts = devicePorts.get(deviceId);
312 - if (versionedPorts == null) {
313 - return Collections.emptyList();
314 - }
315 - List<Port> ports = new ArrayList<>();
316 - for (VersionedValue<Port> port : versionedPorts.values()) {
317 - ports.add(port.entity());
318 - }
319 - return ports;
320 - }
321 -
322 - @Override
323 - public Port getPort(DeviceId deviceId, PortNumber portNumber) {
324 - Map<PortNumber, VersionedValue<Port>> ports = devicePorts.get(deviceId);
325 - return ports == null ? null : ports.get(portNumber).entity();
326 - }
327 -
328 - @Override
329 - public boolean isAvailable(DeviceId deviceId) {
330 - return devices.get(deviceId).isUp();
331 - }
332 -
333 - @Override
334 - public DeviceEvent removeDevice(DeviceId deviceId) {
335 - VersionedValue<Device> previousDevice = devices.remove(deviceId);
336 - return previousDevice == null ? null :
337 - new DeviceEvent(DEVICE_REMOVED, previousDevice.entity(), null);
338 - }
339 -}
1 package org.onlab.onos.store.flow.impl; 1 package org.onlab.onos.store.flow.impl;
2 2
3 -import static org.onlab.onos.net.flow.FlowRuleEvent.Type.RULE_ADDED;
4 import static org.onlab.onos.net.flow.FlowRuleEvent.Type.RULE_REMOVED; 3 import static org.onlab.onos.net.flow.FlowRuleEvent.Type.RULE_REMOVED;
5 import static org.slf4j.LoggerFactory.getLogger; 4 import static org.slf4j.LoggerFactory.getLogger;
6 5
...@@ -13,9 +12,10 @@ import org.apache.felix.scr.annotations.Deactivate; ...@@ -13,9 +12,10 @@ import org.apache.felix.scr.annotations.Deactivate;
13 import org.apache.felix.scr.annotations.Service; 12 import org.apache.felix.scr.annotations.Service;
14 import org.onlab.onos.ApplicationId; 13 import org.onlab.onos.ApplicationId;
15 import org.onlab.onos.net.DeviceId; 14 import org.onlab.onos.net.DeviceId;
16 -import org.onlab.onos.net.flow.DefaultFlowRule; 15 +import org.onlab.onos.net.flow.DefaultFlowEntry;
16 +import org.onlab.onos.net.flow.FlowEntry;
17 +import org.onlab.onos.net.flow.FlowEntry.FlowEntryState;
17 import org.onlab.onos.net.flow.FlowRule; 18 import org.onlab.onos.net.flow.FlowRule;
18 -import org.onlab.onos.net.flow.FlowRule.FlowRuleState;
19 import org.onlab.onos.net.flow.FlowRuleEvent; 19 import org.onlab.onos.net.flow.FlowRuleEvent;
20 import org.onlab.onos.net.flow.FlowRuleEvent.Type; 20 import org.onlab.onos.net.flow.FlowRuleEvent.Type;
21 import org.onlab.onos.net.flow.FlowRuleStore; 21 import org.onlab.onos.net.flow.FlowRuleStore;
...@@ -30,18 +30,18 @@ import com.google.common.collect.Multimap; ...@@ -30,18 +30,18 @@ import com.google.common.collect.Multimap;
30 /** 30 /**
31 * Manages inventory of flow rules using trivial in-memory implementation. 31 * Manages inventory of flow rules using trivial in-memory implementation.
32 */ 32 */
33 -//FIXME: I LIE I AM NOT DISTRIBUTED 33 +//FIXME I LIE. I AIN'T DISTRIBUTED
34 @Component(immediate = true) 34 @Component(immediate = true)
35 @Service 35 @Service
36 public class DistributedFlowRuleStore 36 public class DistributedFlowRuleStore
37 -extends AbstractStore<FlowRuleEvent, FlowRuleStoreDelegate> 37 + extends AbstractStore<FlowRuleEvent, FlowRuleStoreDelegate>
38 -implements FlowRuleStore { 38 + implements FlowRuleStore {
39 39
40 private final Logger log = getLogger(getClass()); 40 private final Logger log = getLogger(getClass());
41 41
42 // store entries as a pile of rules, no info about device tables 42 // store entries as a pile of rules, no info about device tables
43 - private final Multimap<DeviceId, FlowRule> flowEntries = 43 + private final Multimap<DeviceId, FlowEntry> flowEntries =
44 - ArrayListMultimap.<DeviceId, FlowRule>create(); 44 + ArrayListMultimap.<DeviceId, FlowEntry>create();
45 45
46 private final Multimap<ApplicationId, FlowRule> flowEntriesById = 46 private final Multimap<ApplicationId, FlowRule> flowEntriesById =
47 ArrayListMultimap.<ApplicationId, FlowRule>create(); 47 ArrayListMultimap.<ApplicationId, FlowRule>create();
...@@ -58,8 +58,13 @@ implements FlowRuleStore { ...@@ -58,8 +58,13 @@ implements FlowRuleStore {
58 58
59 59
60 @Override 60 @Override
61 - public synchronized FlowRule getFlowRule(FlowRule rule) { 61 + public int getFlowRuleCount() {
62 - for (FlowRule f : flowEntries.get(rule.deviceId())) { 62 + return flowEntries.size();
63 + }
64 +
65 + @Override
66 + public synchronized FlowEntry getFlowEntry(FlowRule rule) {
67 + for (FlowEntry f : flowEntries.get(rule.deviceId())) {
63 if (f.equals(rule)) { 68 if (f.equals(rule)) {
64 return f; 69 return f;
65 } 70 }
...@@ -68,8 +73,8 @@ implements FlowRuleStore { ...@@ -68,8 +73,8 @@ implements FlowRuleStore {
68 } 73 }
69 74
70 @Override 75 @Override
71 - public synchronized Iterable<FlowRule> getFlowEntries(DeviceId deviceId) { 76 + public synchronized Iterable<FlowEntry> getFlowEntries(DeviceId deviceId) {
72 - Collection<FlowRule> rules = flowEntries.get(deviceId); 77 + Collection<FlowEntry> rules = flowEntries.get(deviceId);
73 if (rules == null) { 78 if (rules == null) {
74 return Collections.emptyList(); 79 return Collections.emptyList();
75 } 80 }
...@@ -77,7 +82,7 @@ implements FlowRuleStore { ...@@ -77,7 +82,7 @@ implements FlowRuleStore {
77 } 82 }
78 83
79 @Override 84 @Override
80 - public synchronized Iterable<FlowRule> getFlowEntriesByAppId(ApplicationId appId) { 85 + public synchronized Iterable<FlowRule> getFlowRulesByAppId(ApplicationId appId) {
81 Collection<FlowRule> rules = flowEntriesById.get(appId); 86 Collection<FlowRule> rules = flowEntriesById.get(appId);
82 if (rules == null) { 87 if (rules == null) {
83 return Collections.emptyList(); 88 return Collections.emptyList();
...@@ -87,7 +92,7 @@ implements FlowRuleStore { ...@@ -87,7 +92,7 @@ implements FlowRuleStore {
87 92
88 @Override 93 @Override
89 public synchronized void storeFlowRule(FlowRule rule) { 94 public synchronized void storeFlowRule(FlowRule rule) {
90 - FlowRule f = new DefaultFlowRule(rule, FlowRuleState.PENDING_ADD); 95 + FlowEntry f = new DefaultFlowEntry(rule);
91 DeviceId did = f.deviceId(); 96 DeviceId did = f.deviceId();
92 if (!flowEntries.containsEntry(did, f)) { 97 if (!flowEntries.containsEntry(did, f)) {
93 flowEntries.put(did, f); 98 flowEntries.put(did, f);
...@@ -97,57 +102,41 @@ implements FlowRuleStore { ...@@ -97,57 +102,41 @@ implements FlowRuleStore {
97 102
98 @Override 103 @Override
99 public synchronized void deleteFlowRule(FlowRule rule) { 104 public synchronized void deleteFlowRule(FlowRule rule) {
100 - FlowRule f = new DefaultFlowRule(rule, FlowRuleState.PENDING_REMOVE); 105 + FlowEntry entry = getFlowEntry(rule);
101 - DeviceId did = f.deviceId(); 106 + if (entry == null) {
102 - 107 + return;
103 - /*
104 - * find the rule and mark it for deletion.
105 - * Ultimately a flow removed will come remove it.
106 - */
107 -
108 - if (flowEntries.containsEntry(did, f)) {
109 - //synchronized (flowEntries) {
110 - flowEntries.remove(did, f);
111 - flowEntries.put(did, f);
112 - flowEntriesById.remove(rule.appId(), rule);
113 - //}
114 } 108 }
109 + entry.setState(FlowEntryState.PENDING_REMOVE);
115 } 110 }
116 111
117 @Override 112 @Override
118 - public synchronized FlowRuleEvent addOrUpdateFlowRule(FlowRule rule) { 113 + public synchronized FlowRuleEvent addOrUpdateFlowRule(FlowEntry rule) {
119 DeviceId did = rule.deviceId(); 114 DeviceId did = rule.deviceId();
120 115
121 // check if this new rule is an update to an existing entry 116 // check if this new rule is an update to an existing entry
122 - if (flowEntries.containsEntry(did, rule)) { 117 + FlowEntry stored = getFlowEntry(rule);
123 - //synchronized (flowEntries) { 118 + if (stored != null) {
124 - // Multimaps support duplicates so we have to remove our rule 119 + stored.setBytes(rule.bytes());
125 - // and replace it with the current version. 120 + stored.setLife(rule.life());
126 - flowEntries.remove(did, rule); 121 + stored.setPackets(rule.packets());
127 - flowEntries.put(did, rule); 122 + if (stored.state() == FlowEntryState.PENDING_ADD) {
128 - //} 123 + stored.setState(FlowEntryState.ADDED);
124 + return new FlowRuleEvent(Type.RULE_ADDED, rule);
125 + }
129 return new FlowRuleEvent(Type.RULE_UPDATED, rule); 126 return new FlowRuleEvent(Type.RULE_UPDATED, rule);
130 } 127 }
131 128
132 flowEntries.put(did, rule); 129 flowEntries.put(did, rule);
133 - return new FlowRuleEvent(RULE_ADDED, rule); 130 + return null;
134 } 131 }
135 132
136 @Override 133 @Override
137 - public synchronized FlowRuleEvent removeFlowRule(FlowRule rule) { 134 + public synchronized FlowRuleEvent removeFlowRule(FlowEntry rule) {
138 - //synchronized (this) { 135 + // This is where one could mark a rule as removed and still keep it in the store.
139 if (flowEntries.remove(rule.deviceId(), rule)) { 136 if (flowEntries.remove(rule.deviceId(), rule)) {
140 return new FlowRuleEvent(RULE_REMOVED, rule); 137 return new FlowRuleEvent(RULE_REMOVED, rule);
141 } else { 138 } else {
142 return null; 139 return null;
143 } 140 }
144 - //}
145 } 141 }
146 -
147 -
148 -
149 -
150 -
151 -
152 -
153 } 142 }
......
...@@ -42,6 +42,7 @@ import com.google.common.collect.ImmutableSet.Builder; ...@@ -42,6 +42,7 @@ import com.google.common.collect.ImmutableSet.Builder;
42 import static com.google.common.base.Preconditions.checkArgument; 42 import static com.google.common.base.Preconditions.checkArgument;
43 import static com.google.common.base.Preconditions.checkState; 43 import static com.google.common.base.Preconditions.checkState;
44 44
45 +//TODO: Add support for multiple provider and annotations
45 /** 46 /**
46 * Manages inventory of infrastructure links using a protocol that takes into consideration 47 * Manages inventory of infrastructure links using a protocol that takes into consideration
47 * the order in which events occur. 48 * the order in which events occur.
......
1 +package org.onlab.onos.store.serializers;
2 +
3 +import org.onlab.onos.cluster.NodeId;
4 +import org.onlab.onos.store.cluster.messaging.ClusterMessage;
5 +import org.onlab.onos.store.cluster.messaging.MessageSubject;
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 +public final class ClusterMessageSerializer extends Serializer<ClusterMessage> {
12 +
13 + /**
14 + * Creates a serializer for {@link ClusterMessage}.
15 + */
16 + public ClusterMessageSerializer() {
17 + // does not accept null
18 + super(false);
19 + }
20 +
21 + @Override
22 + public void write(Kryo kryo, Output output, ClusterMessage message) {
23 + kryo.writeClassAndObject(output, message.sender());
24 + kryo.writeClassAndObject(output, message.subject());
25 + output.writeInt(message.payload().length);
26 + output.writeBytes(message.payload());
27 + }
28 +
29 + @Override
30 + public ClusterMessage read(Kryo kryo, Input input,
31 + Class<ClusterMessage> type) {
32 + NodeId sender = (NodeId) kryo.readClassAndObject(input);
33 + MessageSubject subject = (MessageSubject) kryo.readClassAndObject(input);
34 + int payloadSize = input.readInt();
35 + byte[] payload = input.readBytes(payloadSize);
36 + return new ClusterMessage(sender, subject, payload);
37 + }
38 +}
...\ No newline at end of file ...\ No newline at end of file
1 package org.onlab.onos.store.serializers; 1 package org.onlab.onos.store.serializers;
2 2
3 -import org.onlab.onos.store.impl.OnosTimestamp; 3 +import org.onlab.onos.store.common.impl.MastershipBasedTimestamp;
4 4
5 import com.esotericsoftware.kryo.Kryo; 5 import com.esotericsoftware.kryo.Kryo;
6 import com.esotericsoftware.kryo.Serializer; 6 import com.esotericsoftware.kryo.Serializer;
7 import com.esotericsoftware.kryo.io.Input; 7 import com.esotericsoftware.kryo.io.Input;
8 import com.esotericsoftware.kryo.io.Output; 8 import com.esotericsoftware.kryo.io.Output;
9 9
10 +// To be used if Timestamp ever needs to cross bundle boundary.
10 /** 11 /**
11 - * Kryo Serializer for {@link OnosTimestamp}. 12 + * Kryo Serializer for {@link MastershipBasedTimestamp}.
12 */ 13 */
13 -public class OnosTimestampSerializer extends Serializer<OnosTimestamp> { 14 +public class MastershipBasedTimestampSerializer extends Serializer<MastershipBasedTimestamp> {
14 15
15 /** 16 /**
16 - * Default constructor. 17 + * Creates a serializer for {@link MastershipBasedTimestamp}.
17 */ 18 */
18 - public OnosTimestampSerializer() { 19 + public MastershipBasedTimestampSerializer() {
19 // non-null, immutable 20 // non-null, immutable
20 super(false, true); 21 super(false, true);
21 } 22 }
22 23
23 @Override 24 @Override
24 - public void write(Kryo kryo, Output output, OnosTimestamp object) { 25 + public void write(Kryo kryo, Output output, MastershipBasedTimestamp object) {
25 output.writeInt(object.termNumber()); 26 output.writeInt(object.termNumber());
26 output.writeInt(object.sequenceNumber()); 27 output.writeInt(object.sequenceNumber());
27 } 28 }
28 29
29 @Override 30 @Override
30 - public OnosTimestamp read(Kryo kryo, Input input, Class<OnosTimestamp> type) { 31 + public MastershipBasedTimestamp read(Kryo kryo, Input input, Class<MastershipBasedTimestamp> type) {
31 final int term = input.readInt(); 32 final int term = input.readInt();
32 final int sequence = input.readInt(); 33 final int sequence = input.readInt();
33 - return new OnosTimestamp(term, sequence); 34 + return new MastershipBasedTimestamp(term, sequence);
34 } 35 }
35 } 36 }
......
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 +}
...@@ -125,7 +125,8 @@ implements TopologyStore { ...@@ -125,7 +125,8 @@ implements TopologyStore {
125 // Promote the new topology to current and return a ready-to-send event. 125 // Promote the new topology to current and return a ready-to-send event.
126 synchronized (this) { 126 synchronized (this) {
127 current = newTopology; 127 current = newTopology;
128 - return new TopologyEvent(TopologyEvent.Type.TOPOLOGY_CHANGED, current); 128 + return new TopologyEvent(TopologyEvent.Type.TOPOLOGY_CHANGED,
129 + current, reasons);
129 } 130 }
130 } 131 }
131 132
......
...@@ -7,6 +7,7 @@ import org.junit.Test; ...@@ -7,6 +7,7 @@ import org.junit.Test;
7 import org.onlab.onos.cluster.DefaultControllerNode; 7 import org.onlab.onos.cluster.DefaultControllerNode;
8 import org.onlab.onos.cluster.NodeId; 8 import org.onlab.onos.cluster.NodeId;
9 import org.onlab.onos.store.cluster.messaging.impl.ClusterCommunicationManager; 9 import org.onlab.onos.store.cluster.messaging.impl.ClusterCommunicationManager;
10 +import org.onlab.onos.store.cluster.messaging.impl.MessageSerializer;
10 import org.onlab.netty.NettyMessagingService; 11 import org.onlab.netty.NettyMessagingService;
11 import org.onlab.packet.IpPrefix; 12 import org.onlab.packet.IpPrefix;
12 13
......
1 +package org.onlab.onos.store.common.impl;
2 +
3 +import static org.junit.Assert.*;
4 +
5 +import java.nio.ByteBuffer;
6 +
7 +import org.junit.Test;
8 +import org.onlab.onos.store.Timestamp;
9 +import org.onlab.onos.store.serializers.MastershipBasedTimestampSerializer;
10 +import org.onlab.util.KryoPool;
11 +
12 +import com.google.common.testing.EqualsTester;
13 +
14 +/**
15 + * Test of {@link MastershipBasedTimestamp}.
16 + */
17 +public class MastershipBasedTimestampTest {
18 +
19 + private static final Timestamp TS_1_1 = new MastershipBasedTimestamp(1, 1);
20 + private static final Timestamp TS_1_2 = new MastershipBasedTimestamp(1, 2);
21 + private static final Timestamp TS_2_1 = new MastershipBasedTimestamp(2, 1);
22 + private static final Timestamp TS_2_2 = new MastershipBasedTimestamp(2, 2);
23 +
24 + @Test
25 + public final void testBasic() {
26 + final int termNumber = 5;
27 + final int sequenceNumber = 6;
28 + MastershipBasedTimestamp ts = new MastershipBasedTimestamp(termNumber,
29 + sequenceNumber);
30 +
31 + assertEquals(termNumber, ts.termNumber());
32 + assertEquals(sequenceNumber, ts.sequenceNumber());
33 + }
34 +
35 + @Test
36 + public final void testCompareTo() {
37 + assertTrue(TS_1_1.compareTo(TS_1_1) == 0);
38 + assertTrue(TS_1_1.compareTo(new MastershipBasedTimestamp(1, 1)) == 0);
39 +
40 + assertTrue(TS_1_1.compareTo(TS_1_2) < 0);
41 + assertTrue(TS_1_2.compareTo(TS_1_1) > 0);
42 +
43 + assertTrue(TS_1_2.compareTo(TS_2_1) < 0);
44 + assertTrue(TS_1_2.compareTo(TS_2_2) < 0);
45 + assertTrue(TS_2_1.compareTo(TS_1_1) > 0);
46 + assertTrue(TS_2_2.compareTo(TS_1_1) > 0);
47 + }
48 +
49 + @Test
50 + public final void testEqualsObject() {
51 + new EqualsTester()
52 + .addEqualityGroup(new MastershipBasedTimestamp(1, 1),
53 + new MastershipBasedTimestamp(1, 1), TS_1_1)
54 + .addEqualityGroup(new MastershipBasedTimestamp(1, 2),
55 + new MastershipBasedTimestamp(1, 2), TS_1_2)
56 + .addEqualityGroup(new MastershipBasedTimestamp(2, 1),
57 + new MastershipBasedTimestamp(2, 1), TS_2_1)
58 + .addEqualityGroup(new MastershipBasedTimestamp(2, 2),
59 + new MastershipBasedTimestamp(2, 2), TS_2_2)
60 + .testEquals();
61 + }
62 +
63 + @Test
64 + public final void testKryoSerializable() {
65 + final ByteBuffer buffer = ByteBuffer.allocate(1 * 1024 * 1024);
66 + final KryoPool kryos = KryoPool.newBuilder()
67 + .register(MastershipBasedTimestamp.class)
68 + .build();
69 +
70 + kryos.serialize(TS_2_1, buffer);
71 + buffer.flip();
72 + Timestamp copy = kryos.deserialize(buffer);
73 +
74 + new EqualsTester()
75 + .addEqualityGroup(TS_2_1, copy)
76 + .testEquals();
77 + }
78 +
79 + @Test
80 + public final void testKryoSerializableWithHandcraftedSerializer() {
81 + final ByteBuffer buffer = ByteBuffer.allocate(1 * 1024 * 1024);
82 + final KryoPool kryos = KryoPool.newBuilder()
83 + .register(MastershipBasedTimestamp.class, new MastershipBasedTimestampSerializer())
84 + .build();
85 +
86 + kryos.serialize(TS_1_2, buffer);
87 + buffer.flip();
88 + Timestamp copy = kryos.deserialize(buffer);
89 +
90 + new EqualsTester()
91 + .addEqualityGroup(TS_1_2, copy)
92 + .testEquals();
93 + }
94 +
95 +}
1 +package org.onlab.onos.store.common.impl;
2 +
3 +import static org.junit.Assert.*;
4 +
5 +import java.nio.ByteBuffer;
6 +
7 +import org.junit.Test;
8 +import org.onlab.onos.store.Timestamp;
9 +import org.onlab.util.KryoPool;
10 +
11 +import com.google.common.testing.EqualsTester;
12 +
13 +/**
14 + * Test of {@link Timestamped}.
15 + */
16 +public class TimestampedTest {
17 +
18 + private static final Timestamp TS_1_1 = new MastershipBasedTimestamp(1, 1);
19 + private static final Timestamp TS_1_2 = new MastershipBasedTimestamp(1, 2);
20 + private static final Timestamp TS_2_1 = new MastershipBasedTimestamp(2, 1);
21 +
22 + @Test
23 + public final void testHashCode() {
24 + Timestamped<String> a = new Timestamped<>("a", TS_1_1);
25 + Timestamped<String> b = new Timestamped<>("b", TS_1_1);
26 + assertTrue("value does not impact hashCode",
27 + a.hashCode() == b.hashCode());
28 + }
29 +
30 + @Test
31 + public final void testEquals() {
32 + Timestamped<String> a = new Timestamped<>("a", TS_1_1);
33 + Timestamped<String> b = new Timestamped<>("b", TS_1_1);
34 + assertTrue("value does not impact equality",
35 + a.equals(b));
36 +
37 + new EqualsTester()
38 + .addEqualityGroup(new Timestamped<>("a", TS_1_1),
39 + new Timestamped<>("b", TS_1_1),
40 + new Timestamped<>("c", TS_1_1))
41 + .addEqualityGroup(new Timestamped<>("a", TS_1_2),
42 + new Timestamped<>("b", TS_1_2),
43 + new Timestamped<>("c", TS_1_2))
44 + .addEqualityGroup(new Timestamped<>("a", TS_2_1),
45 + new Timestamped<>("b", TS_2_1),
46 + new Timestamped<>("c", TS_2_1))
47 + .testEquals();
48 +
49 + }
50 +
51 + @Test
52 + public final void testValue() {
53 + final Integer n = Integer.valueOf(42);
54 + Timestamped<Integer> tsv = new Timestamped<>(n, TS_1_1);
55 + assertSame(n, tsv.value());
56 +
57 + }
58 +
59 + @Test(expected = NullPointerException.class)
60 + public final void testValueNonNull() {
61 + new Timestamped<>(null, TS_1_1);
62 + }
63 +
64 + @Test(expected = NullPointerException.class)
65 + public final void testTimestampNonNull() {
66 + new Timestamped<>("Foo", null);
67 + }
68 +
69 + @Test
70 + public final void testIsNewer() {
71 + Timestamped<String> a = new Timestamped<>("a", TS_1_2);
72 + Timestamped<String> b = new Timestamped<>("b", TS_1_1);
73 + assertTrue(a.isNewer(b));
74 + assertFalse(b.isNewer(a));
75 + }
76 +
77 + @Test
78 + public final void testKryoSerializable() {
79 + final ByteBuffer buffer = ByteBuffer.allocate(1 * 1024 * 1024);
80 + final KryoPool kryos = KryoPool.newBuilder()
81 + .register(Timestamped.class,
82 + MastershipBasedTimestamp.class)
83 + .build();
84 +
85 + Timestamped<String> original = new Timestamped<>("foobar", TS_1_1);
86 + kryos.serialize(original, buffer);
87 + buffer.flip();
88 + Timestamped<String> copy = kryos.deserialize(buffer);
89 +
90 + new EqualsTester()
91 + .addEqualityGroup(original, copy)
92 + .testEquals();
93 + }
94 +}
1 +package org.onlab.onos.store.device.impl;
2 +
3 +import static org.junit.Assert.*;
4 +import static org.onlab.onos.net.Device.Type.SWITCH;
5 +import static org.onlab.onos.net.DeviceId.deviceId;
6 +import static org.onlab.onos.net.device.DeviceEvent.Type.*;
7 +
8 +import java.io.IOException;
9 +import java.util.Arrays;
10 +import java.util.HashMap;
11 +import java.util.List;
12 +import java.util.Map;
13 +import java.util.Set;
14 +import java.util.concurrent.CountDownLatch;
15 +import java.util.concurrent.TimeUnit;
16 +
17 +import org.junit.After;
18 +import org.junit.AfterClass;
19 +import org.junit.Before;
20 +import org.junit.BeforeClass;
21 +import org.junit.Ignore;
22 +import org.junit.Test;
23 +import org.onlab.onos.cluster.ClusterEventListener;
24 +import org.onlab.onos.cluster.ClusterService;
25 +import org.onlab.onos.cluster.ControllerNode;
26 +import org.onlab.onos.cluster.ControllerNode.State;
27 +import org.onlab.onos.cluster.DefaultControllerNode;
28 +import org.onlab.onos.cluster.MastershipTerm;
29 +import org.onlab.onos.cluster.NodeId;
30 +import org.onlab.onos.net.Annotations;
31 +import org.onlab.onos.net.DefaultAnnotations;
32 +import org.onlab.onos.net.Device;
33 +import org.onlab.onos.net.DeviceId;
34 +import org.onlab.onos.net.Port;
35 +import org.onlab.onos.net.PortNumber;
36 +import org.onlab.onos.net.SparseAnnotations;
37 +import org.onlab.onos.net.device.DefaultDeviceDescription;
38 +import org.onlab.onos.net.device.DefaultPortDescription;
39 +import org.onlab.onos.net.device.DeviceDescription;
40 +import org.onlab.onos.net.device.DeviceEvent;
41 +import org.onlab.onos.net.device.DeviceStore;
42 +import org.onlab.onos.net.device.DeviceStoreDelegate;
43 +import org.onlab.onos.net.device.PortDescription;
44 +import org.onlab.onos.net.provider.ProviderId;
45 +import org.onlab.onos.store.ClockService;
46 +import org.onlab.onos.store.cluster.messaging.ClusterCommunicationService;
47 +import org.onlab.onos.store.cluster.messaging.ClusterMessage;
48 +import org.onlab.onos.store.cluster.messaging.ClusterMessageHandler;
49 +import org.onlab.onos.store.cluster.messaging.MessageSubject;
50 +import org.onlab.packet.IpPrefix;
51 +
52 +import com.google.common.collect.Iterables;
53 +import com.google.common.collect.Sets;
54 +
55 +
56 +// TODO add tests for remote replication
57 +/**
58 + * Test of the gossip based distributed DeviceStore implementation.
59 + */
60 +public class GossipDeviceStoreTest {
61 +
62 + private static final ProviderId PID = new ProviderId("of", "foo");
63 + private static final ProviderId PIDA = new ProviderId("of", "bar", true);
64 + private static final DeviceId DID1 = deviceId("of:foo");
65 + private static final DeviceId DID2 = deviceId("of:bar");
66 + private static final String MFR = "whitebox";
67 + private static final String HW = "1.1.x";
68 + private static final String SW1 = "3.8.1";
69 + private static final String SW2 = "3.9.5";
70 + private static final String SN = "43311-12345";
71 +
72 + private static final PortNumber P1 = PortNumber.portNumber(1);
73 + private static final PortNumber P2 = PortNumber.portNumber(2);
74 + private static final PortNumber P3 = PortNumber.portNumber(3);
75 +
76 + private static final SparseAnnotations A1 = DefaultAnnotations.builder()
77 + .set("A1", "a1")
78 + .set("B1", "b1")
79 + .build();
80 + private static final SparseAnnotations A1_2 = DefaultAnnotations.builder()
81 + .remove("A1")
82 + .set("B3", "b3")
83 + .build();
84 + private static final SparseAnnotations A2 = DefaultAnnotations.builder()
85 + .set("A2", "a2")
86 + .set("B2", "b2")
87 + .build();
88 + private static final SparseAnnotations A2_2 = DefaultAnnotations.builder()
89 + .remove("A2")
90 + .set("B4", "b4")
91 + .build();
92 +
93 + private static final NodeId MYSELF = new NodeId("myself");
94 +
95 + private GossipDeviceStore gossipDeviceStore;
96 + private DeviceStore deviceStore;
97 +
98 + private DeviceClockManager deviceClockManager;
99 + private ClockService clockService;
100 +
101 + @BeforeClass
102 + public static void setUpBeforeClass() throws Exception {
103 + }
104 +
105 + @AfterClass
106 + public static void tearDownAfterClass() throws Exception {
107 + }
108 +
109 +
110 + @Before
111 + public void setUp() throws Exception {
112 + deviceClockManager = new DeviceClockManager();
113 + deviceClockManager.activate();
114 + clockService = deviceClockManager;
115 +
116 + deviceClockManager.setMastershipTerm(DID1, MastershipTerm.of(MYSELF, 1));
117 + deviceClockManager.setMastershipTerm(DID2, MastershipTerm.of(MYSELF, 2));
118 +
119 + ClusterCommunicationService clusterCommunicator = new TestClusterCommunicationService();
120 + ClusterService clusterService = new TestClusterService();
121 +
122 + gossipDeviceStore = new TestGossipDeviceStore(clockService, clusterService, clusterCommunicator);
123 + gossipDeviceStore.activate();
124 + deviceStore = gossipDeviceStore;
125 + }
126 +
127 + @After
128 + public void tearDown() throws Exception {
129 + gossipDeviceStore.deactivate();
130 + deviceClockManager.deactivate();
131 + }
132 +
133 + private void putDevice(DeviceId deviceId, String swVersion,
134 + SparseAnnotations... annotations) {
135 + DeviceDescription description =
136 + new DefaultDeviceDescription(deviceId.uri(), SWITCH, MFR,
137 + HW, swVersion, SN, annotations);
138 + deviceStore.createOrUpdateDevice(PID, deviceId, description);
139 + }
140 +
141 + private void putDeviceAncillary(DeviceId deviceId, String swVersion,
142 + SparseAnnotations... annotations) {
143 + DeviceDescription description =
144 + new DefaultDeviceDescription(deviceId.uri(), SWITCH, MFR,
145 + HW, swVersion, SN, annotations);
146 + deviceStore.createOrUpdateDevice(PIDA, deviceId, description);
147 + }
148 +
149 + private static void assertDevice(DeviceId id, String swVersion, Device device) {
150 + assertNotNull(device);
151 + assertEquals(id, device.id());
152 + assertEquals(MFR, device.manufacturer());
153 + assertEquals(HW, device.hwVersion());
154 + assertEquals(swVersion, device.swVersion());
155 + assertEquals(SN, device.serialNumber());
156 + }
157 +
158 + /**
159 + * Verifies that Annotations created by merging {@code annotations} is
160 + * equal to actual Annotations.
161 + *
162 + * @param actual Annotations to check
163 + * @param annotations
164 + */
165 + private static void assertAnnotationsEquals(Annotations actual, SparseAnnotations... annotations) {
166 + DefaultAnnotations expected = DefaultAnnotations.builder().build();
167 + for (SparseAnnotations a : annotations) {
168 + expected = DefaultAnnotations.merge(expected, a);
169 + }
170 + assertEquals(expected.keys(), actual.keys());
171 + for (String key : expected.keys()) {
172 + assertEquals(expected.value(key), actual.value(key));
173 + }
174 + }
175 +
176 + @Test
177 + public final void testGetDeviceCount() {
178 + assertEquals("initialy empty", 0, deviceStore.getDeviceCount());
179 +
180 + putDevice(DID1, SW1);
181 + putDevice(DID2, SW2);
182 + putDevice(DID1, SW1);
183 +
184 + assertEquals("expect 2 uniq devices", 2, deviceStore.getDeviceCount());
185 + }
186 +
187 + @Test
188 + public final void testGetDevices() {
189 + assertEquals("initialy empty", 0, Iterables.size(deviceStore.getDevices()));
190 +
191 + putDevice(DID1, SW1);
192 + putDevice(DID2, SW2);
193 + putDevice(DID1, SW1);
194 +
195 + assertEquals("expect 2 uniq devices",
196 + 2, Iterables.size(deviceStore.getDevices()));
197 +
198 + Map<DeviceId, Device> devices = new HashMap<>();
199 + for (Device device : deviceStore.getDevices()) {
200 + devices.put(device.id(), device);
201 + }
202 +
203 + assertDevice(DID1, SW1, devices.get(DID1));
204 + assertDevice(DID2, SW2, devices.get(DID2));
205 +
206 + // add case for new node?
207 + }
208 +
209 + @Test
210 + public final void testGetDevice() {
211 +
212 + putDevice(DID1, SW1);
213 +
214 + assertDevice(DID1, SW1, deviceStore.getDevice(DID1));
215 + assertNull("DID2 shouldn't be there", deviceStore.getDevice(DID2));
216 + }
217 +
218 + @Test
219 + public final void testCreateOrUpdateDevice() {
220 + DeviceDescription description =
221 + new DefaultDeviceDescription(DID1.uri(), SWITCH, MFR,
222 + HW, SW1, SN);
223 + DeviceEvent event = deviceStore.createOrUpdateDevice(PID, DID1, description);
224 + assertEquals(DEVICE_ADDED, event.type());
225 + assertDevice(DID1, SW1, event.subject());
226 +
227 + DeviceDescription description2 =
228 + new DefaultDeviceDescription(DID1.uri(), SWITCH, MFR,
229 + HW, SW2, SN);
230 + DeviceEvent event2 = deviceStore.createOrUpdateDevice(PID, DID1, description2);
231 + assertEquals(DEVICE_UPDATED, event2.type());
232 + assertDevice(DID1, SW2, event2.subject());
233 +
234 + assertNull("No change expected", deviceStore.createOrUpdateDevice(PID, DID1, description2));
235 + }
236 +
237 + @Test
238 + public final void testCreateOrUpdateDeviceAncillary() {
239 + DeviceDescription description =
240 + new DefaultDeviceDescription(DID1.uri(), SWITCH, MFR,
241 + HW, SW1, SN, A2);
242 + DeviceEvent event = deviceStore.createOrUpdateDevice(PIDA, DID1, description);
243 + assertEquals(DEVICE_ADDED, event.type());
244 + assertDevice(DID1, SW1, event.subject());
245 + assertEquals(PIDA, event.subject().providerId());
246 + assertAnnotationsEquals(event.subject().annotations(), A2);
247 + assertFalse("Ancillary will not bring device up", deviceStore.isAvailable(DID1));
248 +
249 + DeviceDescription description2 =
250 + new DefaultDeviceDescription(DID1.uri(), SWITCH, MFR,
251 + HW, SW2, SN, A1);
252 + DeviceEvent event2 = deviceStore.createOrUpdateDevice(PID, DID1, description2);
253 + assertEquals(DEVICE_UPDATED, event2.type());
254 + assertDevice(DID1, SW2, event2.subject());
255 + assertEquals(PID, event2.subject().providerId());
256 + assertAnnotationsEquals(event2.subject().annotations(), A1, A2);
257 + assertTrue(deviceStore.isAvailable(DID1));
258 +
259 + assertNull("No change expected", deviceStore.createOrUpdateDevice(PID, DID1, description2));
260 +
261 + // For now, Ancillary is ignored once primary appears
262 + assertNull("No change expected", deviceStore.createOrUpdateDevice(PIDA, DID1, description));
263 +
264 + // But, Ancillary annotations will be in effect
265 + DeviceDescription description3 =
266 + new DefaultDeviceDescription(DID1.uri(), SWITCH, MFR,
267 + HW, SW1, SN, A2_2);
268 + DeviceEvent event3 = deviceStore.createOrUpdateDevice(PIDA, DID1, description3);
269 + assertEquals(DEVICE_UPDATED, event3.type());
270 + // basic information will be the one from Primary
271 + assertDevice(DID1, SW2, event3.subject());
272 + assertEquals(PID, event3.subject().providerId());
273 + // but annotation from Ancillary will be merged
274 + assertAnnotationsEquals(event3.subject().annotations(), A1, A2, A2_2);
275 + assertTrue(deviceStore.isAvailable(DID1));
276 + }
277 +
278 +
279 + @Test
280 + public final void testMarkOffline() {
281 +
282 + putDevice(DID1, SW1);
283 + assertTrue(deviceStore.isAvailable(DID1));
284 +
285 + DeviceEvent event = deviceStore.markOffline(DID1);
286 + assertEquals(DEVICE_AVAILABILITY_CHANGED, event.type());
287 + assertDevice(DID1, SW1, event.subject());
288 + assertFalse(deviceStore.isAvailable(DID1));
289 +
290 + DeviceEvent event2 = deviceStore.markOffline(DID1);
291 + assertNull("No change, no event", event2);
292 +}
293 +
294 + @Test
295 + public final void testUpdatePorts() {
296 + putDevice(DID1, SW1);
297 + List<PortDescription> pds = Arrays.<PortDescription>asList(
298 + new DefaultPortDescription(P1, true),
299 + new DefaultPortDescription(P2, true)
300 + );
301 +
302 + List<DeviceEvent> events = deviceStore.updatePorts(PID, DID1, pds);
303 +
304 + Set<PortNumber> expectedPorts = Sets.newHashSet(P1, P2);
305 + for (DeviceEvent event : events) {
306 + assertEquals(PORT_ADDED, event.type());
307 + assertDevice(DID1, SW1, event.subject());
308 + assertTrue("PortNumber is one of expected",
309 + expectedPorts.remove(event.port().number()));
310 + assertTrue("Port is enabled", event.port().isEnabled());
311 + }
312 + assertTrue("Event for all expectedport appeared", expectedPorts.isEmpty());
313 +
314 +
315 + List<PortDescription> pds2 = Arrays.<PortDescription>asList(
316 + new DefaultPortDescription(P1, false),
317 + new DefaultPortDescription(P2, true),
318 + new DefaultPortDescription(P3, true)
319 + );
320 +
321 + events = deviceStore.updatePorts(PID, DID1, pds2);
322 + assertFalse("event should be triggered", events.isEmpty());
323 + for (DeviceEvent event : events) {
324 + PortNumber num = event.port().number();
325 + if (P1.equals(num)) {
326 + assertEquals(PORT_UPDATED, event.type());
327 + assertDevice(DID1, SW1, event.subject());
328 + assertFalse("Port is disabled", event.port().isEnabled());
329 + } else if (P2.equals(num)) {
330 + fail("P2 event not expected.");
331 + } else if (P3.equals(num)) {
332 + assertEquals(PORT_ADDED, event.type());
333 + assertDevice(DID1, SW1, event.subject());
334 + assertTrue("Port is enabled", event.port().isEnabled());
335 + } else {
336 + fail("Unknown port number encountered: " + num);
337 + }
338 + }
339 +
340 + List<PortDescription> pds3 = Arrays.<PortDescription>asList(
341 + new DefaultPortDescription(P1, false),
342 + new DefaultPortDescription(P2, true)
343 + );
344 + events = deviceStore.updatePorts(PID, DID1, pds3);
345 + assertFalse("event should be triggered", events.isEmpty());
346 + for (DeviceEvent event : events) {
347 + PortNumber num = event.port().number();
348 + if (P1.equals(num)) {
349 + fail("P1 event not expected.");
350 + } else if (P2.equals(num)) {
351 + fail("P2 event not expected.");
352 + } else if (P3.equals(num)) {
353 + assertEquals(PORT_REMOVED, event.type());
354 + assertDevice(DID1, SW1, event.subject());
355 + assertTrue("Port was enabled", event.port().isEnabled());
356 + } else {
357 + fail("Unknown port number encountered: " + num);
358 + }
359 + }
360 +
361 + }
362 +
363 + @Test
364 + public final void testUpdatePortStatus() {
365 + putDevice(DID1, SW1);
366 + List<PortDescription> pds = Arrays.<PortDescription>asList(
367 + new DefaultPortDescription(P1, true)
368 + );
369 + deviceStore.updatePorts(PID, DID1, pds);
370 +
371 + DeviceEvent event = deviceStore.updatePortStatus(PID, DID1,
372 + new DefaultPortDescription(P1, false));
373 + assertEquals(PORT_UPDATED, event.type());
374 + assertDevice(DID1, SW1, event.subject());
375 + assertEquals(P1, event.port().number());
376 + assertFalse("Port is disabled", event.port().isEnabled());
377 +
378 + }
379 + @Test
380 + public final void testUpdatePortStatusAncillary() {
381 + putDeviceAncillary(DID1, SW1);
382 + putDevice(DID1, SW1);
383 + List<PortDescription> pds = Arrays.<PortDescription>asList(
384 + new DefaultPortDescription(P1, true, A1)
385 + );
386 + deviceStore.updatePorts(PID, DID1, pds);
387 +
388 + DeviceEvent event = deviceStore.updatePortStatus(PID, DID1,
389 + new DefaultPortDescription(P1, false, A1_2));
390 + assertEquals(PORT_UPDATED, event.type());
391 + assertDevice(DID1, SW1, event.subject());
392 + assertEquals(P1, event.port().number());
393 + assertAnnotationsEquals(event.port().annotations(), A1, A1_2);
394 + assertFalse("Port is disabled", event.port().isEnabled());
395 +
396 + DeviceEvent event2 = deviceStore.updatePortStatus(PIDA, DID1,
397 + new DefaultPortDescription(P1, true));
398 + assertNull("Ancillary is ignored if primary exists", event2);
399 +
400 + // but, Ancillary annotation update will be notified
401 + DeviceEvent event3 = deviceStore.updatePortStatus(PIDA, DID1,
402 + new DefaultPortDescription(P1, true, A2));
403 + assertEquals(PORT_UPDATED, event3.type());
404 + assertDevice(DID1, SW1, event3.subject());
405 + assertEquals(P1, event3.port().number());
406 + assertAnnotationsEquals(event3.port().annotations(), A1, A1_2, A2);
407 + assertFalse("Port is disabled", event3.port().isEnabled());
408 +
409 + // port only reported from Ancillary will be notified as down
410 + DeviceEvent event4 = deviceStore.updatePortStatus(PIDA, DID1,
411 + new DefaultPortDescription(P2, true));
412 + assertEquals(PORT_ADDED, event4.type());
413 + assertDevice(DID1, SW1, event4.subject());
414 + assertEquals(P2, event4.port().number());
415 + assertAnnotationsEquals(event4.port().annotations());
416 + assertFalse("Port is disabled if not given from primary provider",
417 + event4.port().isEnabled());
418 + }
419 +
420 + @Test
421 + public final void testGetPorts() {
422 + putDevice(DID1, SW1);
423 + putDevice(DID2, SW1);
424 + List<PortDescription> pds = Arrays.<PortDescription>asList(
425 + new DefaultPortDescription(P1, true),
426 + new DefaultPortDescription(P2, true)
427 + );
428 + deviceStore.updatePorts(PID, DID1, pds);
429 +
430 + Set<PortNumber> expectedPorts = Sets.newHashSet(P1, P2);
431 + List<Port> ports = deviceStore.getPorts(DID1);
432 + for (Port port : ports) {
433 + assertTrue("Port is enabled", port.isEnabled());
434 + assertTrue("PortNumber is one of expected",
435 + expectedPorts.remove(port.number()));
436 + }
437 + assertTrue("Event for all expectedport appeared", expectedPorts.isEmpty());
438 +
439 +
440 + assertTrue("DID2 has no ports", deviceStore.getPorts(DID2).isEmpty());
441 + }
442 +
443 + @Test
444 + public final void testGetPort() {
445 + putDevice(DID1, SW1);
446 + putDevice(DID2, SW1);
447 + List<PortDescription> pds = Arrays.<PortDescription>asList(
448 + new DefaultPortDescription(P1, true),
449 + new DefaultPortDescription(P2, false)
450 + );
451 + deviceStore.updatePorts(PID, DID1, pds);
452 +
453 + Port port1 = deviceStore.getPort(DID1, P1);
454 + assertEquals(P1, port1.number());
455 + assertTrue("Port is enabled", port1.isEnabled());
456 +
457 + Port port2 = deviceStore.getPort(DID1, P2);
458 + assertEquals(P2, port2.number());
459 + assertFalse("Port is disabled", port2.isEnabled());
460 +
461 + Port port3 = deviceStore.getPort(DID1, P3);
462 + assertNull("P3 not expected", port3);
463 + }
464 +
465 + @Test
466 + public final void testRemoveDevice() {
467 + putDevice(DID1, SW1, A1);
468 + List<PortDescription> pds = Arrays.<PortDescription>asList(
469 + new DefaultPortDescription(P1, true, A2)
470 + );
471 + deviceStore.updatePorts(PID, DID1, pds);
472 + putDevice(DID2, SW1);
473 +
474 + assertEquals(2, deviceStore.getDeviceCount());
475 + assertEquals(1, deviceStore.getPorts(DID1).size());
476 + assertAnnotationsEquals(deviceStore.getDevice(DID1).annotations(), A1);
477 + assertAnnotationsEquals(deviceStore.getPort(DID1, P1).annotations(), A2);
478 +
479 + DeviceEvent event = deviceStore.removeDevice(DID1);
480 + assertEquals(DEVICE_REMOVED, event.type());
481 + assertDevice(DID1, SW1, event.subject());
482 +
483 + assertEquals(1, deviceStore.getDeviceCount());
484 + assertEquals(0, deviceStore.getPorts(DID1).size());
485 +
486 + // putBack Device, Port w/o annotation
487 + putDevice(DID1, SW1);
488 + List<PortDescription> pds2 = Arrays.<PortDescription>asList(
489 + new DefaultPortDescription(P1, true)
490 + );
491 + deviceStore.updatePorts(PID, DID1, pds2);
492 +
493 + // annotations should not survive
494 + assertEquals(2, deviceStore.getDeviceCount());
495 + assertEquals(1, deviceStore.getPorts(DID1).size());
496 + assertAnnotationsEquals(deviceStore.getDevice(DID1).annotations());
497 + assertAnnotationsEquals(deviceStore.getPort(DID1, P1).annotations());
498 + }
499 +
500 + // If Delegates should be called only on remote events,
501 + // then Simple* should never call them, thus not test required.
502 + // TODO add test for Port events when we have them
503 + @Ignore("Ignore until Delegate spec. is clear.")
504 + @Test
505 + public final void testEvents() throws InterruptedException {
506 + final CountDownLatch addLatch = new CountDownLatch(1);
507 + DeviceStoreDelegate checkAdd = new DeviceStoreDelegate() {
508 + @Override
509 + public void notify(DeviceEvent event) {
510 + assertEquals(DEVICE_ADDED, event.type());
511 + assertDevice(DID1, SW1, event.subject());
512 + addLatch.countDown();
513 + }
514 + };
515 + final CountDownLatch updateLatch = new CountDownLatch(1);
516 + DeviceStoreDelegate checkUpdate = new DeviceStoreDelegate() {
517 + @Override
518 + public void notify(DeviceEvent event) {
519 + assertEquals(DEVICE_UPDATED, event.type());
520 + assertDevice(DID1, SW2, event.subject());
521 + updateLatch.countDown();
522 + }
523 + };
524 + final CountDownLatch removeLatch = new CountDownLatch(1);
525 + DeviceStoreDelegate checkRemove = new DeviceStoreDelegate() {
526 + @Override
527 + public void notify(DeviceEvent event) {
528 + assertEquals(DEVICE_REMOVED, event.type());
529 + assertDevice(DID1, SW2, event.subject());
530 + removeLatch.countDown();
531 + }
532 + };
533 +
534 + DeviceDescription description =
535 + new DefaultDeviceDescription(DID1.uri(), SWITCH, MFR,
536 + HW, SW1, SN);
537 + deviceStore.setDelegate(checkAdd);
538 + deviceStore.createOrUpdateDevice(PID, DID1, description);
539 + assertTrue("Add event fired", addLatch.await(1, TimeUnit.SECONDS));
540 +
541 +
542 + DeviceDescription description2 =
543 + new DefaultDeviceDescription(DID1.uri(), SWITCH, MFR,
544 + HW, SW2, SN);
545 + deviceStore.unsetDelegate(checkAdd);
546 + deviceStore.setDelegate(checkUpdate);
547 + deviceStore.createOrUpdateDevice(PID, DID1, description2);
548 + assertTrue("Update event fired", updateLatch.await(1, TimeUnit.SECONDS));
549 +
550 + deviceStore.unsetDelegate(checkUpdate);
551 + deviceStore.setDelegate(checkRemove);
552 + deviceStore.removeDevice(DID1);
553 + assertTrue("Remove event fired", removeLatch.await(1, TimeUnit.SECONDS));
554 + }
555 +
556 + private static final class TestGossipDeviceStore extends GossipDeviceStore {
557 +
558 + public TestGossipDeviceStore(
559 + ClockService clockService,
560 + ClusterService clusterService,
561 + ClusterCommunicationService clusterCommunicator) {
562 + this.clockService = clockService;
563 + this.clusterService = clusterService;
564 + this.clusterCommunicator = clusterCommunicator;
565 + }
566 + }
567 +
568 + private static final class TestClusterCommunicationService implements ClusterCommunicationService {
569 + @Override
570 + public boolean broadcast(ClusterMessage message) throws IOException { return true; }
571 + @Override
572 + public boolean unicast(ClusterMessage message, NodeId nodeId) throws IOException { return true; }
573 + @Override
574 + public boolean multicast(ClusterMessage message, Set<NodeId> nodeIds) throws IOException { return true; }
575 + @Override
576 + public void addSubscriber(MessageSubject subject, ClusterMessageHandler subscriber) {}
577 + }
578 +
579 + private static final class TestClusterService implements ClusterService {
580 +
581 + private static final ControllerNode ONOS1 =
582 + new DefaultControllerNode(new NodeId("N1"), IpPrefix.valueOf("127.0.0.1"));
583 + private final Map<NodeId, ControllerNode> nodes = new HashMap<>();
584 + private final Map<NodeId, ControllerNode.State> nodeStates = new HashMap<>();
585 +
586 + public TestClusterService() {
587 + nodes.put(new NodeId("N1"), ONOS1);
588 + nodeStates.put(new NodeId("N1"), ControllerNode.State.ACTIVE);
589 + }
590 +
591 + @Override
592 + public ControllerNode getLocalNode() {
593 + return ONOS1;
594 + }
595 +
596 + @Override
597 + public Set<ControllerNode> getNodes() {
598 + return Sets.newHashSet(nodes.values());
599 + }
600 +
601 + @Override
602 + public ControllerNode getNode(NodeId nodeId) {
603 + return nodes.get(nodeId);
604 + }
605 +
606 + @Override
607 + public State getState(NodeId nodeId) {
608 + return nodeStates.get(nodeId);
609 + }
610 +
611 + @Override
612 + public void addListener(ClusterEventListener listener) {
613 + }
614 +
615 + @Override
616 + public void removeListener(ClusterEventListener listener) {
617 + }
618 + }
619 +}
...@@ -57,7 +57,7 @@ public class DistributedClusterStore ...@@ -57,7 +57,7 @@ public class DistributedClusterStore
57 57
58 rawNodes = theInstance.getMap("nodes"); 58 rawNodes = theInstance.getMap("nodes");
59 OptionalCacheLoader<NodeId, DefaultControllerNode> nodeLoader 59 OptionalCacheLoader<NodeId, DefaultControllerNode> nodeLoader
60 - = new OptionalCacheLoader<>(kryoSerializationService, rawNodes); 60 + = new OptionalCacheLoader<>(serializer, rawNodes);
61 nodes = new AbsentInvalidatingLoadingCache<>(newBuilder().build(nodeLoader)); 61 nodes = new AbsentInvalidatingLoadingCache<>(newBuilder().build(nodeLoader));
62 rawNodes.addEntryListener(new RemoteCacheEventHandler<>(nodes), true); 62 rawNodes.addEntryListener(new RemoteCacheEventHandler<>(nodes), true);
63 63
......
...@@ -30,8 +30,7 @@ import org.onlab.onos.net.DeviceId; ...@@ -30,8 +30,7 @@ import org.onlab.onos.net.DeviceId;
30 import org.onlab.onos.store.common.StoreManager; 30 import org.onlab.onos.store.common.StoreManager;
31 import org.onlab.onos.store.common.StoreService; 31 import org.onlab.onos.store.common.StoreService;
32 import org.onlab.onos.store.common.TestStoreManager; 32 import org.onlab.onos.store.common.TestStoreManager;
33 -import org.onlab.onos.store.serializers.KryoSerializationManager; 33 +import org.onlab.onos.store.serializers.KryoSerializer;
34 -import org.onlab.onos.store.serializers.KryoSerializationService;
35 import org.onlab.packet.IpPrefix; 34 import org.onlab.packet.IpPrefix;
36 35
37 import com.google.common.collect.Sets; 36 import com.google.common.collect.Sets;
...@@ -57,7 +56,7 @@ public class DistributedMastershipStoreTest { ...@@ -57,7 +56,7 @@ public class DistributedMastershipStoreTest {
57 56
58 private DistributedMastershipStore dms; 57 private DistributedMastershipStore dms;
59 private TestDistributedMastershipStore testStore; 58 private TestDistributedMastershipStore testStore;
60 - private KryoSerializationManager serializationMgr; 59 + private KryoSerializer serializationMgr;
61 private StoreManager storeMgr; 60 private StoreManager storeMgr;
62 61
63 @BeforeClass 62 @BeforeClass
...@@ -76,8 +75,7 @@ public class DistributedMastershipStoreTest { ...@@ -76,8 +75,7 @@ public class DistributedMastershipStoreTest {
76 storeMgr = new TestStoreManager(Hazelcast.newHazelcastInstance(config)); 75 storeMgr = new TestStoreManager(Hazelcast.newHazelcastInstance(config));
77 storeMgr.activate(); 76 storeMgr.activate();
78 77
79 - serializationMgr = new KryoSerializationManager(); 78 + serializationMgr = new KryoSerializer();
80 - serializationMgr.activate();
81 79
82 dms = new TestDistributedMastershipStore(storeMgr, serializationMgr); 80 dms = new TestDistributedMastershipStore(storeMgr, serializationMgr);
83 dms.clusterService = new TestClusterService(); 81 dms.clusterService = new TestClusterService();
...@@ -90,8 +88,6 @@ public class DistributedMastershipStoreTest { ...@@ -90,8 +88,6 @@ public class DistributedMastershipStoreTest {
90 public void tearDown() throws Exception { 88 public void tearDown() throws Exception {
91 dms.deactivate(); 89 dms.deactivate();
92 90
93 - serializationMgr.deactivate();
94 -
95 storeMgr.deactivate(); 91 storeMgr.deactivate();
96 } 92 }
97 93
...@@ -234,9 +230,9 @@ public class DistributedMastershipStoreTest { ...@@ -234,9 +230,9 @@ public class DistributedMastershipStoreTest {
234 private class TestDistributedMastershipStore extends 230 private class TestDistributedMastershipStore extends
235 DistributedMastershipStore { 231 DistributedMastershipStore {
236 public TestDistributedMastershipStore(StoreService storeService, 232 public TestDistributedMastershipStore(StoreService storeService,
237 - KryoSerializationService kryoSerializationService) { 233 + KryoSerializer kryoSerialization) {
238 this.storeService = storeService; 234 this.storeService = storeService;
239 - this.kryoSerializationService = kryoSerializationService; 235 + this.serializer = kryoSerialization;
240 } 236 }
241 237
242 //helper to populate master/backup structures 238 //helper to populate master/backup structures
...@@ -260,6 +256,7 @@ public class DistributedMastershipStoreTest { ...@@ -260,6 +256,7 @@ public class DistributedMastershipStoreTest {
260 } 256 }
261 } 257 }
262 258
259 + //a dumb utility function.
263 public void dump() { 260 public void dump() {
264 System.out.println("standbys"); 261 System.out.println("standbys");
265 for (Map.Entry<byte [], byte []> e : standbys.entrySet()) { 262 for (Map.Entry<byte [], byte []> e : standbys.entrySet()) {
......
...@@ -15,7 +15,8 @@ import org.apache.felix.scr.annotations.ReferenceCardinality; ...@@ -15,7 +15,8 @@ import org.apache.felix.scr.annotations.ReferenceCardinality;
15 import org.onlab.onos.event.Event; 15 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.KryoSerializationService; 18 +import org.onlab.onos.store.serializers.KryoSerializer;
19 +import org.onlab.onos.store.serializers.StoreSerializer;
19 import org.slf4j.Logger; 20 import org.slf4j.Logger;
20 21
21 import static com.google.common.base.Preconditions.checkNotNull; 22 import static com.google.common.base.Preconditions.checkNotNull;
...@@ -24,7 +25,7 @@ import static org.slf4j.LoggerFactory.getLogger; ...@@ -24,7 +25,7 @@ import static org.slf4j.LoggerFactory.getLogger;
24 /** 25 /**
25 * Abstraction of a distributed store based on Hazelcast. 26 * Abstraction of a distributed store based on Hazelcast.
26 */ 27 */
27 -@Component(componentAbstract = true) 28 +@Component
28 public abstract class AbstractHazelcastStore<E extends Event, D extends StoreDelegate<E>> 29 public abstract class AbstractHazelcastStore<E extends Event, D extends StoreDelegate<E>>
29 extends AbstractStore<E, D> { 30 extends AbstractStore<E, D> {
30 31
...@@ -33,13 +34,13 @@ public abstract class AbstractHazelcastStore<E extends Event, D extends StoreDel ...@@ -33,13 +34,13 @@ public abstract class AbstractHazelcastStore<E extends Event, D extends StoreDel
33 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) 34 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
34 protected StoreService storeService; 35 protected StoreService storeService;
35 36
36 - @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) 37 + protected StoreSerializer serializer;
37 - protected KryoSerializationService kryoSerializationService;
38 38
39 protected HazelcastInstance theInstance; 39 protected HazelcastInstance theInstance;
40 40
41 @Activate 41 @Activate
42 public void activate() { 42 public void activate() {
43 + serializer = new KryoSerializer();
43 theInstance = storeService.getHazelcastInstance(); 44 theInstance = storeService.getHazelcastInstance();
44 } 45 }
45 46
...@@ -50,7 +51,7 @@ public abstract class AbstractHazelcastStore<E extends Event, D extends StoreDel ...@@ -50,7 +51,7 @@ public abstract class AbstractHazelcastStore<E extends Event, D extends StoreDel
50 * @return serialized object 51 * @return serialized object
51 */ 52 */
52 protected byte[] serialize(Object obj) { 53 protected byte[] serialize(Object obj) {
53 - return kryoSerializationService.serialize(obj); 54 + return serializer.encode(obj);
54 } 55 }
55 56
56 /** 57 /**
...@@ -61,7 +62,7 @@ public abstract class AbstractHazelcastStore<E extends Event, D extends StoreDel ...@@ -61,7 +62,7 @@ public abstract class AbstractHazelcastStore<E extends Event, D extends StoreDel
61 * @return deserialized object 62 * @return deserialized object
62 */ 63 */
63 protected <T> T deserialize(byte[] bytes) { 64 protected <T> T deserialize(byte[] bytes) {
64 - return kryoSerializationService.deserialize(bytes); 65 + return serializer.decode(bytes);
65 } 66 }
66 67
67 68
......
...@@ -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.KryoSerializationService; 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,28 +18,28 @@ import com.hazelcast.core.IMap; ...@@ -18,28 +18,28 @@ 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 KryoSerializationService kryoSerializationService; 21 + private final StoreSerializer serializer;
22 private IMap<byte[], byte[]> rawMap; 22 private IMap<byte[], byte[]> rawMap;
23 23
24 /** 24 /**
25 * Constructor. 25 * Constructor.
26 * 26 *
27 - * @param kryoSerializationService 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(KryoSerializationService kryoSerializationService, IMap<byte[], byte[]> rawMap) { 30 + public OptionalCacheLoader(StoreSerializer serializer, IMap<byte[], byte[]> rawMap) {
31 - this.kryoSerializationService = checkNotNull(kryoSerializationService); 31 + this.serializer = checkNotNull(serializer);
32 this.rawMap = checkNotNull(rawMap); 32 this.rawMap = checkNotNull(rawMap);
33 } 33 }
34 34
35 @Override 35 @Override
36 public Optional<V> load(K key) throws Exception { 36 public Optional<V> load(K key) throws Exception {
37 - byte[] keyBytes = kryoSerializationService.serialize(key); 37 + byte[] keyBytes = serializer.encode(key);
38 byte[] valBytes = rawMap.get(keyBytes); 38 byte[] valBytes = rawMap.get(keyBytes);
39 if (valBytes == null) { 39 if (valBytes == null) {
40 return Optional.absent(); 40 return Optional.absent();
41 } 41 }
42 - V dev = kryoSerializationService.deserialize(valBytes); 42 + V dev = serializer.decode(valBytes);
43 return Optional.of(dev); 43 return Optional.of(dev);
44 } 44 }
45 } 45 }
......
...@@ -47,6 +47,7 @@ import static com.google.common.cache.CacheBuilder.newBuilder; ...@@ -47,6 +47,7 @@ import static com.google.common.cache.CacheBuilder.newBuilder;
47 import static org.onlab.onos.net.device.DeviceEvent.Type.*; 47 import static org.onlab.onos.net.device.DeviceEvent.Type.*;
48 import static org.slf4j.LoggerFactory.getLogger; 48 import static org.slf4j.LoggerFactory.getLogger;
49 49
50 +//TODO: Add support for multiple provider and annotations
50 /** 51 /**
51 * Manages inventory of infrastructure devices using Hazelcast-backed map. 52 * Manages inventory of infrastructure devices using Hazelcast-backed map.
52 */ 53 */
...@@ -87,7 +88,7 @@ public class DistributedDeviceStore ...@@ -87,7 +88,7 @@ public class DistributedDeviceStore
87 // TODO decide on Map name scheme to avoid collision 88 // TODO decide on Map name scheme to avoid collision
88 rawDevices = theInstance.getMap("devices"); 89 rawDevices = theInstance.getMap("devices");
89 final OptionalCacheLoader<DeviceId, DefaultDevice> deviceLoader 90 final OptionalCacheLoader<DeviceId, DefaultDevice> deviceLoader
90 - = new OptionalCacheLoader<>(kryoSerializationService, rawDevices); 91 + = new OptionalCacheLoader<>(serializer, rawDevices);
91 devices = new AbsentInvalidatingLoadingCache<>(newBuilder().build(deviceLoader)); 92 devices = new AbsentInvalidatingLoadingCache<>(newBuilder().build(deviceLoader));
92 // refresh/populate cache based on notification from other instance 93 // refresh/populate cache based on notification from other instance
93 devicesListener = rawDevices.addEntryListener(new RemoteDeviceEventHandler(devices), includeValue); 94 devicesListener = rawDevices.addEntryListener(new RemoteDeviceEventHandler(devices), includeValue);
...@@ -97,7 +98,7 @@ public class DistributedDeviceStore ...@@ -97,7 +98,7 @@ public class DistributedDeviceStore
97 98
98 rawDevicePorts = theInstance.getMap("devicePorts"); 99 rawDevicePorts = theInstance.getMap("devicePorts");
99 final OptionalCacheLoader<DeviceId, Map<PortNumber, Port>> devicePortLoader 100 final OptionalCacheLoader<DeviceId, Map<PortNumber, Port>> devicePortLoader
100 - = new OptionalCacheLoader<>(kryoSerializationService, rawDevicePorts); 101 + = new OptionalCacheLoader<>(serializer, rawDevicePorts);
101 devicePorts = new AbsentInvalidatingLoadingCache<>(newBuilder().build(devicePortLoader)); 102 devicePorts = new AbsentInvalidatingLoadingCache<>(newBuilder().build(devicePortLoader));
102 // refresh/populate cache based on notification from other instance 103 // refresh/populate cache based on notification from other instance
103 portsListener = rawDevicePorts.addEntryListener(new RemotePortEventHandler(devicePorts), includeValue); 104 portsListener = rawDevicePorts.addEntryListener(new RemotePortEventHandler(devicePorts), includeValue);
......
...@@ -4,27 +4,15 @@ import org.apache.felix.scr.annotations.Component; ...@@ -4,27 +4,15 @@ import org.apache.felix.scr.annotations.Component;
4 import org.apache.felix.scr.annotations.Service; 4 import org.apache.felix.scr.annotations.Service;
5 import org.onlab.onos.cluster.MastershipTerm; 5 import org.onlab.onos.cluster.MastershipTerm;
6 import org.onlab.onos.net.DeviceId; 6 import org.onlab.onos.net.DeviceId;
7 -import org.onlab.onos.store.ClockService; 7 +import org.onlab.onos.store.ClockProviderService;
8 -import org.onlab.onos.store.Timestamp;
9 8
10 // FIXME: Code clone in onos-core-trivial, onos-core-hz-net 9 // FIXME: Code clone in onos-core-trivial, onos-core-hz-net
11 /** 10 /**
12 - * Dummy implementation of {@link ClockService}. 11 + * Dummy implementation of {@link ClockProviderService}.
13 */ 12 */
14 @Component(immediate = true) 13 @Component(immediate = true)
15 @Service 14 @Service
16 -public class NoOpClockService implements ClockService { 15 +public class NoOpClockProviderService implements ClockProviderService {
17 -
18 - @Override
19 - public Timestamp getTimestamp(DeviceId deviceId) {
20 - return new Timestamp() {
21 -
22 - @Override
23 - public int compareTo(Timestamp o) {
24 - throw new IllegalStateException("Never expected to be used.");
25 - }
26 - };
27 - }
28 16
29 @Override 17 @Override
30 public void setMastershipTerm(DeviceId deviceId, MastershipTerm term) { 18 public void setMastershipTerm(DeviceId deviceId, MastershipTerm term) {
......
1 package org.onlab.onos.store.flow.impl; 1 package org.onlab.onos.store.flow.impl;
2 2
3 -import static org.onlab.onos.net.flow.FlowRuleEvent.Type.RULE_ADDED;
4 import static org.onlab.onos.net.flow.FlowRuleEvent.Type.RULE_REMOVED; 3 import static org.onlab.onos.net.flow.FlowRuleEvent.Type.RULE_REMOVED;
5 import static org.slf4j.LoggerFactory.getLogger; 4 import static org.slf4j.LoggerFactory.getLogger;
6 5
...@@ -13,9 +12,10 @@ import org.apache.felix.scr.annotations.Deactivate; ...@@ -13,9 +12,10 @@ import org.apache.felix.scr.annotations.Deactivate;
13 import org.apache.felix.scr.annotations.Service; 12 import org.apache.felix.scr.annotations.Service;
14 import org.onlab.onos.ApplicationId; 13 import org.onlab.onos.ApplicationId;
15 import org.onlab.onos.net.DeviceId; 14 import org.onlab.onos.net.DeviceId;
16 -import org.onlab.onos.net.flow.DefaultFlowRule; 15 +import org.onlab.onos.net.flow.DefaultFlowEntry;
16 +import org.onlab.onos.net.flow.FlowEntry;
17 +import org.onlab.onos.net.flow.FlowEntry.FlowEntryState;
17 import org.onlab.onos.net.flow.FlowRule; 18 import org.onlab.onos.net.flow.FlowRule;
18 -import org.onlab.onos.net.flow.FlowRule.FlowRuleState;
19 import org.onlab.onos.net.flow.FlowRuleEvent; 19 import org.onlab.onos.net.flow.FlowRuleEvent;
20 import org.onlab.onos.net.flow.FlowRuleEvent.Type; 20 import org.onlab.onos.net.flow.FlowRuleEvent.Type;
21 import org.onlab.onos.net.flow.FlowRuleStore; 21 import org.onlab.onos.net.flow.FlowRuleStore;
...@@ -28,20 +28,20 @@ import com.google.common.collect.ImmutableSet; ...@@ -28,20 +28,20 @@ import com.google.common.collect.ImmutableSet;
28 import com.google.common.collect.Multimap; 28 import com.google.common.collect.Multimap;
29 29
30 /** 30 /**
31 - * TEMPORARY: Manages inventory of flow rules using distributed store implementation. 31 + * Manages inventory of flow rules using trivial in-memory implementation.
32 */ 32 */
33 -//FIXME: I LIE I AM NOT DISTRIBUTED 33 +//FIXME I LIE. I AIN'T DISTRIBUTED
34 @Component(immediate = true) 34 @Component(immediate = true)
35 @Service 35 @Service
36 public class DistributedFlowRuleStore 36 public class DistributedFlowRuleStore
37 -extends AbstractStore<FlowRuleEvent, FlowRuleStoreDelegate> 37 + extends AbstractStore<FlowRuleEvent, FlowRuleStoreDelegate>
38 -implements FlowRuleStore { 38 + implements FlowRuleStore {
39 39
40 private final Logger log = getLogger(getClass()); 40 private final Logger log = getLogger(getClass());
41 41
42 // store entries as a pile of rules, no info about device tables 42 // store entries as a pile of rules, no info about device tables
43 - private final Multimap<DeviceId, FlowRule> flowEntries = 43 + private final Multimap<DeviceId, FlowEntry> flowEntries =
44 - ArrayListMultimap.<DeviceId, FlowRule>create(); 44 + ArrayListMultimap.<DeviceId, FlowEntry>create();
45 45
46 private final Multimap<ApplicationId, FlowRule> flowEntriesById = 46 private final Multimap<ApplicationId, FlowRule> flowEntriesById =
47 ArrayListMultimap.<ApplicationId, FlowRule>create(); 47 ArrayListMultimap.<ApplicationId, FlowRule>create();
...@@ -58,8 +58,13 @@ implements FlowRuleStore { ...@@ -58,8 +58,13 @@ implements FlowRuleStore {
58 58
59 59
60 @Override 60 @Override
61 - public synchronized FlowRule getFlowRule(FlowRule rule) { 61 + public int getFlowRuleCount() {
62 - for (FlowRule f : flowEntries.get(rule.deviceId())) { 62 + return flowEntries.size();
63 + }
64 +
65 + @Override
66 + public synchronized FlowEntry getFlowEntry(FlowRule rule) {
67 + for (FlowEntry f : flowEntries.get(rule.deviceId())) {
63 if (f.equals(rule)) { 68 if (f.equals(rule)) {
64 return f; 69 return f;
65 } 70 }
...@@ -68,8 +73,8 @@ implements FlowRuleStore { ...@@ -68,8 +73,8 @@ implements FlowRuleStore {
68 } 73 }
69 74
70 @Override 75 @Override
71 - public synchronized Iterable<FlowRule> getFlowEntries(DeviceId deviceId) { 76 + public synchronized Iterable<FlowEntry> getFlowEntries(DeviceId deviceId) {
72 - Collection<FlowRule> rules = flowEntries.get(deviceId); 77 + Collection<FlowEntry> rules = flowEntries.get(deviceId);
73 if (rules == null) { 78 if (rules == null) {
74 return Collections.emptyList(); 79 return Collections.emptyList();
75 } 80 }
...@@ -77,7 +82,7 @@ implements FlowRuleStore { ...@@ -77,7 +82,7 @@ implements FlowRuleStore {
77 } 82 }
78 83
79 @Override 84 @Override
80 - public synchronized Iterable<FlowRule> getFlowEntriesByAppId(ApplicationId appId) { 85 + public synchronized Iterable<FlowRule> getFlowRulesByAppId(ApplicationId appId) {
81 Collection<FlowRule> rules = flowEntriesById.get(appId); 86 Collection<FlowRule> rules = flowEntriesById.get(appId);
82 if (rules == null) { 87 if (rules == null) {
83 return Collections.emptyList(); 88 return Collections.emptyList();
...@@ -87,7 +92,7 @@ implements FlowRuleStore { ...@@ -87,7 +92,7 @@ implements FlowRuleStore {
87 92
88 @Override 93 @Override
89 public synchronized void storeFlowRule(FlowRule rule) { 94 public synchronized void storeFlowRule(FlowRule rule) {
90 - FlowRule f = new DefaultFlowRule(rule, FlowRuleState.PENDING_ADD); 95 + FlowEntry f = new DefaultFlowEntry(rule);
91 DeviceId did = f.deviceId(); 96 DeviceId did = f.deviceId();
92 if (!flowEntries.containsEntry(did, f)) { 97 if (!flowEntries.containsEntry(did, f)) {
93 flowEntries.put(did, f); 98 flowEntries.put(did, f);
...@@ -97,57 +102,41 @@ implements FlowRuleStore { ...@@ -97,57 +102,41 @@ implements FlowRuleStore {
97 102
98 @Override 103 @Override
99 public synchronized void deleteFlowRule(FlowRule rule) { 104 public synchronized void deleteFlowRule(FlowRule rule) {
100 - FlowRule f = new DefaultFlowRule(rule, FlowRuleState.PENDING_REMOVE); 105 + FlowEntry entry = getFlowEntry(rule);
101 - DeviceId did = f.deviceId(); 106 + if (entry == null) {
102 - 107 + return;
103 - /*
104 - * find the rule and mark it for deletion.
105 - * Ultimately a flow removed will come remove it.
106 - */
107 -
108 - if (flowEntries.containsEntry(did, f)) {
109 - //synchronized (flowEntries) {
110 - flowEntries.remove(did, f);
111 - flowEntries.put(did, f);
112 - flowEntriesById.remove(rule.appId(), rule);
113 - //}
114 } 108 }
109 + entry.setState(FlowEntryState.PENDING_REMOVE);
115 } 110 }
116 111
117 @Override 112 @Override
118 - public synchronized FlowRuleEvent addOrUpdateFlowRule(FlowRule rule) { 113 + public synchronized FlowRuleEvent addOrUpdateFlowRule(FlowEntry rule) {
119 DeviceId did = rule.deviceId(); 114 DeviceId did = rule.deviceId();
120 115
121 // check if this new rule is an update to an existing entry 116 // check if this new rule is an update to an existing entry
122 - if (flowEntries.containsEntry(did, rule)) { 117 + FlowEntry stored = getFlowEntry(rule);
123 - //synchronized (flowEntries) { 118 + if (stored != null) {
124 - // Multimaps support duplicates so we have to remove our rule 119 + stored.setBytes(rule.bytes());
125 - // and replace it with the current version. 120 + stored.setLife(rule.life());
126 - flowEntries.remove(did, rule); 121 + stored.setPackets(rule.packets());
127 - flowEntries.put(did, rule); 122 + if (stored.state() == FlowEntryState.PENDING_ADD) {
128 - //} 123 + stored.setState(FlowEntryState.ADDED);
124 + return new FlowRuleEvent(Type.RULE_ADDED, rule);
125 + }
129 return new FlowRuleEvent(Type.RULE_UPDATED, rule); 126 return new FlowRuleEvent(Type.RULE_UPDATED, rule);
130 } 127 }
131 128
132 flowEntries.put(did, rule); 129 flowEntries.put(did, rule);
133 - return new FlowRuleEvent(RULE_ADDED, rule); 130 + return null;
134 } 131 }
135 132
136 @Override 133 @Override
137 - public synchronized FlowRuleEvent removeFlowRule(FlowRule rule) { 134 + public synchronized FlowRuleEvent removeFlowRule(FlowEntry rule) {
138 - //synchronized (this) { 135 + // This is where one could mark a rule as removed and still keep it in the store.
139 if (flowEntries.remove(rule.deviceId(), rule)) { 136 if (flowEntries.remove(rule.deviceId(), rule)) {
140 return new FlowRuleEvent(RULE_REMOVED, rule); 137 return new FlowRuleEvent(RULE_REMOVED, rule);
141 } else { 138 } else {
142 return null; 139 return null;
143 } 140 }
144 - //}
145 } 141 }
146 -
147 -
148 -
149 -
150 -
151 -
152 -
153 } 142 }
......
...@@ -38,6 +38,7 @@ import com.google.common.collect.Multimap; ...@@ -38,6 +38,7 @@ import com.google.common.collect.Multimap;
38 import com.google.common.collect.ImmutableSet.Builder; 38 import com.google.common.collect.ImmutableSet.Builder;
39 import com.hazelcast.core.IMap; 39 import com.hazelcast.core.IMap;
40 40
41 +//TODO: Add support for multiple provider and annotations
41 /** 42 /**
42 * Manages inventory of infrastructure links using Hazelcast-backed map. 43 * Manages inventory of infrastructure links using Hazelcast-backed map.
43 */ 44 */
...@@ -70,7 +71,7 @@ public class DistributedLinkStore ...@@ -70,7 +71,7 @@ public class DistributedLinkStore
70 // TODO decide on Map name scheme to avoid collision 71 // TODO decide on Map name scheme to avoid collision
71 rawLinks = theInstance.getMap("links"); 72 rawLinks = theInstance.getMap("links");
72 final OptionalCacheLoader<LinkKey, DefaultLink> linkLoader 73 final OptionalCacheLoader<LinkKey, DefaultLink> linkLoader
73 - = new OptionalCacheLoader<>(kryoSerializationService, rawLinks); 74 + = new OptionalCacheLoader<>(serializer, rawLinks);
74 links = new AbsentInvalidatingLoadingCache<>(newBuilder().build(linkLoader)); 75 links = new AbsentInvalidatingLoadingCache<>(newBuilder().build(linkLoader));
75 // refresh/populate cache based on notification from other instance 76 // refresh/populate cache based on notification from other instance
76 linksListener = rawLinks.addEntryListener(new RemoteLinkEventHandler(links), includeValue); 77 linksListener = rawLinks.addEntryListener(new RemoteLinkEventHandler(links), includeValue);
......
...@@ -125,7 +125,8 @@ implements TopologyStore { ...@@ -125,7 +125,8 @@ implements TopologyStore {
125 // Promote the new topology to current and return a ready-to-send event. 125 // Promote the new topology to current and return a ready-to-send event.
126 synchronized (this) { 126 synchronized (this) {
127 current = newTopology; 127 current = newTopology;
128 - return new TopologyEvent(TopologyEvent.Type.TOPOLOGY_CHANGED, current); 128 + return new TopologyEvent(TopologyEvent.Type.TOPOLOGY_CHANGED,
129 + current, reasons);
129 } 130 }
130 } 131 }
131 132
......
...@@ -36,9 +36,6 @@ import org.onlab.onos.net.provider.ProviderId; ...@@ -36,9 +36,6 @@ import org.onlab.onos.net.provider.ProviderId;
36 import org.onlab.onos.store.common.StoreManager; 36 import org.onlab.onos.store.common.StoreManager;
37 import org.onlab.onos.store.common.StoreService; 37 import org.onlab.onos.store.common.StoreService;
38 import org.onlab.onos.store.common.TestStoreManager; 38 import org.onlab.onos.store.common.TestStoreManager;
39 -import org.onlab.onos.store.serializers.KryoSerializationManager;
40 -import org.onlab.onos.store.serializers.KryoSerializationService;
41 -
42 import com.google.common.collect.Iterables; 39 import com.google.common.collect.Iterables;
43 import com.google.common.collect.Sets; 40 import com.google.common.collect.Sets;
44 import com.hazelcast.config.Config; 41 import com.hazelcast.config.Config;
...@@ -63,7 +60,6 @@ public class DistributedDeviceStoreTest { ...@@ -63,7 +60,6 @@ public class DistributedDeviceStoreTest {
63 private static final PortNumber P3 = PortNumber.portNumber(3); 60 private static final PortNumber P3 = PortNumber.portNumber(3);
64 61
65 private DistributedDeviceStore deviceStore; 62 private DistributedDeviceStore deviceStore;
66 - private KryoSerializationManager serializationMgr;
67 63
68 private StoreManager storeManager; 64 private StoreManager storeManager;
69 65
...@@ -85,10 +81,7 @@ public class DistributedDeviceStoreTest { ...@@ -85,10 +81,7 @@ public class DistributedDeviceStoreTest {
85 storeManager = new TestStoreManager(Hazelcast.newHazelcastInstance(config)); 81 storeManager = new TestStoreManager(Hazelcast.newHazelcastInstance(config));
86 storeManager.activate(); 82 storeManager.activate();
87 83
88 - serializationMgr = new KryoSerializationManager(); 84 + deviceStore = new TestDistributedDeviceStore(storeManager);
89 - serializationMgr.activate();
90 -
91 - deviceStore = new TestDistributedDeviceStore(storeManager, serializationMgr);
92 deviceStore.activate(); 85 deviceStore.activate();
93 } 86 }
94 87
...@@ -96,8 +89,6 @@ public class DistributedDeviceStoreTest { ...@@ -96,8 +89,6 @@ public class DistributedDeviceStoreTest {
96 public void tearDown() throws Exception { 89 public void tearDown() throws Exception {
97 deviceStore.deactivate(); 90 deviceStore.deactivate();
98 91
99 - serializationMgr.deactivate();
100 -
101 storeManager.deactivate(); 92 storeManager.deactivate();
102 } 93 }
103 94
...@@ -392,10 +383,8 @@ public class DistributedDeviceStoreTest { ...@@ -392,10 +383,8 @@ public class DistributedDeviceStoreTest {
392 } 383 }
393 384
394 private class TestDistributedDeviceStore extends DistributedDeviceStore { 385 private class TestDistributedDeviceStore extends DistributedDeviceStore {
395 - public TestDistributedDeviceStore(StoreService storeService, 386 + public TestDistributedDeviceStore(StoreService storeService) {
396 - KryoSerializationService kryoSerializationService) {
397 this.storeService = storeService; 387 this.storeService = storeService;
398 - this.kryoSerializationService = kryoSerializationService;
399 } 388 }
400 } 389 }
401 } 390 }
......
...@@ -30,9 +30,6 @@ import org.onlab.onos.net.provider.ProviderId; ...@@ -30,9 +30,6 @@ import org.onlab.onos.net.provider.ProviderId;
30 import org.onlab.onos.store.common.StoreManager; 30 import org.onlab.onos.store.common.StoreManager;
31 import org.onlab.onos.store.common.StoreService; 31 import org.onlab.onos.store.common.StoreService;
32 import org.onlab.onos.store.common.TestStoreManager; 32 import org.onlab.onos.store.common.TestStoreManager;
33 -import org.onlab.onos.store.serializers.KryoSerializationManager;
34 -import org.onlab.onos.store.serializers.KryoSerializationService;
35 -
36 import com.google.common.collect.Iterables; 33 import com.google.common.collect.Iterables;
37 import com.hazelcast.config.Config; 34 import com.hazelcast.config.Config;
38 import com.hazelcast.core.Hazelcast; 35 import com.hazelcast.core.Hazelcast;
...@@ -51,7 +48,6 @@ public class DistributedLinkStoreTest { ...@@ -51,7 +48,6 @@ public class DistributedLinkStoreTest {
51 private static final PortNumber P3 = PortNumber.portNumber(3); 48 private static final PortNumber P3 = PortNumber.portNumber(3);
52 49
53 private StoreManager storeManager; 50 private StoreManager storeManager;
54 - private KryoSerializationManager serializationMgr;
55 51
56 private DistributedLinkStore linkStore; 52 private DistributedLinkStore linkStore;
57 53
...@@ -71,17 +67,13 @@ public class DistributedLinkStoreTest { ...@@ -71,17 +67,13 @@ public class DistributedLinkStoreTest {
71 storeManager = new TestStoreManager(Hazelcast.newHazelcastInstance(config)); 67 storeManager = new TestStoreManager(Hazelcast.newHazelcastInstance(config));
72 storeManager.activate(); 68 storeManager.activate();
73 69
74 - serializationMgr = new KryoSerializationManager(); 70 + linkStore = new TestDistributedLinkStore(storeManager);
75 - serializationMgr.activate();
76 -
77 - linkStore = new TestDistributedLinkStore(storeManager, serializationMgr);
78 linkStore.activate(); 71 linkStore.activate();
79 } 72 }
80 73
81 @After 74 @After
82 public void tearDown() throws Exception { 75 public void tearDown() throws Exception {
83 linkStore.deactivate(); 76 linkStore.deactivate();
84 - serializationMgr.deactivate();
85 storeManager.deactivate(); 77 storeManager.deactivate();
86 } 78 }
87 79
...@@ -361,10 +353,8 @@ public class DistributedLinkStoreTest { ...@@ -361,10 +353,8 @@ public class DistributedLinkStoreTest {
361 353
362 354
363 class TestDistributedLinkStore extends DistributedLinkStore { 355 class TestDistributedLinkStore extends DistributedLinkStore {
364 - TestDistributedLinkStore(StoreService storeService, 356 + TestDistributedLinkStore(StoreService storeService) {
365 - KryoSerializationService kryoSerializationService) {
366 this.storeService = storeService; 357 this.storeService = storeService;
367 - this.kryoSerializationService = kryoSerializationService;
368 } 358 }
369 } 359 }
370 } 360 }
......
...@@ -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.net.ConnectPoint; 3 import org.onlab.onos.net.ConnectPoint;
4 import org.onlab.onos.net.ElementId; 4 import org.onlab.onos.net.ElementId;
5 import org.onlab.onos.net.PortNumber; 5 import org.onlab.onos.net.PortNumber;
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;
...@@ -15,7 +14,7 @@ import com.esotericsoftware.kryo.io.Output; ...@@ -15,7 +14,7 @@ import com.esotericsoftware.kryo.io.Output;
15 public class ConnectPointSerializer extends Serializer<ConnectPoint> { 14 public class ConnectPointSerializer extends Serializer<ConnectPoint> {
16 15
17 /** 16 /**
18 - * Default constructor. 17 + * Creates {@link ConnectPointSerializer} serializer instance.
19 */ 18 */
20 public ConnectPointSerializer() { 19 public ConnectPointSerializer() {
21 // non-null, immutable 20 // non-null, immutable
......
...@@ -16,7 +16,7 @@ import com.esotericsoftware.kryo.io.Output; ...@@ -16,7 +16,7 @@ import com.esotericsoftware.kryo.io.Output;
16 public class DefaultLinkSerializer extends Serializer<DefaultLink> { 16 public class DefaultLinkSerializer extends Serializer<DefaultLink> {
17 17
18 /** 18 /**
19 - * Default constructor. 19 + * Creates {@link DefaultLink} serializer instance.
20 */ 20 */
21 public DefaultLinkSerializer() { 21 public DefaultLinkSerializer() {
22 // non-null, immutable 22 // non-null, immutable
......
...@@ -16,7 +16,7 @@ public final class DefaultPortSerializer extends ...@@ -16,7 +16,7 @@ public final class DefaultPortSerializer extends
16 Serializer<DefaultPort> { 16 Serializer<DefaultPort> {
17 17
18 /** 18 /**
19 - * Default constructor. 19 + * Creates {@link DefaultPort} serializer instance.
20 */ 20 */
21 public DefaultPortSerializer() { 21 public DefaultPortSerializer() {
22 // non-null, immutable 22 // non-null, immutable
......
...@@ -14,6 +14,14 @@ import com.esotericsoftware.kryo.io.Output; ...@@ -14,6 +14,14 @@ import com.esotericsoftware.kryo.io.Output;
14 */ 14 */
15 public final class DeviceIdSerializer extends Serializer<DeviceId> { 15 public final class DeviceIdSerializer extends Serializer<DeviceId> {
16 16
17 + /**
18 + * Creates {@link DeviceId} serializer instance.
19 + */
20 + public DeviceIdSerializer() {
21 + // non-null, immutable
22 + super(false, true);
23 + }
24 +
17 @Override 25 @Override
18 public void write(Kryo kryo, Output output, DeviceId object) { 26 public void write(Kryo kryo, Output output, DeviceId object) {
19 kryo.writeObject(output, object.uri()); 27 kryo.writeObject(output, object.uri());
......
...@@ -19,6 +19,9 @@ public class ImmutableMapSerializer extends FamilySerializer<ImmutableMap<?, ?>> ...@@ -19,6 +19,9 @@ public class ImmutableMapSerializer extends FamilySerializer<ImmutableMap<?, ?>>
19 19
20 private final MapSerializer mapSerializer = new MapSerializer(); 20 private final MapSerializer mapSerializer = new MapSerializer();
21 21
22 + /**
23 + * Creates {@link ImmutableMap} serializer instance.
24 + */
22 public ImmutableMapSerializer() { 25 public ImmutableMapSerializer() {
23 // non-null, immutable 26 // non-null, immutable
24 super(false, true); 27 super(false, true);
......
...@@ -18,6 +18,9 @@ public class ImmutableSetSerializer extends FamilySerializer<ImmutableSet<?>> { ...@@ -18,6 +18,9 @@ public class ImmutableSetSerializer extends FamilySerializer<ImmutableSet<?>> {
18 18
19 private final CollectionSerializer serializer = new CollectionSerializer(); 19 private final CollectionSerializer serializer = new CollectionSerializer();
20 20
21 + /**
22 + * Creates {@link ImmutableSet} serializer instance.
23 + */
21 public ImmutableSetSerializer() { 24 public ImmutableSetSerializer() {
22 // non-null, immutable 25 // non-null, immutable
23 super(false, true); 26 super(false, true);
......
1 +package org.onlab.onos.store.serializers;
2 +
3 +import org.onlab.packet.IpAddress;
4 +import com.esotericsoftware.kryo.Kryo;
5 +import com.esotericsoftware.kryo.Serializer;
6 +import com.esotericsoftware.kryo.io.Input;
7 +import com.esotericsoftware.kryo.io.Output;
8 +
9 +/**
10 + * Kryo Serializer for {@link IpAddress}.
11 + */
12 +public class IpAddressSerializer extends Serializer<IpAddress> {
13 +
14 + /**
15 + * Creates {@link IpAddress} serializer instance.
16 + */
17 + public IpAddressSerializer() {
18 + // non-null, immutable
19 + super(false, true);
20 + }
21 +
22 + @Override
23 + public void write(Kryo kryo, Output output,
24 + IpAddress object) {
25 + byte[] octs = object.toOctets();
26 + output.writeInt(octs.length);
27 + output.writeBytes(octs);
28 + output.writeInt(object.prefixLength());
29 + }
30 +
31 + @Override
32 + public IpAddress read(Kryo kryo, Input input,
33 + Class<IpAddress> type) {
34 + int octLen = input.readInt();
35 + byte[] octs = new byte[octLen];
36 + input.read(octs);
37 + int prefLen = input.readInt();
38 + return IpAddress.valueOf(octs, prefLen);
39 + }
40 +
41 +}
...@@ -13,7 +13,7 @@ import com.esotericsoftware.kryo.io.Output; ...@@ -13,7 +13,7 @@ import com.esotericsoftware.kryo.io.Output;
13 public final class IpPrefixSerializer extends Serializer<IpPrefix> { 13 public final class IpPrefixSerializer extends Serializer<IpPrefix> {
14 14
15 /** 15 /**
16 - * Default constructor. 16 + * Creates {@link IpPrefix} serializer instance.
17 */ 17 */
18 public IpPrefixSerializer() { 18 public IpPrefixSerializer() {
19 // non-null, immutable 19 // non-null, immutable
......
1 package org.onlab.onos.store.serializers; 1 package org.onlab.onos.store.serializers;
2 2
3 -import de.javakaffee.kryoserializers.URISerializer; 3 +import java.net.URI;
4 -import org.apache.felix.scr.annotations.Activate; 4 +import java.util.ArrayList;
5 -import org.apache.felix.scr.annotations.Component; 5 +import java.util.Arrays;
6 -import org.apache.felix.scr.annotations.Deactivate; 6 +import java.util.HashMap;
7 -import org.apache.felix.scr.annotations.Service; 7 +
8 import org.onlab.onos.cluster.ControllerNode; 8 import org.onlab.onos.cluster.ControllerNode;
9 import org.onlab.onos.cluster.DefaultControllerNode; 9 import org.onlab.onos.cluster.DefaultControllerNode;
10 +import org.onlab.onos.cluster.MastershipTerm;
10 import org.onlab.onos.cluster.NodeId; 11 import org.onlab.onos.cluster.NodeId;
11 import org.onlab.onos.net.ConnectPoint; 12 import org.onlab.onos.net.ConnectPoint;
12 import org.onlab.onos.net.DefaultAnnotations; 13 import org.onlab.onos.net.DefaultAnnotations;
...@@ -21,61 +22,49 @@ import org.onlab.onos.net.LinkKey; ...@@ -21,61 +22,49 @@ import org.onlab.onos.net.LinkKey;
21 import org.onlab.onos.net.MastershipRole; 22 import org.onlab.onos.net.MastershipRole;
22 import org.onlab.onos.net.Port; 23 import org.onlab.onos.net.Port;
23 import org.onlab.onos.net.PortNumber; 24 import org.onlab.onos.net.PortNumber;
25 +import org.onlab.onos.net.device.DefaultDeviceDescription;
26 +import org.onlab.onos.net.device.DefaultPortDescription;
24 import org.onlab.onos.net.provider.ProviderId; 27 import org.onlab.onos.net.provider.ProviderId;
28 +import org.onlab.packet.IpAddress;
25 import org.onlab.packet.IpPrefix; 29 import org.onlab.packet.IpPrefix;
26 import org.onlab.util.KryoPool; 30 import org.onlab.util.KryoPool;
27 -import org.slf4j.Logger;
28 -import org.slf4j.LoggerFactory;
29 -
30 -import java.net.URI;
31 -import java.nio.ByteBuffer;
32 -import java.util.ArrayList;
33 -import java.util.HashMap;
34 -
35 -/**
36 - * Serialization service using Kryo.
37 - */
38 -@Component(immediate = true)
39 -@Service
40 -public class KryoSerializationManager implements KryoSerializationService {
41 -
42 - private final Logger log = LoggerFactory.getLogger(getClass());
43 - private KryoPool serializerPool;
44 31
32 +import de.javakaffee.kryoserializers.URISerializer;
45 33
46 - @Activate 34 +public final class KryoPoolUtil {
47 - public void activate() {
48 - setupKryoPool();
49 - log.info("Started");
50 - }
51 35
52 - @Deactivate 36 + /**
53 - public void deactivate() { 37 + * KryoPool which can serialize ON.lab misc classes.
54 - log.info("Stopped"); 38 + */
55 - } 39 + public static final KryoPool MISC = KryoPool.newBuilder()
40 + .register(IpPrefix.class, new IpPrefixSerializer())
41 + .register(IpAddress.class, new IpAddressSerializer())
42 + .build();
56 43
44 + // TODO: Populate other classes
57 /** 45 /**
58 - * Sets up the common serialzers pool. 46 + * KryoPool which can serialize API bundle classes.
59 */ 47 */
60 - protected void setupKryoPool() { 48 + public static final KryoPool API = KryoPool.newBuilder()
61 - // FIXME Slice out types used in common to separate pool/namespace. 49 + .register(MISC)
62 - serializerPool = KryoPool.newBuilder() 50 + .register(
63 - .register(ArrayList.class, 51 + //
52 + ArrayList.class,
53 + Arrays.asList().getClass(),
64 HashMap.class, 54 HashMap.class,
65 - 55 + //
66 ControllerNode.State.class, 56 ControllerNode.State.class,
67 Device.Type.class, 57 Device.Type.class,
68 -
69 DefaultAnnotations.class, 58 DefaultAnnotations.class,
70 DefaultControllerNode.class, 59 DefaultControllerNode.class,
71 DefaultDevice.class, 60 DefaultDevice.class,
61 + DefaultDeviceDescription.class,
72 MastershipRole.class, 62 MastershipRole.class,
73 Port.class, 63 Port.class,
64 + DefaultPortDescription.class,
74 Element.class, 65 Element.class,
75 -
76 Link.Type.class 66 Link.Type.class
77 ) 67 )
78 - .register(IpPrefix.class, new IpPrefixSerializer())
79 .register(URI.class, new URISerializer()) 68 .register(URI.class, new URISerializer())
80 .register(NodeId.class, new NodeIdSerializer()) 69 .register(NodeId.class, new NodeIdSerializer())
81 .register(ProviderId.class, new ProviderIdSerializer()) 70 .register(ProviderId.class, new ProviderIdSerializer())
...@@ -85,31 +74,12 @@ public class KryoSerializationManager implements KryoSerializationService { ...@@ -85,31 +74,12 @@ public class KryoSerializationManager implements KryoSerializationService {
85 .register(LinkKey.class, new LinkKeySerializer()) 74 .register(LinkKey.class, new LinkKeySerializer())
86 .register(ConnectPoint.class, new ConnectPointSerializer()) 75 .register(ConnectPoint.class, new ConnectPointSerializer())
87 .register(DefaultLink.class, new DefaultLinkSerializer()) 76 .register(DefaultLink.class, new DefaultLinkSerializer())
88 - .build() 77 + .register(MastershipTerm.class, new MastershipTermSerializer())
89 - .populate(1); 78 + .register(MastershipRole.class, new MastershipRoleSerializer())
90 - }
91 -
92 - @Override
93 - public byte[] serialize(final Object obj) {
94 - return serializerPool.serialize(obj);
95 - }
96 -
97 - @Override
98 - public <T> T deserialize(final byte[] bytes) {
99 - if (bytes == null) {
100 - return null;
101 - }
102 - return serializerPool.deserialize(bytes);
103 - }
104 79
105 - @Override 80 + .build();
106 - public void serialize(Object obj, ByteBuffer buffer) {
107 - serializerPool.serialize(obj, buffer);
108 - }
109 81
110 - @Override
111 - public <T> T deserialize(ByteBuffer buffer) {
112 - return serializerPool.deserialize(buffer);
113 - }
114 82
83 + // not to be instantiated
84 + private KryoPoolUtil() {}
115 } 85 }
......
1 +package org.onlab.onos.store.serializers;
2 +
3 +import org.onlab.util.KryoPool;
4 +import org.slf4j.Logger;
5 +import org.slf4j.LoggerFactory;
6 +
7 +import java.nio.ByteBuffer;
8 +
9 +/**
10 + * StoreSerializer implementation using Kryo.
11 + */
12 +public class KryoSerializer implements StoreSerializer {
13 +
14 + private final Logger log = LoggerFactory.getLogger(getClass());
15 + protected KryoPool serializerPool;
16 +
17 +
18 + public KryoSerializer() {
19 + setupKryoPool();
20 + }
21 +
22 + /**
23 + * Sets up the common serialzers pool.
24 + */
25 + protected void setupKryoPool() {
26 + serializerPool = KryoPool.newBuilder()
27 + .register(KryoPoolUtil.API)
28 + .build()
29 + .populate(1);
30 + }
31 +
32 + @Override
33 + public byte[] encode(final Object obj) {
34 + return serializerPool.serialize(obj);
35 + }
36 +
37 + @Override
38 + public <T> T decode(final byte[] bytes) {
39 + if (bytes == null) {
40 + return null;
41 + }
42 + return serializerPool.deserialize(bytes);
43 + }
44 +
45 + @Override
46 + public void encode(Object obj, ByteBuffer buffer) {
47 + serializerPool.serialize(obj, buffer);
48 + }
49 +
50 + @Override
51 + public <T> T decode(ByteBuffer buffer) {
52 + return serializerPool.deserialize(buffer);
53 + }
54 +
55 +}
...@@ -2,6 +2,7 @@ package org.onlab.onos.store.serializers; ...@@ -2,6 +2,7 @@ package org.onlab.onos.store.serializers;
2 2
3 import org.onlab.onos.net.ConnectPoint; 3 import org.onlab.onos.net.ConnectPoint;
4 import org.onlab.onos.net.LinkKey; 4 import org.onlab.onos.net.LinkKey;
5 +
5 import com.esotericsoftware.kryo.Kryo; 6 import com.esotericsoftware.kryo.Kryo;
6 import com.esotericsoftware.kryo.Serializer; 7 import com.esotericsoftware.kryo.Serializer;
7 import com.esotericsoftware.kryo.io.Input; 8 import com.esotericsoftware.kryo.io.Input;
...@@ -13,7 +14,7 @@ import com.esotericsoftware.kryo.io.Output; ...@@ -13,7 +14,7 @@ import com.esotericsoftware.kryo.io.Output;
13 public class LinkKeySerializer extends Serializer<LinkKey> { 14 public class LinkKeySerializer extends Serializer<LinkKey> {
14 15
15 /** 16 /**
16 - * Default constructor. 17 + * Creates {@link LinkKey} serializer instance.
17 */ 18 */
18 public LinkKeySerializer() { 19 public LinkKeySerializer() {
19 // non-null, immutable 20 // non-null, immutable
......
...@@ -12,6 +12,14 @@ import com.esotericsoftware.kryo.io.Output; ...@@ -12,6 +12,14 @@ import com.esotericsoftware.kryo.io.Output;
12 */ 12 */
13 public class MastershipRoleSerializer extends Serializer<MastershipRole> { 13 public class MastershipRoleSerializer extends Serializer<MastershipRole> {
14 14
15 + /**
16 + * Creates {@link MastershipRole} serializer instance.
17 + */
18 + public MastershipRoleSerializer() {
19 + // non-null, immutable
20 + super(false, true);
21 + }
22 +
15 @Override 23 @Override
16 public MastershipRole read(Kryo kryo, Input input, Class<MastershipRole> type) { 24 public MastershipRole read(Kryo kryo, Input input, Class<MastershipRole> type) {
17 final String role = kryo.readObject(input, String.class); 25 final String role = kryo.readObject(input, String.class);
......
...@@ -2,7 +2,6 @@ package org.onlab.onos.store.serializers; ...@@ -2,7 +2,6 @@ package org.onlab.onos.store.serializers;
2 2
3 import org.onlab.onos.cluster.MastershipTerm; 3 import org.onlab.onos.cluster.MastershipTerm;
4 import org.onlab.onos.cluster.NodeId; 4 import org.onlab.onos.cluster.NodeId;
5 -
6 import com.esotericsoftware.kryo.Kryo; 5 import com.esotericsoftware.kryo.Kryo;
7 import com.esotericsoftware.kryo.Serializer; 6 import com.esotericsoftware.kryo.Serializer;
8 import com.esotericsoftware.kryo.io.Input; 7 import com.esotericsoftware.kryo.io.Input;
...@@ -13,9 +12,17 @@ import com.esotericsoftware.kryo.io.Output; ...@@ -13,9 +12,17 @@ import com.esotericsoftware.kryo.io.Output;
13 */ 12 */
14 public class MastershipTermSerializer extends Serializer<MastershipTerm> { 13 public class MastershipTermSerializer extends Serializer<MastershipTerm> {
15 14
15 + /**
16 + * Creates {@link MastershipTerm} serializer instance.
17 + */
18 + public MastershipTermSerializer() {
19 + // non-null, immutable
20 + super(false, true);
21 + }
22 +
16 @Override 23 @Override
17 public MastershipTerm read(Kryo kryo, Input input, Class<MastershipTerm> type) { 24 public MastershipTerm read(Kryo kryo, Input input, Class<MastershipTerm> type) {
18 - final NodeId node = new NodeId(kryo.readObject(input, String.class)); 25 + final NodeId node = new NodeId(input.readString());
19 final int term = input.readInt(); 26 final int term = input.readInt();
20 return MastershipTerm.of(node, term); 27 return MastershipTerm.of(node, term);
21 } 28 }
......
...@@ -4,6 +4,7 @@ import com.esotericsoftware.kryo.Kryo; ...@@ -4,6 +4,7 @@ import com.esotericsoftware.kryo.Kryo;
4 import com.esotericsoftware.kryo.Serializer; 4 import com.esotericsoftware.kryo.Serializer;
5 import com.esotericsoftware.kryo.io.Input; 5 import com.esotericsoftware.kryo.io.Input;
6 import com.esotericsoftware.kryo.io.Output; 6 import com.esotericsoftware.kryo.io.Output;
7 +
7 import org.onlab.onos.cluster.NodeId; 8 import org.onlab.onos.cluster.NodeId;
8 9
9 /** 10 /**
...@@ -11,14 +12,22 @@ import org.onlab.onos.cluster.NodeId; ...@@ -11,14 +12,22 @@ import org.onlab.onos.cluster.NodeId;
11 */ 12 */
12 public final class NodeIdSerializer extends Serializer<NodeId> { 13 public final class NodeIdSerializer extends Serializer<NodeId> {
13 14
15 + /**
16 + * Creates {@link NodeId} serializer instance.
17 + */
18 + public NodeIdSerializer() {
19 + // non-null, immutable
20 + super(false, true);
21 + }
22 +
14 @Override 23 @Override
15 public void write(Kryo kryo, Output output, NodeId object) { 24 public void write(Kryo kryo, Output output, NodeId object) {
16 - kryo.writeObject(output, object.toString()); 25 + output.writeString(object.toString());
17 } 26 }
18 27
19 @Override 28 @Override
20 public NodeId read(Kryo kryo, Input input, Class<NodeId> type) { 29 public NodeId read(Kryo kryo, Input input, Class<NodeId> type) {
21 - final String id = kryo.readObject(input, String.class); 30 + final String id = input.readString();
22 return new NodeId(id); 31 return new NodeId(id);
23 } 32 }
24 } 33 }
......
...@@ -14,7 +14,7 @@ public final class PortNumberSerializer extends ...@@ -14,7 +14,7 @@ public final class PortNumberSerializer extends
14 Serializer<PortNumber> { 14 Serializer<PortNumber> {
15 15
16 /** 16 /**
17 - * Default constructor. 17 + * Creates {@link PortNumber} serializer instance.
18 */ 18 */
19 public PortNumberSerializer() { 19 public PortNumberSerializer() {
20 // non-null, immutable 20 // non-null, immutable
......
...@@ -13,7 +13,7 @@ import com.esotericsoftware.kryo.io.Output; ...@@ -13,7 +13,7 @@ import com.esotericsoftware.kryo.io.Output;
13 public class ProviderIdSerializer extends Serializer<ProviderId> { 13 public class ProviderIdSerializer extends Serializer<ProviderId> {
14 14
15 /** 15 /**
16 - * Default constructor. 16 + * Creates {@link ProviderId} serializer instance.
17 */ 17 */
18 public ProviderIdSerializer() { 18 public ProviderIdSerializer() {
19 // non-null, immutable 19 // non-null, immutable
...@@ -24,13 +24,15 @@ public class ProviderIdSerializer extends Serializer<ProviderId> { ...@@ -24,13 +24,15 @@ public class ProviderIdSerializer extends Serializer<ProviderId> {
24 public void write(Kryo kryo, Output output, ProviderId object) { 24 public void write(Kryo kryo, Output output, ProviderId object) {
25 output.writeString(object.scheme()); 25 output.writeString(object.scheme());
26 output.writeString(object.id()); 26 output.writeString(object.id());
27 + output.writeBoolean(object.isAncillary());
27 } 28 }
28 29
29 @Override 30 @Override
30 public ProviderId read(Kryo kryo, Input input, Class<ProviderId> type) { 31 public ProviderId read(Kryo kryo, Input input, Class<ProviderId> type) {
31 String scheme = input.readString(); 32 String scheme = input.readString();
32 String id = input.readString(); 33 String id = input.readString();
33 - return new ProviderId(scheme, id); 34 + boolean isAncillary = input.readBoolean();
35 + return new ProviderId(scheme, id, isAncillary);
34 } 36 }
35 37
36 } 38 }
......
...@@ -6,41 +6,37 @@ import java.nio.ByteBuffer; ...@@ -6,41 +6,37 @@ 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 KryoSerializationService { 9 +public interface StoreSerializer {
10 10
11 /** 11 /**
12 - * Serializes the specified object into bytes using one of the 12 + * Serializes the specified object into bytes.
13 - * pre-registered serializers.
14 * 13 *
15 * @param obj object to be serialized 14 * @param obj object to be serialized
16 * @return serialized bytes 15 * @return serialized bytes
17 */ 16 */
18 - public byte[] serialize(final Object obj); 17 + public byte[] encode(final Object obj);
19 18
20 /** 19 /**
21 - * Serializes the specified object into bytes using one of the 20 + * Serializes the specified object into bytes.
22 - * pre-registered serializers.
23 * 21 *
24 * @param obj object to be serialized 22 * @param obj object to be serialized
25 * @param buffer to write serialized bytes 23 * @param buffer to write serialized bytes
26 */ 24 */
27 - public void serialize(final Object obj, ByteBuffer buffer); 25 + public void encode(final Object obj, ByteBuffer buffer);
28 26
29 /** 27 /**
30 - * Deserializes the specified bytes into an object using one of the 28 + * Deserializes the specified bytes into an object.
31 - * pre-registered serializers.
32 * 29 *
33 * @param bytes bytes to be deserialized 30 * @param bytes bytes to be deserialized
34 * @return deserialized object 31 * @return deserialized object
35 */ 32 */
36 - public <T> T deserialize(final byte[] bytes); 33 + public <T> T decode(final byte[] bytes);
37 34
38 /** 35 /**
39 - * Deserializes the specified bytes into an object using one of the 36 + * Deserializes the specified bytes into an object.
40 - * pre-registered serializers.
41 * 37 *
42 * @param buffer bytes to be deserialized 38 * @param buffer bytes to be deserialized
43 * @return deserialized object 39 * @return deserialized object
44 */ 40 */
45 - public <T> T deserialize(final ByteBuffer buffer); 41 + public <T> T decode(final ByteBuffer buffer);
46 } 42 }
......
1 package org.onlab.onos.store.serializers; 1 package org.onlab.onos.store.serializers;
2 2
3 +import static org.junit.Assert.assertEquals;
3 import static org.onlab.onos.net.DeviceId.deviceId; 4 import static org.onlab.onos.net.DeviceId.deviceId;
4 import static org.onlab.onos.net.PortNumber.portNumber; 5 import static org.onlab.onos.net.PortNumber.portNumber;
5 6
6 -import java.net.URI;
7 import java.nio.ByteBuffer; 7 import java.nio.ByteBuffer;
8 -import java.util.ArrayList;
9 -import java.util.HashMap;
10 8
11 import org.junit.After; 9 import org.junit.After;
12 import org.junit.Before; 10 import org.junit.Before;
...@@ -14,7 +12,9 @@ import org.junit.BeforeClass; ...@@ -14,7 +12,9 @@ import org.junit.BeforeClass;
14 import org.junit.Test; 12 import org.junit.Test;
15 import org.onlab.onos.cluster.MastershipTerm; 13 import org.onlab.onos.cluster.MastershipTerm;
16 import org.onlab.onos.cluster.NodeId; 14 import org.onlab.onos.cluster.NodeId;
15 +import org.onlab.onos.net.Annotations;
17 import org.onlab.onos.net.ConnectPoint; 16 import org.onlab.onos.net.ConnectPoint;
17 +import org.onlab.onos.net.DefaultAnnotations;
18 import org.onlab.onos.net.DefaultDevice; 18 import org.onlab.onos.net.DefaultDevice;
19 import org.onlab.onos.net.DefaultLink; 19 import org.onlab.onos.net.DefaultLink;
20 import org.onlab.onos.net.DefaultPort; 20 import org.onlab.onos.net.DefaultPort;
...@@ -24,7 +24,9 @@ import org.onlab.onos.net.Link; ...@@ -24,7 +24,9 @@ import org.onlab.onos.net.Link;
24 import org.onlab.onos.net.LinkKey; 24 import org.onlab.onos.net.LinkKey;
25 import org.onlab.onos.net.MastershipRole; 25 import org.onlab.onos.net.MastershipRole;
26 import org.onlab.onos.net.PortNumber; 26 import org.onlab.onos.net.PortNumber;
27 +import org.onlab.onos.net.SparseAnnotations;
27 import org.onlab.onos.net.provider.ProviderId; 28 import org.onlab.onos.net.provider.ProviderId;
29 +import org.onlab.packet.IpAddress;
28 import org.onlab.packet.IpPrefix; 30 import org.onlab.packet.IpPrefix;
29 import org.onlab.util.KryoPool; 31 import org.onlab.util.KryoPool;
30 32
...@@ -32,10 +34,10 @@ import com.google.common.collect.ImmutableMap; ...@@ -32,10 +34,10 @@ import com.google.common.collect.ImmutableMap;
32 import com.google.common.collect.ImmutableSet; 34 import com.google.common.collect.ImmutableSet;
33 import com.google.common.testing.EqualsTester; 35 import com.google.common.testing.EqualsTester;
34 36
35 -import de.javakaffee.kryoserializers.URISerializer; 37 +public class KryoSerializerTest {
36 38
37 -public class KryoSerializerTests {
38 private static final ProviderId PID = new ProviderId("of", "foo"); 39 private static final ProviderId PID = new ProviderId("of", "foo");
40 + private static final ProviderId PIDA = new ProviderId("of", "foo", true);
39 private static final DeviceId DID1 = deviceId("of:foo"); 41 private static final DeviceId DID1 = deviceId("of:foo");
40 private static final DeviceId DID2 = deviceId("of:bar"); 42 private static final DeviceId DID2 = deviceId("of:bar");
41 private static final PortNumber P1 = portNumber(1); 43 private static final PortNumber P1 = portNumber(1);
...@@ -48,44 +50,23 @@ public class KryoSerializerTests { ...@@ -48,44 +50,23 @@ public class KryoSerializerTests {
48 private static final String SW2 = "3.9.5"; 50 private static final String SW2 = "3.9.5";
49 private static final String SN = "43311-12345"; 51 private static final String SN = "43311-12345";
50 private static final Device DEV1 = new DefaultDevice(PID, DID1, Device.Type.SWITCH, MFR, HW, SW1, SN); 52 private static final Device DEV1 = new DefaultDevice(PID, DID1, Device.Type.SWITCH, MFR, HW, SW1, SN);
53 + private static final SparseAnnotations A1 = DefaultAnnotations.builder()
54 + .set("A1", "a1")
55 + .set("B1", "b1")
56 + .build();
57 + private static final SparseAnnotations A1_2 = DefaultAnnotations.builder()
58 + .remove("A1")
59 + .set("B3", "b3")
60 + .build();
51 61
52 private static KryoPool kryos; 62 private static KryoPool kryos;
53 63
54 @BeforeClass 64 @BeforeClass
55 public static void setUpBeforeClass() throws Exception { 65 public static void setUpBeforeClass() throws Exception {
56 kryos = KryoPool.newBuilder() 66 kryos = KryoPool.newBuilder()
57 - .register( 67 + .register(KryoPoolUtil.API)
58 - ArrayList.class,
59 - HashMap.class
60 - )
61 - .register(
62 - Device.Type.class,
63 - Link.Type.class
64 -
65 -// ControllerNode.State.class,
66 -// DefaultControllerNode.class,
67 -// MastershipRole.class,
68 -// Port.class,
69 -// Element.class,
70 - )
71 - .register(ConnectPoint.class, new ConnectPointSerializer())
72 - .register(DefaultLink.class, new DefaultLinkSerializer())
73 - .register(DefaultPort.class, new DefaultPortSerializer())
74 - .register(DeviceId.class, new DeviceIdSerializer())
75 .register(ImmutableMap.class, new ImmutableMapSerializer()) 68 .register(ImmutableMap.class, new ImmutableMapSerializer())
76 .register(ImmutableSet.class, new ImmutableSetSerializer()) 69 .register(ImmutableSet.class, new ImmutableSetSerializer())
77 - .register(IpPrefix.class, new IpPrefixSerializer())
78 - .register(LinkKey.class, new LinkKeySerializer())
79 - .register(NodeId.class, new NodeIdSerializer())
80 - .register(PortNumber.class, new PortNumberSerializer())
81 - .register(ProviderId.class, new ProviderIdSerializer())
82 -
83 - .register(DefaultDevice.class)
84 -
85 - .register(URI.class, new URISerializer())
86 -
87 - .register(MastershipRole.class, new MastershipRoleSerializer())
88 - .register(MastershipTerm.class, new MastershipTermSerializer())
89 .build(); 70 .build();
90 } 71 }
91 72
...@@ -112,10 +93,12 @@ public class KryoSerializerTests { ...@@ -112,10 +93,12 @@ public class KryoSerializerTests {
112 93
113 94
114 @Test 95 @Test
115 - public final void test() { 96 + public final void testSerialization() {
116 testSerialized(new ConnectPoint(DID1, P1)); 97 testSerialized(new ConnectPoint(DID1, P1));
117 testSerialized(new DefaultLink(PID, CP1, CP2, Link.Type.DIRECT)); 98 testSerialized(new DefaultLink(PID, CP1, CP2, Link.Type.DIRECT));
118 testSerialized(new DefaultPort(DEV1, P1, true)); 99 testSerialized(new DefaultPort(DEV1, P1, true));
100 + testSerialized(new DefaultLink(PID, CP1, CP2, Link.Type.DIRECT, A1));
101 + testSerialized(new DefaultPort(DEV1, P1, true, A1_2));
119 testSerialized(DID1); 102 testSerialized(DID1);
120 testSerialized(ImmutableMap.of(DID1, DEV1, DID2, DEV1)); 103 testSerialized(ImmutableMap.of(DID1, DEV1, DID2, DEV1));
121 testSerialized(ImmutableMap.of(DID1, DEV1)); 104 testSerialized(ImmutableMap.of(DID1, DEV1));
...@@ -124,10 +107,41 @@ public class KryoSerializerTests { ...@@ -124,10 +107,41 @@ public class KryoSerializerTests {
124 testSerialized(ImmutableSet.of(DID1)); 107 testSerialized(ImmutableSet.of(DID1));
125 testSerialized(ImmutableSet.of()); 108 testSerialized(ImmutableSet.of());
126 testSerialized(IpPrefix.valueOf("192.168.0.1/24")); 109 testSerialized(IpPrefix.valueOf("192.168.0.1/24"));
110 + testSerialized(IpAddress.valueOf("192.168.0.1"));
127 testSerialized(new LinkKey(CP1, CP2)); 111 testSerialized(new LinkKey(CP1, CP2));
128 testSerialized(new NodeId("SomeNodeIdentifier")); 112 testSerialized(new NodeId("SomeNodeIdentifier"));
129 testSerialized(P1); 113 testSerialized(P1);
130 testSerialized(PID); 114 testSerialized(PID);
115 + testSerialized(PIDA);
116 + testSerialized(new NodeId("bar"));
117 + testSerialized(MastershipTerm.of(new NodeId("foo"), 2));
118 + for (MastershipRole role : MastershipRole.values()) {
119 + testSerialized(role);
120 + }
121 + }
122 +
123 + @Test
124 + public final void testAnnotations() {
125 + // Annotations does not have equals defined, manually test equality
126 + final byte[] a1Bytes = kryos.serialize(A1);
127 + SparseAnnotations copiedA1 = kryos.deserialize(a1Bytes);
128 + assertAnnotationsEquals(copiedA1, A1);
129 +
130 + final byte[] a12Bytes = kryos.serialize(A1_2);
131 + SparseAnnotations copiedA12 = kryos.deserialize(a12Bytes);
132 + assertAnnotationsEquals(copiedA12, A1_2);
133 + }
134 +
135 + // code clone
136 + public static void assertAnnotationsEquals(Annotations actual, SparseAnnotations... annotations) {
137 + SparseAnnotations expected = DefaultAnnotations.builder().build();
138 + for (SparseAnnotations a : annotations) {
139 + expected = DefaultAnnotations.union(expected, a);
140 + }
141 + assertEquals(expected.keys(), actual.keys());
142 + for (String key : expected.keys()) {
143 + assertEquals(expected.value(key), actual.value(key));
144 + }
131 } 145 }
132 146
133 } 147 }
......
...@@ -4,27 +4,15 @@ import org.apache.felix.scr.annotations.Component; ...@@ -4,27 +4,15 @@ import org.apache.felix.scr.annotations.Component;
4 import org.apache.felix.scr.annotations.Service; 4 import org.apache.felix.scr.annotations.Service;
5 import org.onlab.onos.cluster.MastershipTerm; 5 import org.onlab.onos.cluster.MastershipTerm;
6 import org.onlab.onos.net.DeviceId; 6 import org.onlab.onos.net.DeviceId;
7 -import org.onlab.onos.store.ClockService; 7 +import org.onlab.onos.store.ClockProviderService;
8 -import org.onlab.onos.store.Timestamp;
9 8
10 //FIXME: Code clone in onos-core-trivial, onos-core-hz-net 9 //FIXME: Code clone in onos-core-trivial, onos-core-hz-net
11 /** 10 /**
12 - * Dummy implementation of {@link ClockService}. 11 + * Dummy implementation of {@link ClockProviderService}.
13 */ 12 */
14 @Component(immediate = true) 13 @Component(immediate = true)
15 @Service 14 @Service
16 -public class NoOpClockService implements ClockService { 15 +public class NoOpClockProviderService implements ClockProviderService {
17 -
18 - @Override
19 - public Timestamp getTimestamp(DeviceId deviceId) {
20 - return new Timestamp() {
21 -
22 - @Override
23 - public int compareTo(Timestamp o) {
24 - throw new IllegalStateException("Never expected to be used.");
25 - }
26 - };
27 - }
28 16
29 @Override 17 @Override
30 public void setMastershipTerm(DeviceId deviceId, MastershipTerm term) { 18 public void setMastershipTerm(DeviceId deviceId, MastershipTerm term) {
......
...@@ -2,6 +2,8 @@ package org.onlab.onos.store.trivial.impl; ...@@ -2,6 +2,8 @@ package org.onlab.onos.store.trivial.impl;
2 2
3 import com.google.common.collect.FluentIterable; 3 import com.google.common.collect.FluentIterable;
4 import com.google.common.collect.ImmutableList; 4 import com.google.common.collect.ImmutableList;
5 +import com.google.common.collect.Maps;
6 +import com.google.common.collect.Sets;
5 7
6 import org.apache.commons.lang3.concurrent.ConcurrentException; 8 import org.apache.commons.lang3.concurrent.ConcurrentException;
7 import org.apache.commons.lang3.concurrent.ConcurrentInitializer; 9 import org.apache.commons.lang3.concurrent.ConcurrentInitializer;
...@@ -9,7 +11,7 @@ import org.apache.felix.scr.annotations.Activate; ...@@ -9,7 +11,7 @@ import org.apache.felix.scr.annotations.Activate;
9 import org.apache.felix.scr.annotations.Component; 11 import org.apache.felix.scr.annotations.Component;
10 import org.apache.felix.scr.annotations.Deactivate; 12 import org.apache.felix.scr.annotations.Deactivate;
11 import org.apache.felix.scr.annotations.Service; 13 import org.apache.felix.scr.annotations.Service;
12 -import org.onlab.onos.net.Annotations; 14 +import org.onlab.onos.net.AnnotationsUtil;
13 import org.onlab.onos.net.DefaultAnnotations; 15 import org.onlab.onos.net.DefaultAnnotations;
14 import org.onlab.onos.net.DefaultDevice; 16 import org.onlab.onos.net.DefaultDevice;
15 import org.onlab.onos.net.DefaultPort; 17 import org.onlab.onos.net.DefaultPort;
...@@ -28,10 +30,10 @@ import org.onlab.onos.net.device.DeviceStoreDelegate; ...@@ -28,10 +30,10 @@ import org.onlab.onos.net.device.DeviceStoreDelegate;
28 import org.onlab.onos.net.device.PortDescription; 30 import org.onlab.onos.net.device.PortDescription;
29 import org.onlab.onos.net.provider.ProviderId; 31 import org.onlab.onos.net.provider.ProviderId;
30 import org.onlab.onos.store.AbstractStore; 32 import org.onlab.onos.store.AbstractStore;
33 +import org.onlab.util.NewConcurrentHashMap;
31 import org.slf4j.Logger; 34 import org.slf4j.Logger;
32 35
33 import java.util.ArrayList; 36 import java.util.ArrayList;
34 -import java.util.Collection;
35 import java.util.Collections; 37 import java.util.Collections;
36 import java.util.HashSet; 38 import java.util.HashSet;
37 import java.util.Iterator; 39 import java.util.Iterator;
...@@ -47,12 +49,13 @@ import java.util.concurrent.atomic.AtomicReference; ...@@ -47,12 +49,13 @@ import java.util.concurrent.atomic.AtomicReference;
47 import static com.google.common.base.Preconditions.checkArgument; 49 import static com.google.common.base.Preconditions.checkArgument;
48 import static com.google.common.base.Preconditions.checkNotNull; 50 import static com.google.common.base.Preconditions.checkNotNull;
49 import static com.google.common.base.Predicates.notNull; 51 import static com.google.common.base.Predicates.notNull;
52 +import static com.google.common.base.Verify.verify;
50 import static org.onlab.onos.net.device.DeviceEvent.Type.*; 53 import static org.onlab.onos.net.device.DeviceEvent.Type.*;
51 import static org.slf4j.LoggerFactory.getLogger; 54 import static org.slf4j.LoggerFactory.getLogger;
52 import static org.apache.commons.lang3.concurrent.ConcurrentUtils.createIfAbsentUnchecked; 55 import static org.apache.commons.lang3.concurrent.ConcurrentUtils.createIfAbsentUnchecked;
56 +import static org.onlab.onos.net.DefaultAnnotations.union;
53 import static org.onlab.onos.net.DefaultAnnotations.merge; 57 import static org.onlab.onos.net.DefaultAnnotations.merge;
54 58
55 -// TODO: synchronization should be done in more fine-grained manner.
56 /** 59 /**
57 * Manages inventory of infrastructure devices using trivial in-memory 60 * Manages inventory of infrastructure devices using trivial in-memory
58 * structures implementation. 61 * structures implementation.
...@@ -70,14 +73,14 @@ public class SimpleDeviceStore ...@@ -70,14 +73,14 @@ public class SimpleDeviceStore
70 // collection of Description given from various providers 73 // collection of Description given from various providers
71 private final ConcurrentMap<DeviceId, 74 private final ConcurrentMap<DeviceId,
72 ConcurrentMap<ProviderId, DeviceDescriptions>> 75 ConcurrentMap<ProviderId, DeviceDescriptions>>
73 - deviceDescs = new ConcurrentHashMap<>(); 76 + deviceDescs = Maps.newConcurrentMap();
74 77
75 // cache of Device and Ports generated by compositing descriptions from providers 78 // cache of Device and Ports generated by compositing descriptions from providers
76 - private final ConcurrentMap<DeviceId, Device> devices = new ConcurrentHashMap<>(); 79 + private final ConcurrentMap<DeviceId, Device> devices = Maps.newConcurrentMap();
77 - private final ConcurrentMap<DeviceId, ConcurrentMap<PortNumber, Port>> devicePorts = new ConcurrentHashMap<>(); 80 + private final ConcurrentMap<DeviceId, ConcurrentMap<PortNumber, Port>> devicePorts = Maps.newConcurrentMap();
78 81
79 // available(=UP) devices 82 // available(=UP) devices
80 - private final Set<DeviceId> availableDevices = new HashSet<>(); 83 + private final Set<DeviceId> availableDevices = Sets.newConcurrentHashSet();
81 84
82 85
83 @Activate 86 @Activate
...@@ -87,6 +90,10 @@ public class SimpleDeviceStore ...@@ -87,6 +90,10 @@ public class SimpleDeviceStore
87 90
88 @Deactivate 91 @Deactivate
89 public void deactivate() { 92 public void deactivate() {
93 + deviceDescs.clear();
94 + devices.clear();
95 + devicePorts.clear();
96 + availableDevices.clear();
90 log.info("Stopped"); 97 log.info("Stopped");
91 } 98 }
92 99
...@@ -106,18 +113,21 @@ public class SimpleDeviceStore ...@@ -106,18 +113,21 @@ public class SimpleDeviceStore
106 } 113 }
107 114
108 @Override 115 @Override
109 - public synchronized DeviceEvent createOrUpdateDevice(ProviderId providerId, DeviceId deviceId, 116 + public DeviceEvent createOrUpdateDevice(ProviderId providerId,
117 + DeviceId deviceId,
110 DeviceDescription deviceDescription) { 118 DeviceDescription deviceDescription) {
119 +
111 ConcurrentMap<ProviderId, DeviceDescriptions> providerDescs 120 ConcurrentMap<ProviderId, DeviceDescriptions> providerDescs
112 - = createIfAbsentUnchecked(deviceDescs, deviceId, 121 + = getDeviceDescriptions(deviceId);
113 - new InitConcurrentHashMap<ProviderId, DeviceDescriptions>());
114 122
115 - Device oldDevice = devices.get(deviceId); 123 + synchronized (providerDescs) {
124 + // locking per device
116 125
117 DeviceDescriptions descs 126 DeviceDescriptions descs
118 = createIfAbsentUnchecked(providerDescs, providerId, 127 = createIfAbsentUnchecked(providerDescs, providerId,
119 new InitDeviceDescs(deviceDescription)); 128 new InitDeviceDescs(deviceDescription));
120 129
130 + Device oldDevice = devices.get(deviceId);
121 // update description 131 // update description
122 descs.putDeviceDesc(deviceDescription); 132 descs.putDeviceDesc(deviceDescription);
123 Device newDevice = composeDevice(deviceId, providerDescs); 133 Device newDevice = composeDevice(deviceId, providerDescs);
...@@ -130,93 +140,115 @@ public class SimpleDeviceStore ...@@ -130,93 +140,115 @@ public class SimpleDeviceStore
130 return updateDevice(providerId, oldDevice, newDevice); 140 return updateDevice(providerId, oldDevice, newDevice);
131 } 141 }
132 } 142 }
143 + }
133 144
134 // Creates the device and returns the appropriate event if necessary. 145 // Creates the device and returns the appropriate event if necessary.
146 + // Guarded by deviceDescs value (=Device lock)
135 private DeviceEvent createDevice(ProviderId providerId, Device newDevice) { 147 private DeviceEvent createDevice(ProviderId providerId, Device newDevice) {
136 148
137 // update composed device cache 149 // update composed device cache
138 - synchronized (this) { 150 + Device oldDevice = devices.putIfAbsent(newDevice.id(), newDevice);
139 - devices.putIfAbsent(newDevice.id(), newDevice); 151 + verify(oldDevice == null,
152 + "Unexpected Device in cache. PID:%s [old=%s, new=%s]",
153 + providerId, oldDevice, newDevice);
154 +
140 if (!providerId.isAncillary()) { 155 if (!providerId.isAncillary()) {
141 availableDevices.add(newDevice.id()); 156 availableDevices.add(newDevice.id());
142 } 157 }
143 - }
144 158
145 return new DeviceEvent(DeviceEvent.Type.DEVICE_ADDED, newDevice, null); 159 return new DeviceEvent(DeviceEvent.Type.DEVICE_ADDED, newDevice, null);
146 } 160 }
147 161
148 // Updates the device and returns the appropriate event if necessary. 162 // Updates the device and returns the appropriate event if necessary.
163 + // Guarded by deviceDescs value (=Device lock)
149 private DeviceEvent updateDevice(ProviderId providerId, Device oldDevice, Device newDevice) { 164 private DeviceEvent updateDevice(ProviderId providerId, Device oldDevice, Device newDevice) {
150 165
151 // We allow only certain attributes to trigger update 166 // We allow only certain attributes to trigger update
152 if (!Objects.equals(oldDevice.hwVersion(), newDevice.hwVersion()) || 167 if (!Objects.equals(oldDevice.hwVersion(), newDevice.hwVersion()) ||
153 !Objects.equals(oldDevice.swVersion(), newDevice.swVersion()) || 168 !Objects.equals(oldDevice.swVersion(), newDevice.swVersion()) ||
154 - !isAnnotationsEqual(oldDevice.annotations(), newDevice.annotations())) { 169 + !AnnotationsUtil.isEqual(oldDevice.annotations(), newDevice.annotations())) {
155 170
156 - synchronized (this) { 171 + boolean replaced = devices.replace(newDevice.id(), oldDevice, newDevice);
157 - devices.replace(newDevice.id(), oldDevice, newDevice); 172 + if (!replaced) {
173 + verify(replaced,
174 + "Replacing devices cache failed. PID:%s [expected:%s, found:%s, new=%s]",
175 + providerId, oldDevice, devices.get(newDevice.id())
176 + , newDevice);
177 + }
158 if (!providerId.isAncillary()) { 178 if (!providerId.isAncillary()) {
159 availableDevices.add(newDevice.id()); 179 availableDevices.add(newDevice.id());
160 } 180 }
161 - }
162 return new DeviceEvent(DeviceEvent.Type.DEVICE_UPDATED, newDevice, null); 181 return new DeviceEvent(DeviceEvent.Type.DEVICE_UPDATED, newDevice, null);
163 } 182 }
164 183
165 // Otherwise merely attempt to change availability if primary provider 184 // Otherwise merely attempt to change availability if primary provider
166 if (!providerId.isAncillary()) { 185 if (!providerId.isAncillary()) {
167 - synchronized (this) {
168 boolean added = availableDevices.add(newDevice.id()); 186 boolean added = availableDevices.add(newDevice.id());
169 return !added ? null : 187 return !added ? null :
170 new DeviceEvent(DEVICE_AVAILABILITY_CHANGED, newDevice, null); 188 new DeviceEvent(DEVICE_AVAILABILITY_CHANGED, newDevice, null);
171 } 189 }
172 - }
173 return null; 190 return null;
174 } 191 }
175 192
176 @Override 193 @Override
177 public DeviceEvent markOffline(DeviceId deviceId) { 194 public DeviceEvent markOffline(DeviceId deviceId) {
178 - synchronized (this) { 195 + ConcurrentMap<ProviderId, DeviceDescriptions> providerDescs
196 + = getDeviceDescriptions(deviceId);
197 +
198 + // locking device
199 + synchronized (providerDescs) {
179 Device device = devices.get(deviceId); 200 Device device = devices.get(deviceId);
180 - boolean removed = (device != null) && availableDevices.remove(deviceId); 201 + if (device == null) {
181 - return !removed ? null : 202 + return null;
182 - new DeviceEvent(DEVICE_AVAILABILITY_CHANGED, device, null); 203 + }
204 + boolean removed = availableDevices.remove(deviceId);
205 + if (removed) {
206 + // TODO: broadcast ... DOWN only?
207 + return new DeviceEvent(DEVICE_AVAILABILITY_CHANGED, device, null);
208 + }
209 + return null;
183 } 210 }
184 } 211 }
185 212
186 @Override 213 @Override
187 - public synchronized List<DeviceEvent> updatePorts(ProviderId providerId, DeviceId deviceId, 214 + public List<DeviceEvent> updatePorts(ProviderId providerId,
215 + DeviceId deviceId,
188 List<PortDescription> portDescriptions) { 216 List<PortDescription> portDescriptions) {
189 217
190 - // TODO: implement multi-provider
191 Device device = devices.get(deviceId); 218 Device device = devices.get(deviceId);
192 checkArgument(device != null, DEVICE_NOT_FOUND, deviceId); 219 checkArgument(device != null, DEVICE_NOT_FOUND, deviceId);
193 220
194 ConcurrentMap<ProviderId, DeviceDescriptions> descsMap = deviceDescs.get(deviceId); 221 ConcurrentMap<ProviderId, DeviceDescriptions> descsMap = deviceDescs.get(deviceId);
195 checkArgument(descsMap != null, DEVICE_NOT_FOUND, deviceId); 222 checkArgument(descsMap != null, DEVICE_NOT_FOUND, deviceId);
196 223
224 + List<DeviceEvent> events = new ArrayList<>();
225 + synchronized (descsMap) {
197 DeviceDescriptions descs = descsMap.get(providerId); 226 DeviceDescriptions descs = descsMap.get(providerId);
227 + // every provider must provide DeviceDescription.
198 checkArgument(descs != null, 228 checkArgument(descs != null,
199 "Device description for Device ID %s from Provider %s was not found", 229 "Device description for Device ID %s from Provider %s was not found",
200 deviceId, providerId); 230 deviceId, providerId);
201 231
202 - 232 + Map<PortNumber, Port> ports = getPortMap(deviceId);
203 - List<DeviceEvent> events = new ArrayList<>();
204 - synchronized (this) {
205 - ConcurrentMap<PortNumber, Port> ports = getPortMap(deviceId);
206 233
207 // Add new ports 234 // Add new ports
208 Set<PortNumber> processed = new HashSet<>(); 235 Set<PortNumber> processed = new HashSet<>();
209 for (PortDescription portDescription : portDescriptions) { 236 for (PortDescription portDescription : portDescriptions) {
210 - PortNumber number = portDescription.portNumber(); 237 + final PortNumber number = portDescription.portNumber();
211 - Port oldPort = ports.get(number); 238 + processed.add(portDescription.portNumber());
239 +
240 + final Port oldPort = ports.get(number);
241 + final Port newPort;
242 +
243 +// event suppression hook?
244 +
212 // update description 245 // update description
213 descs.putPortDesc(portDescription); 246 descs.putPortDesc(portDescription);
214 - Port newPort = composePort(device, number, descsMap); 247 + newPort = composePort(device, number, descsMap);
215 248
216 events.add(oldPort == null ? 249 events.add(oldPort == null ?
217 createPort(device, newPort, ports) : 250 createPort(device, newPort, ports) :
218 updatePort(device, oldPort, newPort, ports)); 251 updatePort(device, oldPort, newPort, ports));
219 - processed.add(portDescription.portNumber());
220 } 252 }
221 253
222 events.addAll(pruneOldPorts(device, ports, processed)); 254 events.addAll(pruneOldPorts(device, ports, processed));
...@@ -226,19 +258,21 @@ public class SimpleDeviceStore ...@@ -226,19 +258,21 @@ public class SimpleDeviceStore
226 258
227 // Creates a new port based on the port description adds it to the map and 259 // Creates a new port based on the port description adds it to the map and
228 // Returns corresponding event. 260 // Returns corresponding event.
261 + // Guarded by deviceDescs value (=Device lock)
229 private DeviceEvent createPort(Device device, Port newPort, 262 private DeviceEvent createPort(Device device, Port newPort,
230 - ConcurrentMap<PortNumber, Port> ports) { 263 + Map<PortNumber, Port> ports) {
231 ports.put(newPort.number(), newPort); 264 ports.put(newPort.number(), newPort);
232 return new DeviceEvent(PORT_ADDED, device, newPort); 265 return new DeviceEvent(PORT_ADDED, device, newPort);
233 } 266 }
234 267
235 // Checks if the specified port requires update and if so, it replaces the 268 // Checks if the specified port requires update and if so, it replaces the
236 // existing entry in the map and returns corresponding event. 269 // existing entry in the map and returns corresponding event.
270 + // Guarded by deviceDescs value (=Device lock)
237 private DeviceEvent updatePort(Device device, Port oldPort, 271 private DeviceEvent updatePort(Device device, Port oldPort,
238 Port newPort, 272 Port newPort,
239 - ConcurrentMap<PortNumber, Port> ports) { 273 + Map<PortNumber, Port> ports) {
240 if (oldPort.isEnabled() != newPort.isEnabled() || 274 if (oldPort.isEnabled() != newPort.isEnabled() ||
241 - !isAnnotationsEqual(oldPort.annotations(), newPort.annotations())) { 275 + !AnnotationsUtil.isEqual(oldPort.annotations(), newPort.annotations())) {
242 276
243 ports.put(oldPort.number(), newPort); 277 ports.put(oldPort.number(), newPort);
244 return new DeviceEvent(PORT_UPDATED, device, newPort); 278 return new DeviceEvent(PORT_UPDATED, device, newPort);
...@@ -248,6 +282,7 @@ public class SimpleDeviceStore ...@@ -248,6 +282,7 @@ public class SimpleDeviceStore
248 282
249 // Prunes the specified list of ports based on which ports are in the 283 // Prunes the specified list of ports based on which ports are in the
250 // processed list and returns list of corresponding events. 284 // processed list and returns list of corresponding events.
285 + // Guarded by deviceDescs value (=Device lock)
251 private List<DeviceEvent> pruneOldPorts(Device device, 286 private List<DeviceEvent> pruneOldPorts(Device device,
252 Map<PortNumber, Port> ports, 287 Map<PortNumber, Port> ports,
253 Set<PortNumber> processed) { 288 Set<PortNumber> processed) {
...@@ -268,11 +303,17 @@ public class SimpleDeviceStore ...@@ -268,11 +303,17 @@ public class SimpleDeviceStore
268 // exist, it creates and registers a new one. 303 // exist, it creates and registers a new one.
269 private ConcurrentMap<PortNumber, Port> getPortMap(DeviceId deviceId) { 304 private ConcurrentMap<PortNumber, Port> getPortMap(DeviceId deviceId) {
270 return createIfAbsentUnchecked(devicePorts, deviceId, 305 return createIfAbsentUnchecked(devicePorts, deviceId,
271 - new InitConcurrentHashMap<PortNumber, Port>()); 306 + NewConcurrentHashMap.<PortNumber, Port>ifNeeded());
307 + }
308 +
309 + private ConcurrentMap<ProviderId, DeviceDescriptions> getDeviceDescriptions(
310 + DeviceId deviceId) {
311 + return createIfAbsentUnchecked(deviceDescs, deviceId,
312 + NewConcurrentHashMap.<ProviderId, DeviceDescriptions>ifNeeded());
272 } 313 }
273 314
274 @Override 315 @Override
275 - public synchronized DeviceEvent updatePortStatus(ProviderId providerId, DeviceId deviceId, 316 + public DeviceEvent updatePortStatus(ProviderId providerId, DeviceId deviceId,
276 PortDescription portDescription) { 317 PortDescription portDescription) {
277 Device device = devices.get(deviceId); 318 Device device = devices.get(deviceId);
278 checkArgument(device != null, DEVICE_NOT_FOUND, deviceId); 319 checkArgument(device != null, DEVICE_NOT_FOUND, deviceId);
...@@ -280,19 +321,22 @@ public class SimpleDeviceStore ...@@ -280,19 +321,22 @@ public class SimpleDeviceStore
280 ConcurrentMap<ProviderId, DeviceDescriptions> descsMap = deviceDescs.get(deviceId); 321 ConcurrentMap<ProviderId, DeviceDescriptions> descsMap = deviceDescs.get(deviceId);
281 checkArgument(descsMap != null, DEVICE_NOT_FOUND, deviceId); 322 checkArgument(descsMap != null, DEVICE_NOT_FOUND, deviceId);
282 323
324 + synchronized (descsMap) {
283 DeviceDescriptions descs = descsMap.get(providerId); 325 DeviceDescriptions descs = descsMap.get(providerId);
284 // assuming all providers must to give DeviceDescription 326 // assuming all providers must to give DeviceDescription
285 checkArgument(descs != null, 327 checkArgument(descs != null,
286 "Device description for Device ID %s from Provider %s was not found", 328 "Device description for Device ID %s from Provider %s was not found",
287 deviceId, providerId); 329 deviceId, providerId);
288 330
289 - synchronized (this) {
290 ConcurrentMap<PortNumber, Port> ports = getPortMap(deviceId); 331 ConcurrentMap<PortNumber, Port> ports = getPortMap(deviceId);
291 final PortNumber number = portDescription.portNumber(); 332 final PortNumber number = portDescription.portNumber();
292 - Port oldPort = ports.get(number); 333 + final Port oldPort = ports.get(number);
334 + final Port newPort;
335 +
293 // update description 336 // update description
294 descs.putPortDesc(portDescription); 337 descs.putPortDesc(portDescription);
295 - Port newPort = composePort(device, number, descsMap); 338 + newPort = composePort(device, number, descsMap);
339 +
296 if (oldPort == null) { 340 if (oldPort == null) {
297 return createPort(device, newPort, ports); 341 return createPort(device, newPort, ports);
298 } else { 342 } else {
...@@ -323,33 +367,21 @@ public class SimpleDeviceStore ...@@ -323,33 +367,21 @@ public class SimpleDeviceStore
323 367
324 @Override 368 @Override
325 public DeviceEvent removeDevice(DeviceId deviceId) { 369 public DeviceEvent removeDevice(DeviceId deviceId) {
326 - synchronized (this) { 370 + ConcurrentMap<ProviderId, DeviceDescriptions> descs = getDeviceDescriptions(deviceId);
371 + synchronized (descs) {
327 Device device = devices.remove(deviceId); 372 Device device = devices.remove(deviceId);
373 + // should DEVICE_REMOVED carry removed ports?
374 + Map<PortNumber, Port> ports = devicePorts.get(deviceId);
375 + if (ports != null) {
376 + ports.clear();
377 + }
378 + availableDevices.remove(deviceId);
379 + descs.clear();
328 return device == null ? null : 380 return device == null ? null :
329 new DeviceEvent(DEVICE_REMOVED, device, null); 381 new DeviceEvent(DEVICE_REMOVED, device, null);
330 } 382 }
331 } 383 }
332 384
333 - private static boolean isAnnotationsEqual(Annotations lhs, Annotations rhs) {
334 - if (lhs == rhs) {
335 - return true;
336 - }
337 - if (lhs == null || rhs == null) {
338 - return false;
339 - }
340 -
341 - if (!lhs.keys().equals(rhs.keys())) {
342 - return false;
343 - }
344 -
345 - for (String key : lhs.keys()) {
346 - if (!lhs.value(key).equals(rhs.value(key))) {
347 - return false;
348 - }
349 - }
350 - return true;
351 - }
352 -
353 /** 385 /**
354 * Returns a Device, merging description given from multiple Providers. 386 * Returns a Device, merging description given from multiple Providers.
355 * 387 *
...@@ -366,14 +398,14 @@ public class SimpleDeviceStore ...@@ -366,14 +398,14 @@ public class SimpleDeviceStore
366 398
367 DeviceDescriptions desc = providerDescs.get(primary); 399 DeviceDescriptions desc = providerDescs.get(primary);
368 400
369 - // base 401 + final DeviceDescription base = desc.getDeviceDesc();
370 - Type type = desc.getDeviceDesc().type(); 402 + Type type = base.type();
371 - String manufacturer = desc.getDeviceDesc().manufacturer(); 403 + String manufacturer = base.manufacturer();
372 - String hwVersion = desc.getDeviceDesc().hwVersion(); 404 + String hwVersion = base.hwVersion();
373 - String swVersion = desc.getDeviceDesc().swVersion(); 405 + String swVersion = base.swVersion();
374 - String serialNumber = desc.getDeviceDesc().serialNumber(); 406 + String serialNumber = base.serialNumber();
375 DefaultAnnotations annotations = DefaultAnnotations.builder().build(); 407 DefaultAnnotations annotations = DefaultAnnotations.builder().build();
376 - annotations = merge(annotations, desc.getDeviceDesc().annotations()); 408 + annotations = merge(annotations, base.annotations());
377 409
378 for (Entry<ProviderId, DeviceDescriptions> e : providerDescs.entrySet()) { 410 for (Entry<ProviderId, DeviceDescriptions> e : providerDescs.entrySet()) {
379 if (e.getKey().equals(primary)) { 411 if (e.getKey().equals(primary)) {
...@@ -392,7 +424,14 @@ public class SimpleDeviceStore ...@@ -392,7 +424,14 @@ public class SimpleDeviceStore
392 hwVersion, swVersion, serialNumber, annotations); 424 hwVersion, swVersion, serialNumber, annotations);
393 } 425 }
394 426
395 - // probably want composePort"s" also 427 + /**
428 + * Returns a Port, merging description given from multiple Providers.
429 + *
430 + * @param device device the port is on
431 + * @param number port number
432 + * @param providerDescs Collection of Descriptions from multiple providers
433 + * @return Port instance
434 + */
396 private Port composePort(Device device, PortNumber number, 435 private Port composePort(Device device, PortNumber number,
397 ConcurrentMap<ProviderId, DeviceDescriptions> providerDescs) { 436 ConcurrentMap<ProviderId, DeviceDescriptions> providerDescs) {
398 437
...@@ -445,18 +484,11 @@ public class SimpleDeviceStore ...@@ -445,18 +484,11 @@ public class SimpleDeviceStore
445 return fallBackPrimary; 484 return fallBackPrimary;
446 } 485 }
447 486
448 - // TODO: can be made generic
449 - private static final class InitConcurrentHashMap<K, V> implements
450 - ConcurrentInitializer<ConcurrentMap<K, V>> {
451 - @Override
452 - public ConcurrentMap<K, V> get() throws ConcurrentException {
453 - return new ConcurrentHashMap<>();
454 - }
455 - }
456 -
457 public static final class InitDeviceDescs 487 public static final class InitDeviceDescs
458 implements ConcurrentInitializer<DeviceDescriptions> { 488 implements ConcurrentInitializer<DeviceDescriptions> {
489 +
459 private final DeviceDescription deviceDesc; 490 private final DeviceDescription deviceDesc;
491 +
460 public InitDeviceDescs(DeviceDescription deviceDesc) { 492 public InitDeviceDescs(DeviceDescription deviceDesc) {
461 this.deviceDesc = checkNotNull(deviceDesc); 493 this.deviceDesc = checkNotNull(deviceDesc);
462 } 494 }
...@@ -471,8 +503,6 @@ public class SimpleDeviceStore ...@@ -471,8 +503,6 @@ public class SimpleDeviceStore
471 * Collection of Description of a Device and it's Ports given from a Provider. 503 * Collection of Description of a Device and it's Ports given from a Provider.
472 */ 504 */
473 private static class DeviceDescriptions { 505 private static class DeviceDescriptions {
474 - // private final DeviceId id;
475 - // private final ProviderId pid;
476 506
477 private final AtomicReference<DeviceDescription> deviceDesc; 507 private final AtomicReference<DeviceDescription> deviceDesc;
478 private final ConcurrentMap<PortNumber, PortDescription> portDescs; 508 private final ConcurrentMap<PortNumber, PortDescription> portDescs;
...@@ -490,10 +520,6 @@ public class SimpleDeviceStore ...@@ -490,10 +520,6 @@ public class SimpleDeviceStore
490 return portDescs.get(number); 520 return portDescs.get(number);
491 } 521 }
492 522
493 - public Collection<PortDescription> getPortDescs() {
494 - return Collections.unmodifiableCollection(portDescs.values());
495 - }
496 -
497 /** 523 /**
498 * Puts DeviceDescription, merging annotations as necessary. 524 * Puts DeviceDescription, merging annotations as necessary.
499 * 525 *
...@@ -504,7 +530,7 @@ public class SimpleDeviceStore ...@@ -504,7 +530,7 @@ public class SimpleDeviceStore
504 DeviceDescription oldOne = deviceDesc.get(); 530 DeviceDescription oldOne = deviceDesc.get();
505 DeviceDescription newOne = newDesc; 531 DeviceDescription newOne = newDesc;
506 if (oldOne != null) { 532 if (oldOne != null) {
507 - SparseAnnotations merged = merge(oldOne.annotations(), 533 + SparseAnnotations merged = union(oldOne.annotations(),
508 newDesc.annotations()); 534 newDesc.annotations());
509 newOne = new DefaultDeviceDescription(newOne, merged); 535 newOne = new DefaultDeviceDescription(newOne, merged);
510 } 536 }
...@@ -521,7 +547,7 @@ public class SimpleDeviceStore ...@@ -521,7 +547,7 @@ public class SimpleDeviceStore
521 PortDescription oldOne = portDescs.get(newDesc.portNumber()); 547 PortDescription oldOne = portDescs.get(newDesc.portNumber());
522 PortDescription newOne = newDesc; 548 PortDescription newOne = newDesc;
523 if (oldOne != null) { 549 if (oldOne != null) {
524 - SparseAnnotations merged = merge(oldOne.annotations(), 550 + SparseAnnotations merged = union(oldOne.annotations(),
525 newDesc.annotations()); 551 newDesc.annotations());
526 newOne = new DefaultPortDescription(newOne, merged); 552 newOne = new DefaultPortDescription(newOne, merged);
527 } 553 }
......
...@@ -12,9 +12,10 @@ import org.apache.felix.scr.annotations.Deactivate; ...@@ -12,9 +12,10 @@ import org.apache.felix.scr.annotations.Deactivate;
12 import org.apache.felix.scr.annotations.Service; 12 import org.apache.felix.scr.annotations.Service;
13 import org.onlab.onos.ApplicationId; 13 import org.onlab.onos.ApplicationId;
14 import org.onlab.onos.net.DeviceId; 14 import org.onlab.onos.net.DeviceId;
15 -import org.onlab.onos.net.flow.DefaultFlowRule; 15 +import org.onlab.onos.net.flow.DefaultFlowEntry;
16 +import org.onlab.onos.net.flow.FlowEntry;
17 +import org.onlab.onos.net.flow.FlowEntry.FlowEntryState;
16 import org.onlab.onos.net.flow.FlowRule; 18 import org.onlab.onos.net.flow.FlowRule;
17 -import org.onlab.onos.net.flow.FlowRule.FlowRuleState;
18 import org.onlab.onos.net.flow.FlowRuleEvent; 19 import org.onlab.onos.net.flow.FlowRuleEvent;
19 import org.onlab.onos.net.flow.FlowRuleEvent.Type; 20 import org.onlab.onos.net.flow.FlowRuleEvent.Type;
20 import org.onlab.onos.net.flow.FlowRuleStore; 21 import org.onlab.onos.net.flow.FlowRuleStore;
...@@ -38,8 +39,8 @@ public class SimpleFlowRuleStore ...@@ -38,8 +39,8 @@ public class SimpleFlowRuleStore
38 private final Logger log = getLogger(getClass()); 39 private final Logger log = getLogger(getClass());
39 40
40 // store entries as a pile of rules, no info about device tables 41 // store entries as a pile of rules, no info about device tables
41 - private final Multimap<DeviceId, FlowRule> flowEntries = 42 + private final Multimap<DeviceId, FlowEntry> flowEntries =
42 - ArrayListMultimap.<DeviceId, FlowRule>create(); 43 + ArrayListMultimap.<DeviceId, FlowEntry>create();
43 44
44 private final Multimap<ApplicationId, FlowRule> flowEntriesById = 45 private final Multimap<ApplicationId, FlowRule> flowEntriesById =
45 ArrayListMultimap.<ApplicationId, FlowRule>create(); 46 ArrayListMultimap.<ApplicationId, FlowRule>create();
...@@ -56,8 +57,13 @@ public class SimpleFlowRuleStore ...@@ -56,8 +57,13 @@ public class SimpleFlowRuleStore
56 57
57 58
58 @Override 59 @Override
59 - public synchronized FlowRule getFlowRule(FlowRule rule) { 60 + public int getFlowRuleCount() {
60 - for (FlowRule f : flowEntries.get(rule.deviceId())) { 61 + return flowEntries.size();
62 + }
63 +
64 + @Override
65 + public synchronized FlowEntry getFlowEntry(FlowRule rule) {
66 + for (FlowEntry f : flowEntries.get(rule.deviceId())) {
61 if (f.equals(rule)) { 67 if (f.equals(rule)) {
62 return f; 68 return f;
63 } 69 }
...@@ -66,8 +72,8 @@ public class SimpleFlowRuleStore ...@@ -66,8 +72,8 @@ public class SimpleFlowRuleStore
66 } 72 }
67 73
68 @Override 74 @Override
69 - public synchronized Iterable<FlowRule> getFlowEntries(DeviceId deviceId) { 75 + public synchronized Iterable<FlowEntry> getFlowEntries(DeviceId deviceId) {
70 - Collection<FlowRule> rules = flowEntries.get(deviceId); 76 + Collection<FlowEntry> rules = flowEntries.get(deviceId);
71 if (rules == null) { 77 if (rules == null) {
72 return Collections.emptyList(); 78 return Collections.emptyList();
73 } 79 }
...@@ -75,7 +81,7 @@ public class SimpleFlowRuleStore ...@@ -75,7 +81,7 @@ public class SimpleFlowRuleStore
75 } 81 }
76 82
77 @Override 83 @Override
78 - public synchronized Iterable<FlowRule> getFlowEntriesByAppId(ApplicationId appId) { 84 + public synchronized Iterable<FlowRule> getFlowRulesByAppId(ApplicationId appId) {
79 Collection<FlowRule> rules = flowEntriesById.get(appId); 85 Collection<FlowRule> rules = flowEntriesById.get(appId);
80 if (rules == null) { 86 if (rules == null) {
81 return Collections.emptyList(); 87 return Collections.emptyList();
...@@ -85,7 +91,7 @@ public class SimpleFlowRuleStore ...@@ -85,7 +91,7 @@ public class SimpleFlowRuleStore
85 91
86 @Override 92 @Override
87 public synchronized void storeFlowRule(FlowRule rule) { 93 public synchronized void storeFlowRule(FlowRule rule) {
88 - FlowRule f = new DefaultFlowRule(rule, FlowRuleState.PENDING_ADD); 94 + FlowEntry f = new DefaultFlowEntry(rule);
89 DeviceId did = f.deviceId(); 95 DeviceId did = f.deviceId();
90 if (!flowEntries.containsEntry(did, f)) { 96 if (!flowEntries.containsEntry(did, f)) {
91 flowEntries.put(did, f); 97 flowEntries.put(did, f);
...@@ -95,51 +101,42 @@ public class SimpleFlowRuleStore ...@@ -95,51 +101,42 @@ public class SimpleFlowRuleStore
95 101
96 @Override 102 @Override
97 public synchronized void deleteFlowRule(FlowRule rule) { 103 public synchronized void deleteFlowRule(FlowRule rule) {
98 - FlowRule f = new DefaultFlowRule(rule, FlowRuleState.PENDING_REMOVE); 104 + FlowEntry entry = getFlowEntry(rule);
99 - DeviceId did = f.deviceId(); 105 + if (entry == null) {
100 - 106 + //log.warn("Cannot find rule {}", rule);
101 - /* 107 + return;
102 - * find the rule and mark it for deletion.
103 - * Ultimately a flow removed will come remove it.
104 - */
105 -
106 - if (flowEntries.containsEntry(did, f)) {
107 - flowEntries.remove(did, f);
108 - flowEntries.put(did, f);
109 - flowEntriesById.remove(rule.appId(), rule);
110 } 108 }
109 + entry.setState(FlowEntryState.PENDING_REMOVE);
111 } 110 }
112 111
113 @Override 112 @Override
114 - public synchronized FlowRuleEvent addOrUpdateFlowRule(FlowRule rule) { 113 + public synchronized FlowRuleEvent addOrUpdateFlowRule(FlowEntry rule) {
115 DeviceId did = rule.deviceId(); 114 DeviceId did = rule.deviceId();
116 115
117 // check if this new rule is an update to an existing entry 116 // check if this new rule is an update to an existing entry
118 - FlowRule stored = getFlowRule(rule); 117 + FlowEntry stored = getFlowEntry(rule);
119 if (stored != null) { 118 if (stored != null) {
120 - // Multimaps support duplicates so we have to remove our rule 119 + stored.setBytes(rule.bytes());
121 - // and replace it with the current version. 120 + stored.setLife(rule.life());
122 - flowEntries.remove(did, rule); 121 + stored.setPackets(rule.packets());
123 - flowEntries.put(did, rule); 122 + if (stored.state() == FlowEntryState.PENDING_ADD) {
124 - 123 + stored.setState(FlowEntryState.ADDED);
125 - if (stored.state() == FlowRuleState.PENDING_ADD) {
126 return new FlowRuleEvent(Type.RULE_ADDED, rule); 124 return new FlowRuleEvent(Type.RULE_ADDED, rule);
127 } 125 }
128 return new FlowRuleEvent(Type.RULE_UPDATED, rule); 126 return new FlowRuleEvent(Type.RULE_UPDATED, rule);
129 } 127 }
130 128
131 - flowEntries.put(did, rule); 129 + //flowEntries.put(did, rule);
132 return null; 130 return null;
133 } 131 }
134 132
135 @Override 133 @Override
136 - public synchronized FlowRuleEvent removeFlowRule(FlowRule rule) { 134 + public synchronized FlowRuleEvent removeFlowRule(FlowEntry rule) {
137 - //synchronized (this) { 135 + // This is where one could mark a rule as removed and still keep it in the store.
138 if (flowEntries.remove(rule.deviceId(), rule)) { 136 if (flowEntries.remove(rule.deviceId(), rule)) {
139 return new FlowRuleEvent(RULE_REMOVED, rule); 137 return new FlowRuleEvent(RULE_REMOVED, rule);
140 } else { 138 } else {
141 return null; 139 return null;
142 } 140 }
143 - //}
144 } 141 }
145 } 142 }
......
1 +package org.onlab.onos.store.trivial.impl;
2 +
3 +import com.google.common.collect.ImmutableSet;
4 +import org.apache.felix.scr.annotations.Activate;
5 +import org.apache.felix.scr.annotations.Component;
6 +import org.apache.felix.scr.annotations.Deactivate;
7 +import org.apache.felix.scr.annotations.Service;
8 +import org.onlab.onos.net.intent.InstallableIntent;
9 +import org.onlab.onos.net.intent.Intent;
10 +import org.onlab.onos.net.intent.IntentEvent;
11 +import org.onlab.onos.net.intent.IntentId;
12 +import org.onlab.onos.net.intent.IntentState;
13 +import org.onlab.onos.net.intent.IntentStore;
14 +import org.onlab.onos.net.intent.IntentStoreDelegate;
15 +import org.onlab.onos.store.AbstractStore;
16 +import org.slf4j.Logger;
17 +
18 +import java.util.HashMap;
19 +import java.util.List;
20 +import java.util.Map;
21 +
22 +import static org.onlab.onos.net.intent.IntentState.*;
23 +import static org.slf4j.LoggerFactory.getLogger;
24 +
25 +@Component(immediate = true)
26 +@Service
27 +public class SimpleIntentStore
28 + extends AbstractStore<IntentEvent, IntentStoreDelegate>
29 + implements IntentStore {
30 +
31 + private final Logger log = getLogger(getClass());
32 + private final Map<IntentId, Intent> intents = new HashMap<>();
33 + private final Map<IntentId, IntentState> states = new HashMap<>();
34 + private final Map<IntentId, List<InstallableIntent>> installable = new HashMap<>();
35 +
36 + @Activate
37 + public void activate() {
38 + log.info("Started");
39 + }
40 +
41 + @Deactivate
42 + public void deactivate() {
43 + log.info("Stopped");
44 + }
45 +
46 + @Override
47 + public IntentEvent createIntent(Intent intent) {
48 + intents.put(intent.id(), intent);
49 + return this.setState(intent, IntentState.SUBMITTED);
50 + }
51 +
52 + @Override
53 + public IntentEvent removeIntent(IntentId intentId) {
54 + Intent intent = intents.remove(intentId);
55 + installable.remove(intentId);
56 + IntentEvent event = this.setState(intent, WITHDRAWN);
57 + states.remove(intentId);
58 + return event;
59 + }
60 +
61 + @Override
62 + public long getIntentCount() {
63 + return intents.size();
64 + }
65 +
66 + @Override
67 + public Iterable<Intent> getIntents() {
68 + return ImmutableSet.copyOf(intents.values());
69 + }
70 +
71 + @Override
72 + public Intent getIntent(IntentId intentId) {
73 + return intents.get(intentId);
74 + }
75 +
76 + @Override
77 + public IntentState getIntentState(IntentId id) {
78 + return states.get(id);
79 + }
80 +
81 + @Override
82 + public IntentEvent setState(Intent intent, IntentState state) {
83 + IntentId id = intent.id();
84 + states.put(id, state);
85 + IntentEvent.Type type = (state == SUBMITTED ? IntentEvent.Type.SUBMITTED :
86 + (state == INSTALLED ? IntentEvent.Type.INSTALLED :
87 + (state == FAILED ? IntentEvent.Type.FAILED :
88 + state == WITHDRAWN ? IntentEvent.Type.WITHDRAWN :
89 + null)));
90 + return type == null ? null : new IntentEvent(type, intent);
91 + }
92 +
93 + @Override
94 + public void addInstallableIntents(IntentId intentId, List<InstallableIntent> result) {
95 + installable.put(intentId, result);
96 + }
97 +
98 + @Override
99 + public List<InstallableIntent> getInstallableIntents(IntentId intentId) {
100 + return installable.get(intentId);
101 + }
102 +
103 + @Override
104 + public void removeInstalledIntents(IntentId intentId) {
105 + installable.remove(intentId);
106 + }
107 +
108 +}
1 package org.onlab.onos.store.trivial.impl; 1 package org.onlab.onos.store.trivial.impl;
2 2
3 +import com.google.common.base.Function;
4 +import com.google.common.base.Predicate;
5 +import com.google.common.collect.FluentIterable;
3 import com.google.common.collect.HashMultimap; 6 import com.google.common.collect.HashMultimap;
4 -import com.google.common.collect.ImmutableSet; 7 +import com.google.common.collect.SetMultimap;
5 -import com.google.common.collect.Multimap;
6 8
9 +import org.apache.commons.lang3.concurrent.ConcurrentUtils;
7 import org.apache.felix.scr.annotations.Activate; 10 import org.apache.felix.scr.annotations.Activate;
8 import org.apache.felix.scr.annotations.Component; 11 import org.apache.felix.scr.annotations.Component;
9 import org.apache.felix.scr.annotations.Deactivate; 12 import org.apache.felix.scr.annotations.Deactivate;
10 import org.apache.felix.scr.annotations.Service; 13 import org.apache.felix.scr.annotations.Service;
14 +import org.onlab.onos.net.AnnotationsUtil;
11 import org.onlab.onos.net.ConnectPoint; 15 import org.onlab.onos.net.ConnectPoint;
16 +import org.onlab.onos.net.DefaultAnnotations;
12 import org.onlab.onos.net.DefaultLink; 17 import org.onlab.onos.net.DefaultLink;
13 import org.onlab.onos.net.DeviceId; 18 import org.onlab.onos.net.DeviceId;
14 import org.onlab.onos.net.Link; 19 import org.onlab.onos.net.Link;
20 +import org.onlab.onos.net.SparseAnnotations;
21 +import org.onlab.onos.net.Link.Type;
15 import org.onlab.onos.net.LinkKey; 22 import org.onlab.onos.net.LinkKey;
23 +import org.onlab.onos.net.Provided;
24 +import org.onlab.onos.net.link.DefaultLinkDescription;
16 import org.onlab.onos.net.link.LinkDescription; 25 import org.onlab.onos.net.link.LinkDescription;
17 import org.onlab.onos.net.link.LinkEvent; 26 import org.onlab.onos.net.link.LinkEvent;
18 import org.onlab.onos.net.link.LinkStore; 27 import org.onlab.onos.net.link.LinkStore;
19 import org.onlab.onos.net.link.LinkStoreDelegate; 28 import org.onlab.onos.net.link.LinkStoreDelegate;
20 import org.onlab.onos.net.provider.ProviderId; 29 import org.onlab.onos.net.provider.ProviderId;
21 import org.onlab.onos.store.AbstractStore; 30 import org.onlab.onos.store.AbstractStore;
31 +import org.onlab.util.NewConcurrentHashMap;
22 import org.slf4j.Logger; 32 import org.slf4j.Logger;
23 33
24 import java.util.Collections; 34 import java.util.Collections;
25 import java.util.HashSet; 35 import java.util.HashSet;
26 -import java.util.Map;
27 import java.util.Set; 36 import java.util.Set;
37 +import java.util.Map.Entry;
28 import java.util.concurrent.ConcurrentHashMap; 38 import java.util.concurrent.ConcurrentHashMap;
39 +import java.util.concurrent.ConcurrentMap;
29 40
41 +import static org.onlab.onos.net.DefaultAnnotations.union;
42 +import static org.onlab.onos.net.DefaultAnnotations.merge;
30 import static org.onlab.onos.net.Link.Type.DIRECT; 43 import static org.onlab.onos.net.Link.Type.DIRECT;
31 import static org.onlab.onos.net.Link.Type.INDIRECT; 44 import static org.onlab.onos.net.Link.Type.INDIRECT;
32 import static org.onlab.onos.net.link.LinkEvent.Type.*; 45 import static org.onlab.onos.net.link.LinkEvent.Type.*;
33 import static org.slf4j.LoggerFactory.getLogger; 46 import static org.slf4j.LoggerFactory.getLogger;
47 +import static com.google.common.collect.Multimaps.synchronizedSetMultimap;
48 +import static com.google.common.base.Predicates.notNull;
34 49
35 /** 50 /**
36 * Manages inventory of infrastructure links using trivial in-memory structures 51 * Manages inventory of infrastructure links using trivial in-memory structures
...@@ -45,11 +60,17 @@ public class SimpleLinkStore ...@@ -45,11 +60,17 @@ public class SimpleLinkStore
45 private final Logger log = getLogger(getClass()); 60 private final Logger log = getLogger(getClass());
46 61
47 // Link inventory 62 // Link inventory
48 - private final Map<LinkKey, DefaultLink> links = new ConcurrentHashMap<>(); 63 + private final ConcurrentMap<LinkKey,
64 + ConcurrentMap<ProviderId, LinkDescription>>
65 + linkDescs = new ConcurrentHashMap<>();
66 +
67 + // Link instance cache
68 + private final ConcurrentMap<LinkKey, Link> links = new ConcurrentHashMap<>();
49 69
50 // Egress and ingress link sets 70 // Egress and ingress link sets
51 - private final Multimap<DeviceId, Link> srcLinks = HashMultimap.create(); 71 + private final SetMultimap<DeviceId, LinkKey> srcLinks = createSynchronizedHashMultiMap();
52 - private final Multimap<DeviceId, Link> dstLinks = HashMultimap.create(); 72 + private final SetMultimap<DeviceId, LinkKey> dstLinks = createSynchronizedHashMultiMap();
73 +
53 74
54 @Activate 75 @Activate
55 public void activate() { 76 public void activate() {
...@@ -58,6 +79,10 @@ public class SimpleLinkStore ...@@ -58,6 +79,10 @@ public class SimpleLinkStore
58 79
59 @Deactivate 80 @Deactivate
60 public void deactivate() { 81 public void deactivate() {
82 + linkDescs.clear();
83 + links.clear();
84 + srcLinks.clear();
85 + dstLinks.clear();
61 log.info("Stopped"); 86 log.info("Stopped");
62 } 87 }
63 88
...@@ -68,17 +93,29 @@ public class SimpleLinkStore ...@@ -68,17 +93,29 @@ public class SimpleLinkStore
68 93
69 @Override 94 @Override
70 public Iterable<Link> getLinks() { 95 public Iterable<Link> getLinks() {
71 - return Collections.unmodifiableSet(new HashSet<Link>(links.values())); 96 + return Collections.unmodifiableCollection(links.values());
72 } 97 }
73 98
74 @Override 99 @Override
75 public Set<Link> getDeviceEgressLinks(DeviceId deviceId) { 100 public Set<Link> getDeviceEgressLinks(DeviceId deviceId) {
76 - return ImmutableSet.copyOf(srcLinks.get(deviceId)); 101 + // lock for iteration
102 + synchronized (srcLinks) {
103 + return FluentIterable.from(srcLinks.get(deviceId))
104 + .transform(lookupLink())
105 + .filter(notNull())
106 + .toSet();
107 + }
77 } 108 }
78 109
79 @Override 110 @Override
80 public Set<Link> getDeviceIngressLinks(DeviceId deviceId) { 111 public Set<Link> getDeviceIngressLinks(DeviceId deviceId) {
81 - return ImmutableSet.copyOf(dstLinks.get(deviceId)); 112 + // lock for iteration
113 + synchronized (dstLinks) {
114 + return FluentIterable.from(dstLinks.get(deviceId))
115 + .transform(lookupLink())
116 + .filter(notNull())
117 + .toSet();
118 + }
82 } 119 }
83 120
84 @Override 121 @Override
...@@ -89,9 +126,9 @@ public class SimpleLinkStore ...@@ -89,9 +126,9 @@ public class SimpleLinkStore
89 @Override 126 @Override
90 public Set<Link> getEgressLinks(ConnectPoint src) { 127 public Set<Link> getEgressLinks(ConnectPoint src) {
91 Set<Link> egress = new HashSet<>(); 128 Set<Link> egress = new HashSet<>();
92 - for (Link link : srcLinks.get(src.deviceId())) { 129 + for (LinkKey linkKey : srcLinks.get(src.deviceId())) {
93 - if (link.src().equals(src)) { 130 + if (linkKey.src().equals(src)) {
94 - egress.add(link); 131 + egress.add(links.get(linkKey));
95 } 132 }
96 } 133 }
97 return egress; 134 return egress;
...@@ -100,9 +137,9 @@ public class SimpleLinkStore ...@@ -100,9 +137,9 @@ public class SimpleLinkStore
100 @Override 137 @Override
101 public Set<Link> getIngressLinks(ConnectPoint dst) { 138 public Set<Link> getIngressLinks(ConnectPoint dst) {
102 Set<Link> ingress = new HashSet<>(); 139 Set<Link> ingress = new HashSet<>();
103 - for (Link link : dstLinks.get(dst.deviceId())) { 140 + for (LinkKey linkKey : dstLinks.get(dst.deviceId())) {
104 - if (link.dst().equals(dst)) { 141 + if (linkKey.dst().equals(dst)) {
105 - ingress.add(link); 142 + ingress.add(links.get(linkKey));
106 } 143 }
107 } 144 }
108 return ingress; 145 return ingress;
...@@ -112,56 +149,172 @@ public class SimpleLinkStore ...@@ -112,56 +149,172 @@ public class SimpleLinkStore
112 public LinkEvent createOrUpdateLink(ProviderId providerId, 149 public LinkEvent createOrUpdateLink(ProviderId providerId,
113 LinkDescription linkDescription) { 150 LinkDescription linkDescription) {
114 LinkKey key = new LinkKey(linkDescription.src(), linkDescription.dst()); 151 LinkKey key = new LinkKey(linkDescription.src(), linkDescription.dst());
115 - DefaultLink link = links.get(key); 152 +
116 - if (link == null) { 153 + ConcurrentMap<ProviderId, LinkDescription> descs = getLinkDescriptions(key);
117 - return createLink(providerId, key, linkDescription); 154 + synchronized (descs) {
155 + final Link oldLink = links.get(key);
156 + // update description
157 + createOrUpdateLinkDescription(descs, providerId, linkDescription);
158 + final Link newLink = composeLink(descs);
159 + if (oldLink == null) {
160 + return createLink(key, newLink);
161 + }
162 + return updateLink(key, oldLink, newLink);
118 } 163 }
119 - return updateLink(providerId, link, key, linkDescription);
120 } 164 }
121 165
122 - // Creates and stores the link and returns the appropriate event. 166 + // Guarded by linkDescs value (=locking each Link)
123 - private LinkEvent createLink(ProviderId providerId, LinkKey key, 167 + private LinkDescription createOrUpdateLinkDescription(
168 + ConcurrentMap<ProviderId, LinkDescription> descs,
169 + ProviderId providerId,
124 LinkDescription linkDescription) { 170 LinkDescription linkDescription) {
125 - DefaultLink link = new DefaultLink(providerId, key.src(), key.dst(), 171 +
126 - linkDescription.type()); 172 + // merge existing attributes and merge
127 - synchronized (this) { 173 + LinkDescription oldDesc = descs.get(providerId);
128 - links.put(key, link); 174 + LinkDescription newDesc = linkDescription;
129 - srcLinks.put(link.src().deviceId(), link); 175 + if (oldDesc != null) {
130 - dstLinks.put(link.dst().deviceId(), link); 176 + SparseAnnotations merged = union(oldDesc.annotations(),
177 + linkDescription.annotations());
178 + newDesc = new DefaultLinkDescription(
179 + linkDescription.src(),
180 + linkDescription.dst(),
181 + linkDescription.type(), merged);
182 + }
183 + return descs.put(providerId, newDesc);
131 } 184 }
132 - return new LinkEvent(LINK_ADDED, link); 185 +
186 + // Creates and stores the link and returns the appropriate event.
187 + // Guarded by linkDescs value (=locking each Link)
188 + private LinkEvent createLink(LinkKey key, Link newLink) {
189 +
190 + if (newLink.providerId().isAncillary()) {
191 + // TODO: revisit ancillary only Link handling
192 +
193 + // currently treating ancillary only as down (not visible outside)
194 + return null;
195 + }
196 +
197 + links.put(key, newLink);
198 + srcLinks.put(newLink.src().deviceId(), key);
199 + dstLinks.put(newLink.dst().deviceId(), key);
200 + return new LinkEvent(LINK_ADDED, newLink);
133 } 201 }
134 202
135 // Updates, if necessary the specified link and returns the appropriate event. 203 // Updates, if necessary the specified link and returns the appropriate event.
136 - private LinkEvent updateLink(ProviderId providerId, DefaultLink link, 204 + // Guarded by linkDescs value (=locking each Link)
137 - LinkKey key, LinkDescription linkDescription) { 205 + private LinkEvent updateLink(LinkKey key, Link oldLink, Link newLink) {
138 - if (link.type() == INDIRECT && linkDescription.type() == DIRECT) { 206 +
139 - synchronized (this) { 207 + if (newLink.providerId().isAncillary()) {
140 - srcLinks.remove(link.src().deviceId(), link); 208 + // TODO: revisit ancillary only Link handling
141 - dstLinks.remove(link.dst().deviceId(), link); 209 +
142 - 210 + // currently treating ancillary only as down (not visible outside)
143 - DefaultLink updated = 211 + return null;
144 - new DefaultLink(providerId, link.src(), link.dst(),
145 - linkDescription.type());
146 - links.put(key, updated);
147 - srcLinks.put(link.src().deviceId(), updated);
148 - dstLinks.put(link.dst().deviceId(), updated);
149 - return new LinkEvent(LINK_UPDATED, updated);
150 } 212 }
213 +
214 + if ((oldLink.type() == INDIRECT && newLink.type() == DIRECT) ||
215 + !AnnotationsUtil.isEqual(oldLink.annotations(), newLink.annotations())) {
216 +
217 + links.put(key, newLink);
218 + // strictly speaking following can be ommitted
219 + srcLinks.put(oldLink.src().deviceId(), key);
220 + dstLinks.put(oldLink.dst().deviceId(), key);
221 + return new LinkEvent(LINK_UPDATED, newLink);
151 } 222 }
152 return null; 223 return null;
153 } 224 }
154 225
155 @Override 226 @Override
156 public LinkEvent removeLink(ConnectPoint src, ConnectPoint dst) { 227 public LinkEvent removeLink(ConnectPoint src, ConnectPoint dst) {
157 - synchronized (this) { 228 + final LinkKey key = new LinkKey(src, dst);
158 - Link link = links.remove(new LinkKey(src, dst)); 229 + ConcurrentMap<ProviderId, LinkDescription> descs = getLinkDescriptions(key);
230 + synchronized (descs) {
231 + Link link = links.remove(key);
232 + descs.clear();
159 if (link != null) { 233 if (link != null) {
160 - srcLinks.remove(link.src().deviceId(), link); 234 + srcLinks.remove(link.src().deviceId(), key);
161 - dstLinks.remove(link.dst().deviceId(), link); 235 + dstLinks.remove(link.dst().deviceId(), key);
162 return new LinkEvent(LINK_REMOVED, link); 236 return new LinkEvent(LINK_REMOVED, link);
163 } 237 }
164 return null; 238 return null;
165 } 239 }
166 } 240 }
241 +
242 + private static <K, V> SetMultimap<K, V> createSynchronizedHashMultiMap() {
243 + return synchronizedSetMultimap(HashMultimap.<K, V>create());
244 + }
245 +
246 + /**
247 + * @return primary ProviderID, or randomly chosen one if none exists
248 + */
249 + private ProviderId pickPrimaryPID(
250 + ConcurrentMap<ProviderId, LinkDescription> providerDescs) {
251 +
252 + ProviderId fallBackPrimary = null;
253 + for (Entry<ProviderId, LinkDescription> e : providerDescs.entrySet()) {
254 + if (!e.getKey().isAncillary()) {
255 + return e.getKey();
256 + } else if (fallBackPrimary == null) {
257 + // pick randomly as a fallback in case there is no primary
258 + fallBackPrimary = e.getKey();
259 + }
260 + }
261 + return fallBackPrimary;
262 + }
263 +
264 + private Link composeLink(ConcurrentMap<ProviderId, LinkDescription> descs) {
265 + ProviderId primary = pickPrimaryPID(descs);
266 + LinkDescription base = descs.get(primary);
267 +
268 + ConnectPoint src = base.src();
269 + ConnectPoint dst = base.dst();
270 + Type type = base.type();
271 + DefaultAnnotations annotations = DefaultAnnotations.builder().build();
272 + annotations = merge(annotations, base.annotations());
273 +
274 + for (Entry<ProviderId, LinkDescription> e : descs.entrySet()) {
275 + if (primary.equals(e.getKey())) {
276 + continue;
277 + }
278 +
279 + // TODO: should keep track of Description timestamp
280 + // and only merge conflicting keys when timestamp is newer
281 + // Currently assuming there will never be a key conflict between
282 + // providers
283 +
284 + // annotation merging. not so efficient, should revisit later
285 + annotations = merge(annotations, e.getValue().annotations());
286 + }
287 +
288 + return new DefaultLink(primary , src, dst, type, annotations);
289 + }
290 +
291 + private ConcurrentMap<ProviderId, LinkDescription> getLinkDescriptions(LinkKey key) {
292 + return ConcurrentUtils.createIfAbsentUnchecked(linkDescs, key,
293 + NewConcurrentHashMap.<ProviderId, LinkDescription>ifNeeded());
294 + }
295 +
296 + private final Function<LinkKey, Link> lookupLink = new LookupLink();
297 + private Function<LinkKey, Link> lookupLink() {
298 + return lookupLink;
299 + }
300 +
301 + private final class LookupLink implements Function<LinkKey, Link> {
302 + @Override
303 + public Link apply(LinkKey input) {
304 + return links.get(input);
305 + }
306 + }
307 +
308 + private static final Predicate<Provided> IS_PRIMARY = new IsPrimary();
309 + private static final Predicate<Provided> isPrimary() {
310 + return IS_PRIMARY;
311 + }
312 +
313 + private static final class IsPrimary implements Predicate<Provided> {
314 +
315 + @Override
316 + public boolean apply(Provided input) {
317 + return !input.providerId().isAncillary();
318 + }
319 + }
167 } 320 }
......
...@@ -124,7 +124,8 @@ public class SimpleTopologyStore ...@@ -124,7 +124,8 @@ public class SimpleTopologyStore
124 // Promote the new topology to current and return a ready-to-send event. 124 // Promote the new topology to current and return a ready-to-send event.
125 synchronized (this) { 125 synchronized (this) {
126 current = newTopology; 126 current = newTopology;
127 - return new TopologyEvent(TopologyEvent.Type.TOPOLOGY_CHANGED, current); 127 + return new TopologyEvent(TopologyEvent.Type.TOPOLOGY_CHANGED,
128 + current, reasons);
128 } 129 }
129 } 130 }
130 131
......
...@@ -103,17 +103,19 @@ public class SimpleDeviceStoreTest { ...@@ -103,17 +103,19 @@ public class SimpleDeviceStoreTest {
103 simpleDeviceStore.deactivate(); 103 simpleDeviceStore.deactivate();
104 } 104 }
105 105
106 - private void putDevice(DeviceId deviceId, String swVersion) { 106 + private void putDevice(DeviceId deviceId, String swVersion,
107 + SparseAnnotations... annotations) {
107 DeviceDescription description = 108 DeviceDescription description =
108 new DefaultDeviceDescription(deviceId.uri(), SWITCH, MFR, 109 new DefaultDeviceDescription(deviceId.uri(), SWITCH, MFR,
109 - HW, swVersion, SN); 110 + HW, swVersion, SN, annotations);
110 deviceStore.createOrUpdateDevice(PID, deviceId, description); 111 deviceStore.createOrUpdateDevice(PID, deviceId, description);
111 } 112 }
112 113
113 - private void putDeviceAncillary(DeviceId deviceId, String swVersion) { 114 + private void putDeviceAncillary(DeviceId deviceId, String swVersion,
115 + SparseAnnotations... annotations) {
114 DeviceDescription description = 116 DeviceDescription description =
115 new DefaultDeviceDescription(deviceId.uri(), SWITCH, MFR, 117 new DefaultDeviceDescription(deviceId.uri(), SWITCH, MFR,
116 - HW, swVersion, SN); 118 + HW, swVersion, SN, annotations);
117 deviceStore.createOrUpdateDevice(PIDA, deviceId, description); 119 deviceStore.createOrUpdateDevice(PIDA, deviceId, description);
118 } 120 }
119 121
...@@ -126,6 +128,7 @@ public class SimpleDeviceStoreTest { ...@@ -126,6 +128,7 @@ public class SimpleDeviceStoreTest {
126 assertEquals(SN, device.serialNumber()); 128 assertEquals(SN, device.serialNumber());
127 } 129 }
128 130
131 + // TODO slice this out somewhere
129 /** 132 /**
130 * Verifies that Annotations created by merging {@code annotations} is 133 * Verifies that Annotations created by merging {@code annotations} is
131 * equal to actual Annotations. 134 * equal to actual Annotations.
...@@ -133,7 +136,7 @@ public class SimpleDeviceStoreTest { ...@@ -133,7 +136,7 @@ public class SimpleDeviceStoreTest {
133 * @param actual Annotations to check 136 * @param actual Annotations to check
134 * @param annotations 137 * @param annotations
135 */ 138 */
136 - private static void assertAnnotationsEquals(Annotations actual, SparseAnnotations... annotations) { 139 + public static void assertAnnotationsEquals(Annotations actual, SparseAnnotations... annotations) {
137 DefaultAnnotations expected = DefaultAnnotations.builder().build(); 140 DefaultAnnotations expected = DefaultAnnotations.builder().build();
138 for (SparseAnnotations a : annotations) { 141 for (SparseAnnotations a : annotations) {
139 expected = DefaultAnnotations.merge(expected, a); 142 expected = DefaultAnnotations.merge(expected, a);
...@@ -347,6 +350,7 @@ public class SimpleDeviceStoreTest { ...@@ -347,6 +350,7 @@ public class SimpleDeviceStoreTest {
347 assertFalse("Port is disabled", event.port().isEnabled()); 350 assertFalse("Port is disabled", event.port().isEnabled());
348 351
349 } 352 }
353 +
350 @Test 354 @Test
351 public final void testUpdatePortStatusAncillary() { 355 public final void testUpdatePortStatusAncillary() {
352 putDeviceAncillary(DID1, SW1); 356 putDeviceAncillary(DID1, SW1);
...@@ -435,16 +439,37 @@ public class SimpleDeviceStoreTest { ...@@ -435,16 +439,37 @@ public class SimpleDeviceStoreTest {
435 439
436 @Test 440 @Test
437 public final void testRemoveDevice() { 441 public final void testRemoveDevice() {
438 - putDevice(DID1, SW1); 442 + putDevice(DID1, SW1, A1);
443 + List<PortDescription> pds = Arrays.<PortDescription>asList(
444 + new DefaultPortDescription(P1, true, A2)
445 + );
446 + deviceStore.updatePorts(PID, DID1, pds);
439 putDevice(DID2, SW1); 447 putDevice(DID2, SW1);
440 448
441 assertEquals(2, deviceStore.getDeviceCount()); 449 assertEquals(2, deviceStore.getDeviceCount());
450 + assertEquals(1, deviceStore.getPorts(DID1).size());
451 + assertAnnotationsEquals(deviceStore.getDevice(DID1).annotations(), A1);
452 + assertAnnotationsEquals(deviceStore.getPort(DID1, P1).annotations(), A2);
442 453
443 DeviceEvent event = deviceStore.removeDevice(DID1); 454 DeviceEvent event = deviceStore.removeDevice(DID1);
444 assertEquals(DEVICE_REMOVED, event.type()); 455 assertEquals(DEVICE_REMOVED, event.type());
445 assertDevice(DID1, SW1, event.subject()); 456 assertDevice(DID1, SW1, event.subject());
446 457
447 assertEquals(1, deviceStore.getDeviceCount()); 458 assertEquals(1, deviceStore.getDeviceCount());
459 + assertEquals(0, deviceStore.getPorts(DID1).size());
460 +
461 + // putBack Device, Port w/o annotation
462 + putDevice(DID1, SW1);
463 + List<PortDescription> pds2 = Arrays.<PortDescription>asList(
464 + new DefaultPortDescription(P1, true)
465 + );
466 + deviceStore.updatePorts(PID, DID1, pds2);
467 +
468 + // annotations should not survive
469 + assertEquals(2, deviceStore.getDeviceCount());
470 + assertEquals(1, deviceStore.getPorts(DID1).size());
471 + assertAnnotationsEquals(deviceStore.getDevice(DID1).annotations());
472 + assertAnnotationsEquals(deviceStore.getPort(DID1, P1).annotations());
448 } 473 }
449 474
450 // If Delegates should be called only on remote events, 475 // If Delegates should be called only on remote events,
......
...@@ -4,7 +4,9 @@ import static org.junit.Assert.*; ...@@ -4,7 +4,9 @@ import static org.junit.Assert.*;
4 import static org.onlab.onos.net.DeviceId.deviceId; 4 import static org.onlab.onos.net.DeviceId.deviceId;
5 import static org.onlab.onos.net.Link.Type.*; 5 import static org.onlab.onos.net.Link.Type.*;
6 import static org.onlab.onos.net.link.LinkEvent.Type.*; 6 import static org.onlab.onos.net.link.LinkEvent.Type.*;
7 +import static org.onlab.onos.store.trivial.impl.SimpleDeviceStoreTest.assertAnnotationsEquals;
7 8
9 +import java.util.Collections;
8 import java.util.HashMap; 10 import java.util.HashMap;
9 import java.util.Map; 11 import java.util.Map;
10 import java.util.Set; 12 import java.util.Set;
...@@ -18,10 +20,12 @@ import org.junit.BeforeClass; ...@@ -18,10 +20,12 @@ import org.junit.BeforeClass;
18 import org.junit.Ignore; 20 import org.junit.Ignore;
19 import org.junit.Test; 21 import org.junit.Test;
20 import org.onlab.onos.net.ConnectPoint; 22 import org.onlab.onos.net.ConnectPoint;
23 +import org.onlab.onos.net.DefaultAnnotations;
21 import org.onlab.onos.net.DeviceId; 24 import org.onlab.onos.net.DeviceId;
22 import org.onlab.onos.net.Link; 25 import org.onlab.onos.net.Link;
23 import org.onlab.onos.net.LinkKey; 26 import org.onlab.onos.net.LinkKey;
24 import org.onlab.onos.net.PortNumber; 27 import org.onlab.onos.net.PortNumber;
28 +import org.onlab.onos.net.SparseAnnotations;
25 import org.onlab.onos.net.Link.Type; 29 import org.onlab.onos.net.Link.Type;
26 import org.onlab.onos.net.link.DefaultLinkDescription; 30 import org.onlab.onos.net.link.DefaultLinkDescription;
27 import org.onlab.onos.net.link.LinkEvent; 31 import org.onlab.onos.net.link.LinkEvent;
...@@ -37,6 +41,7 @@ import com.google.common.collect.Iterables; ...@@ -37,6 +41,7 @@ import com.google.common.collect.Iterables;
37 public class SimpleLinkStoreTest { 41 public class SimpleLinkStoreTest {
38 42
39 private static final ProviderId PID = new ProviderId("of", "foo"); 43 private static final ProviderId PID = new ProviderId("of", "foo");
44 + private static final ProviderId PIDA = new ProviderId("of", "bar", true);
40 private static final DeviceId DID1 = deviceId("of:foo"); 45 private static final DeviceId DID1 = deviceId("of:foo");
41 private static final DeviceId DID2 = deviceId("of:bar"); 46 private static final DeviceId DID2 = deviceId("of:bar");
42 47
...@@ -44,6 +49,23 @@ public class SimpleLinkStoreTest { ...@@ -44,6 +49,23 @@ public class SimpleLinkStoreTest {
44 private static final PortNumber P2 = PortNumber.portNumber(2); 49 private static final PortNumber P2 = PortNumber.portNumber(2);
45 private static final PortNumber P3 = PortNumber.portNumber(3); 50 private static final PortNumber P3 = PortNumber.portNumber(3);
46 51
52 + private static final SparseAnnotations A1 = DefaultAnnotations.builder()
53 + .set("A1", "a1")
54 + .set("B1", "b1")
55 + .build();
56 + private static final SparseAnnotations A1_2 = DefaultAnnotations.builder()
57 + .remove("A1")
58 + .set("B3", "b3")
59 + .build();
60 + private static final SparseAnnotations A2 = DefaultAnnotations.builder()
61 + .set("A2", "a2")
62 + .set("B2", "b2")
63 + .build();
64 + private static final SparseAnnotations A2_2 = DefaultAnnotations.builder()
65 + .remove("A2")
66 + .set("B4", "b4")
67 + .build();
68 +
47 69
48 private SimpleLinkStore simpleLinkStore; 70 private SimpleLinkStore simpleLinkStore;
49 private LinkStore linkStore; 71 private LinkStore linkStore;
...@@ -69,16 +91,17 @@ public class SimpleLinkStoreTest { ...@@ -69,16 +91,17 @@ public class SimpleLinkStoreTest {
69 } 91 }
70 92
71 private void putLink(DeviceId srcId, PortNumber srcNum, 93 private void putLink(DeviceId srcId, PortNumber srcNum,
72 - DeviceId dstId, PortNumber dstNum, Type type) { 94 + DeviceId dstId, PortNumber dstNum, Type type,
95 + SparseAnnotations... annotations) {
73 ConnectPoint src = new ConnectPoint(srcId, srcNum); 96 ConnectPoint src = new ConnectPoint(srcId, srcNum);
74 ConnectPoint dst = new ConnectPoint(dstId, dstNum); 97 ConnectPoint dst = new ConnectPoint(dstId, dstNum);
75 - linkStore.createOrUpdateLink(PID, new DefaultLinkDescription(src, dst, type)); 98 + linkStore.createOrUpdateLink(PID, new DefaultLinkDescription(src, dst, type, annotations));
76 } 99 }
77 100
78 - private void putLink(LinkKey key, Type type) { 101 + private void putLink(LinkKey key, Type type, SparseAnnotations... annotations) {
79 putLink(key.src().deviceId(), key.src().port(), 102 putLink(key.src().deviceId(), key.src().port(),
80 key.dst().deviceId(), key.dst().port(), 103 key.dst().deviceId(), key.dst().port(),
81 - type); 104 + type, annotations);
82 } 105 }
83 106
84 private static void assertLink(DeviceId srcId, PortNumber srcNum, 107 private static void assertLink(DeviceId srcId, PortNumber srcNum,
...@@ -270,14 +293,67 @@ public class SimpleLinkStoreTest { ...@@ -270,14 +293,67 @@ public class SimpleLinkStoreTest {
270 } 293 }
271 294
272 @Test 295 @Test
296 + public final void testCreateOrUpdateLinkAncillary() {
297 + ConnectPoint src = new ConnectPoint(DID1, P1);
298 + ConnectPoint dst = new ConnectPoint(DID2, P2);
299 +
300 + // add Ancillary link
301 + LinkEvent event = linkStore.createOrUpdateLink(PIDA,
302 + new DefaultLinkDescription(src, dst, INDIRECT, A1));
303 +
304 + assertNull("Ancillary only link is ignored", event);
305 +
306 + // add Primary link
307 + LinkEvent event2 = linkStore.createOrUpdateLink(PID,
308 + new DefaultLinkDescription(src, dst, INDIRECT, A2));
309 +
310 + assertLink(DID1, P1, DID2, P2, INDIRECT, event2.subject());
311 + assertAnnotationsEquals(event2.subject().annotations(), A2, A1);
312 + assertEquals(LINK_ADDED, event2.type());
313 +
314 + // update link type
315 + LinkEvent event3 = linkStore.createOrUpdateLink(PID,
316 + new DefaultLinkDescription(src, dst, DIRECT, A2));
317 + assertLink(DID1, P1, DID2, P2, DIRECT, event3.subject());
318 + assertAnnotationsEquals(event3.subject().annotations(), A2, A1);
319 + assertEquals(LINK_UPDATED, event3.type());
320 +
321 +
322 + // no change
323 + LinkEvent event4 = linkStore.createOrUpdateLink(PID,
324 + new DefaultLinkDescription(src, dst, DIRECT));
325 + assertNull("No change event expected", event4);
326 +
327 + // update link annotation (Primary)
328 + LinkEvent event5 = linkStore.createOrUpdateLink(PID,
329 + new DefaultLinkDescription(src, dst, DIRECT, A2_2));
330 + assertLink(DID1, P1, DID2, P2, DIRECT, event5.subject());
331 + assertAnnotationsEquals(event5.subject().annotations(), A2, A2_2, A1);
332 + assertEquals(LINK_UPDATED, event5.type());
333 +
334 + // update link annotation (Ancillary)
335 + LinkEvent event6 = linkStore.createOrUpdateLink(PIDA,
336 + new DefaultLinkDescription(src, dst, DIRECT, A1_2));
337 + assertLink(DID1, P1, DID2, P2, DIRECT, event6.subject());
338 + assertAnnotationsEquals(event6.subject().annotations(), A2, A2_2, A1, A1_2);
339 + assertEquals(LINK_UPDATED, event6.type());
340 +
341 + // update link type (Ancillary) : ignored
342 + LinkEvent event7 = linkStore.createOrUpdateLink(PIDA,
343 + new DefaultLinkDescription(src, dst, EDGE));
344 + assertNull("Ancillary change other than annotation is ignored", event7);
345 + }
346 +
347 +
348 + @Test
273 public final void testRemoveLink() { 349 public final void testRemoveLink() {
274 final ConnectPoint d1P1 = new ConnectPoint(DID1, P1); 350 final ConnectPoint d1P1 = new ConnectPoint(DID1, P1);
275 final ConnectPoint d2P2 = new ConnectPoint(DID2, P2); 351 final ConnectPoint d2P2 = new ConnectPoint(DID2, P2);
276 LinkKey linkId1 = new LinkKey(d1P1, d2P2); 352 LinkKey linkId1 = new LinkKey(d1P1, d2P2);
277 LinkKey linkId2 = new LinkKey(d2P2, d1P1); 353 LinkKey linkId2 = new LinkKey(d2P2, d1P1);
278 354
279 - putLink(linkId1, DIRECT); 355 + putLink(linkId1, DIRECT, A1);
280 - putLink(linkId2, DIRECT); 356 + putLink(linkId2, DIRECT, A2);
281 357
282 // DID1,P1 => DID2,P2 358 // DID1,P1 => DID2,P2
283 // DID2,P2 => DID1,P1 359 // DID2,P2 => DID1,P1
...@@ -285,10 +361,41 @@ public class SimpleLinkStoreTest { ...@@ -285,10 +361,41 @@ public class SimpleLinkStoreTest {
285 361
286 LinkEvent event = linkStore.removeLink(d1P1, d2P2); 362 LinkEvent event = linkStore.removeLink(d1P1, d2P2);
287 assertEquals(LINK_REMOVED, event.type()); 363 assertEquals(LINK_REMOVED, event.type());
364 + assertAnnotationsEquals(event.subject().annotations(), A1);
288 LinkEvent event2 = linkStore.removeLink(d1P1, d2P2); 365 LinkEvent event2 = linkStore.removeLink(d1P1, d2P2);
289 assertNull(event2); 366 assertNull(event2);
290 367
291 assertLink(linkId2, DIRECT, linkStore.getLink(d2P2, d1P1)); 368 assertLink(linkId2, DIRECT, linkStore.getLink(d2P2, d1P1));
369 + assertAnnotationsEquals(linkStore.getLink(d2P2, d1P1).annotations(), A2);
370 +
371 + // annotations, etc. should not survive remove
372 + putLink(linkId1, DIRECT);
373 + assertLink(linkId1, DIRECT, linkStore.getLink(d1P1, d2P2));
374 + assertAnnotationsEquals(linkStore.getLink(d1P1, d2P2).annotations());
375 + }
376 +
377 + @Test
378 + public final void testAncillaryOnlyNotVisible() {
379 + ConnectPoint src = new ConnectPoint(DID1, P1);
380 + ConnectPoint dst = new ConnectPoint(DID2, P2);
381 +
382 + // add Ancillary link
383 + linkStore.createOrUpdateLink(PIDA,
384 + new DefaultLinkDescription(src, dst, INDIRECT, A1));
385 +
386 + // Ancillary only link should not be visible
387 + assertEquals(0, linkStore.getLinkCount());
388 +
389 + assertTrue(Iterables.isEmpty(linkStore.getLinks()));
390 +
391 + assertNull(linkStore.getLink(src, dst));
392 +
393 + assertEquals(Collections.emptySet(), linkStore.getIngressLinks(dst));
394 +
395 + assertEquals(Collections.emptySet(), linkStore.getEgressLinks(src));
396 +
397 + assertEquals(Collections.emptySet(), linkStore.getDeviceEgressLinks(DID1));
398 + assertEquals(Collections.emptySet(), linkStore.getDeviceIngressLinks(DID2));
292 } 399 }
293 400
294 // If Delegates should be called only on remote events, 401 // If Delegates should be called only on remote events,
......
...@@ -9,6 +9,12 @@ ...@@ -9,6 +9,12 @@
9 <bundle>mvn:org.apache.commons/commons-lang3/3.3.2</bundle> 9 <bundle>mvn:org.apache.commons/commons-lang3/3.3.2</bundle>
10 <bundle>mvn:com.google.guava/guava/18.0</bundle> 10 <bundle>mvn:com.google.guava/guava/18.0</bundle>
11 <bundle>mvn:io.netty/netty/3.9.2.Final</bundle> 11 <bundle>mvn:io.netty/netty/3.9.2.Final</bundle>
12 + <bundle>mvn:io.netty/netty-common/4.0.23.Final</bundle>
13 + <bundle>mvn:io.netty/netty-buffer/4.0.23.Final</bundle>
14 + <bundle>mvn:io.netty/netty-transport/4.0.23.Final</bundle>
15 + <bundle>mvn:io.netty/netty-handler/4.0.23.Final</bundle>
16 + <bundle>mvn:io.netty/netty-codec/4.0.23.Final</bundle>
17 + <bundle>mvn:commons-pool/commons-pool/1.6</bundle>
12 18
13 <bundle>mvn:com.hazelcast/hazelcast/3.3</bundle> 19 <bundle>mvn:com.hazelcast/hazelcast/3.3</bundle>
14 <bundle>mvn:io.dropwizard.metrics/metrics-core/3.1.0</bundle> 20 <bundle>mvn:io.dropwizard.metrics/metrics-core/3.1.0</bundle>
...@@ -53,6 +59,11 @@ ...@@ -53,6 +59,11 @@
53 <feature>onos-api</feature> 59 <feature>onos-api</feature>
54 <bundle>mvn:org.onlab.onos/onos-core-net/1.0.0-SNAPSHOT</bundle> 60 <bundle>mvn:org.onlab.onos/onos-core-net/1.0.0-SNAPSHOT</bundle>
55 <bundle>mvn:org.onlab.onos/onos-core-dist/1.0.0-SNAPSHOT</bundle> 61 <bundle>mvn:org.onlab.onos/onos-core-dist/1.0.0-SNAPSHOT</bundle>
62 + <bundle>mvn:org.onlab.onos/onos-core-serializers/1.0.0-SNAPSHOT</bundle>
63 + <bundle>mvn:org.onlab.onos/onlab-netty/1.0.0-SNAPSHOT</bundle>
64 +
65 + <bundle>mvn:org.onlab.onos/onos-core-hz-common/1.0.0-SNAPSHOT</bundle>
66 + <bundle>mvn:org.onlab.onos/onos-core-hz-cluster/1.0.0-SNAPSHOT</bundle>
56 </feature> 67 </feature>
57 68
58 <feature name="onos-core-hazelcast" version="1.0.0" 69 <feature name="onos-core-hazelcast" version="1.0.0"
...@@ -120,18 +131,29 @@ ...@@ -120,18 +131,29 @@
120 <bundle>mvn:org.onlab.onos/onos-app-fwd/1.0.0-SNAPSHOT</bundle> 131 <bundle>mvn:org.onlab.onos/onos-app-fwd/1.0.0-SNAPSHOT</bundle>
121 </feature> 132 </feature>
122 133
134 + <feature name="onos-app-ifwd" version="1.0.0"
135 + description="ONOS sample forwarding application using intents">
136 + <feature>onos-api</feature>
137 + <bundle>mvn:org.onlab.onos/onos-app-ifwd/1.0.0-SNAPSHOT</bundle>
138 + </feature>
139 +
123 <feature name="onos-app-mobility" version="1.0.0" 140 <feature name="onos-app-mobility" version="1.0.0"
124 - description="ONOS sample forwarding application"> 141 + description="ONOS sample mobility application">
125 <feature>onos-api</feature> 142 <feature>onos-api</feature>
126 <bundle>mvn:org.onlab.onos/onos-app-mobility/1.0.0-SNAPSHOT</bundle> 143 <bundle>mvn:org.onlab.onos/onos-app-mobility/1.0.0-SNAPSHOT</bundle>
127 </feature> 144 </feature>
128 145
129 - 146 + <feature name="onos-app-proxyarp" version="1.0.0"
147 + description="ONOS sample proxyarp application">
148 + <feature>onos-api</feature>
149 + <bundle>mvn:org.onlab.onos/onos-app-proxyarp/1.0.0-SNAPSHOT</bundle>
150 + </feature>
130 151
131 <feature name="onos-app-foo" version="1.0.0" 152 <feature name="onos-app-foo" version="1.0.0"
132 description="ONOS sample playground application"> 153 description="ONOS sample playground application">
133 <feature>onos-api</feature> 154 <feature>onos-api</feature>
134 <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>
135 </feature> 157 </feature>
136 158
137 <feature name="onos-app-config" version="1.0.0" 159 <feature name="onos-app-config" version="1.0.0"
......
1 -<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
2 -<!--
3 - ~ Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
4 - ~
5 - ~ This program and the accompanying materials are made available under the
6 - ~ terms of the Eclipse Public License v1.0 which accompanies this distribution,
7 - ~ and is available at http://www.eclipse.org/legal/epl-v10.html
8 - -->
9 -
10 -<features xmlns="http://karaf.apache.org/xmlns/features/v1.2.0"
11 - name="net.onrc.onos-1.0.0">
12 - <repository>mvn:net.onrc.onos/onos-features/1.0.0-SNAPSHOT/xml/features</repository>
13 -
14 - <feature name="thirdparty" version="1.0.0"
15 - description="ONOS 3rd party dependencies">
16 - <bundle>mvn:com.google.code.findbugs/annotations/2.0.2</bundle>
17 - <bundle>mvn:io.netty/netty/3.9.2.Final</bundle>
18 - <bundle>mvn:com.google.guava/guava/17.0</bundle>
19 - <bundle>mvn:com.google.guava/guava/15.0</bundle>
20 -
21 - </feature>
22 -
23 - <feature name="base" version="1.0.0"
24 - description="ONOS Base">
25 - <feature>scr</feature>
26 - <feature>thirdparty</feature>
27 - <bundle>mvn:net.onrc.onos.sb/onos-sb/0.0.1</bundle>
28 - <bundle>mvn:org.projectfloodlight/openflowj/0.3.6-SNAPSHOT</bundle>
29 - </feature>
30 -
31 -</features>
...@@ -17,14 +17,11 @@ ...@@ -17,14 +17,11 @@
17 <description>ONOS OpenFlow controller subsystem API</description> 17 <description>ONOS OpenFlow controller subsystem API</description>
18 18
19 <repositories> 19 <repositories>
20 - <!-- FIXME: for Loxigen. Decide how to use Loxigen before release. --> 20 + <!-- FIXME: for Loxigen + optical experimenter. Decide how to use Loxigen before release. -->
21 <repository> 21 <repository>
22 - <id>sonatype-oss-snapshot</id> 22 + <id>onlab-temp</id>
23 - <name>Sonatype OSS snapshot repository</name> 23 + <name>ON.lab temporary repository</name>
24 - <url>https://oss.sonatype.org/content/repositories/snapshots</url> 24 + <url>http://mavenrepo.onlab.us:8081/nexus/content/repositories/releases</url>
25 - <releases>
26 - <enabled>false</enabled>
27 - </releases>
28 </repository> 25 </repository>
29 </repositories> 26 </repositories>
30 27
...@@ -32,7 +29,8 @@ ...@@ -32,7 +29,8 @@
32 <dependency> 29 <dependency>
33 <groupId>org.projectfloodlight</groupId> 30 <groupId>org.projectfloodlight</groupId>
34 <artifactId>openflowj</artifactId> 31 <artifactId>openflowj</artifactId>
35 - <version>0.3.8-SNAPSHOT</version> 32 + <!-- FIXME once experimenter gets merged to upstream -->
33 + <version>0.3.8-optical_experimenter</version>
36 </dependency> 34 </dependency>
37 <dependency> 35 <dependency>
38 <groupId>io.netty</groupId> 36 <groupId>io.netty</groupId>
......
...@@ -167,11 +167,11 @@ class OFChannelHandler extends IdleStateAwareChannelHandler { ...@@ -167,11 +167,11 @@ class OFChannelHandler extends IdleStateAwareChannelHandler {
167 // TODO We could check for the optional bitmap, but for now 167 // TODO We could check for the optional bitmap, but for now
168 // we are just checking the version number. 168 // we are just checking the version number.
169 if (m.getVersion() == OFVersion.OF_13) { 169 if (m.getVersion() == OFVersion.OF_13) {
170 - log.info("Received {} Hello from {}", m.getVersion(), 170 + log.debug("Received {} Hello from {}", m.getVersion(),
171 h.channel.getRemoteAddress()); 171 h.channel.getRemoteAddress());
172 h.ofVersion = OFVersion.OF_13; 172 h.ofVersion = OFVersion.OF_13;
173 } else if (m.getVersion() == OFVersion.OF_10) { 173 } else if (m.getVersion() == OFVersion.OF_10) {
174 - log.info("Received {} Hello from {} - switching to OF " 174 + log.debug("Received {} Hello from {} - switching to OF "
175 + "version 1.0", m.getVersion(), 175 + "version 1.0", m.getVersion(),
176 h.channel.getRemoteAddress()); 176 h.channel.getRemoteAddress());
177 h.ofVersion = OFVersion.OF_10; 177 h.ofVersion = OFVersion.OF_10;
...@@ -222,7 +222,7 @@ class OFChannelHandler extends IdleStateAwareChannelHandler { ...@@ -222,7 +222,7 @@ class OFChannelHandler extends IdleStateAwareChannelHandler {
222 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m) 222 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
223 throws IOException { 223 throws IOException {
224 h.thisdpid = m.getDatapathId().getLong(); 224 h.thisdpid = m.getDatapathId().getLong();
225 - log.info("Received features reply for switch at {} with dpid {}", 225 + log.debug("Received features reply for switch at {} with dpid {}",
226 h.getSwitchInfoString(), h.thisdpid); 226 h.getSwitchInfoString(), h.thisdpid);
227 227
228 h.featuresReply = m; //temp store 228 h.featuresReply = m; //temp store
...@@ -409,7 +409,7 @@ class OFChannelHandler extends IdleStateAwareChannelHandler { ...@@ -409,7 +409,7 @@ class OFChannelHandler extends IdleStateAwareChannelHandler {
409 409
410 410
411 411
412 - log.info("Switch {} bound to class {}, description {}", 412 + log.debug("Switch {} bound to class {}, description {}",
413 new Object[] {h.sw, h.sw.getClass(), drep }); 413 new Object[] {h.sw, h.sw.getClass(), drep });
414 //Put switch in EQUAL mode until we hear back from the global registry 414 //Put switch in EQUAL mode until we hear back from the global registry
415 //log.debug("Setting new switch {} to EQUAL and sending Role request", 415 //log.debug("Setting new switch {} to EQUAL and sending Role request",
...@@ -651,7 +651,7 @@ class OFChannelHandler extends IdleStateAwareChannelHandler { ...@@ -651,7 +651,7 @@ class OFChannelHandler extends IdleStateAwareChannelHandler {
651 * @param error The error message 651 * @param error The error message
652 */ 652 */
653 protected void logError(OFChannelHandler h, OFErrorMsg error) { 653 protected void logError(OFChannelHandler h, OFErrorMsg error) {
654 - log.info("{} from switch {} in state {}", 654 + log.error("{} from switch {} in state {}",
655 new Object[] { 655 new Object[] {
656 error, 656 error,
657 h.getSwitchInfoString(), 657 h.getSwitchInfoString(),
...@@ -1052,7 +1052,7 @@ class OFChannelHandler extends IdleStateAwareChannelHandler { ...@@ -1052,7 +1052,7 @@ class OFChannelHandler extends IdleStateAwareChannelHandler {
1052 throws Exception { 1052 throws Exception {
1053 OFFactory factory = (ofVersion == OFVersion.OF_13) ? factory13 : factory10; 1053 OFFactory factory = (ofVersion == OFVersion.OF_13) ? factory13 : factory10;
1054 OFMessage m = factory.buildEchoRequest().build(); 1054 OFMessage m = factory.buildEchoRequest().build();
1055 - log.info("Sending Echo Request on idle channel: {}", 1055 + log.debug("Sending Echo Request on idle channel: {}",
1056 e.getChannel().getPipeline().getLast().toString()); 1056 e.getChannel().getPipeline().getLast().toString());
1057 e.getChannel().write(Collections.singletonList(m)); 1057 e.getChannel().write(Collections.singletonList(m));
1058 // XXX S some problems here -- echo request has no transaction id, and 1058 // XXX S some problems here -- echo request has no transaction id, and
......
...@@ -122,7 +122,7 @@ class RoleManager implements RoleHandler { ...@@ -122,7 +122,7 @@ class RoleManager implements RoleHandler {
122 //FIXME fix below when we actually use generation ids 122 //FIXME fix below when we actually use generation ids
123 .setGenerationId(U64.ZERO) 123 .setGenerationId(U64.ZERO)
124 .build(); 124 .build();
125 - sw.sendMsg(rrm); 125 + sw.write(rrm);
126 return xid; 126 return xid;
127 } 127 }
128 128
......
...@@ -138,7 +138,7 @@ public class OFSwitchImplOVS13 extends AbstractOpenFlowSwitch { ...@@ -138,7 +138,7 @@ public class OFSwitchImplOVS13 extends AbstractOpenFlowSwitch {
138 .buildBarrierRequest() 138 .buildBarrierRequest()
139 .setXid(xid) 139 .setXid(xid)
140 .build(); 140 .build();
141 - sendMsg(br); 141 + write(br);
142 } 142 }
143 143
144 @Override 144 @Override
...@@ -227,7 +227,7 @@ public class OFSwitchImplOVS13 extends AbstractOpenFlowSwitch { ...@@ -227,7 +227,7 @@ public class OFSwitchImplOVS13 extends AbstractOpenFlowSwitch {
227 .setHardTimeout(0) 227 .setHardTimeout(0)
228 .setXid(getNextTransactionId()) 228 .setXid(getNextTransactionId())
229 .build(); 229 .build();
230 - sendMsg(tableMissEntry); 230 + write(tableMissEntry);
231 } 231 }
232 232
233 } 233 }
......
...@@ -40,15 +40,15 @@ public enum OFType { ...@@ -40,15 +40,15 @@ public enum OFType {
40 PORT_STATUS, 40 PORT_STATUS,
41 PACKET_OUT, 41 PACKET_OUT,
42 FLOW_MOD, 42 FLOW_MOD,
43 + GROUP_MOD,
43 PORT_MOD, 44 PORT_MOD,
45 + TABLE_MOD,
44 STATS_REQUEST, 46 STATS_REQUEST,
45 STATS_REPLY, 47 STATS_REPLY,
46 BARRIER_REQUEST, 48 BARRIER_REQUEST,
47 BARRIER_REPLY, 49 BARRIER_REPLY,
48 QUEUE_GET_CONFIG_REQUEST, 50 QUEUE_GET_CONFIG_REQUEST,
49 QUEUE_GET_CONFIG_REPLY, 51 QUEUE_GET_CONFIG_REPLY,
50 - GROUP_MOD,
51 - TABLE_MOD,
52 ROLE_REQUEST, 52 ROLE_REQUEST,
53 ROLE_REPLY, 53 ROLE_REPLY,
54 GET_ASYNC_REQUEST, 54 GET_ASYNC_REQUEST,
......
...@@ -69,6 +69,13 @@ ...@@ -69,6 +69,13 @@
69 69
70 <dependency> 70 <dependency>
71 <groupId>org.slf4j</groupId> 71 <groupId>org.slf4j</groupId>
72 + <artifactId>slf4j-core</artifactId>
73 + <version>1.7.6</version>
74 + <scope>test</scope>
75 + </dependency>
76 +
77 + <dependency>
78 + <groupId>org.slf4j</groupId>
72 <artifactId>slf4j-jdk14</artifactId> 79 <artifactId>slf4j-jdk14</artifactId>
73 <version>1.7.6</version> 80 <version>1.7.6</version>
74 <scope>test</scope> 81 <scope>test</scope>
...@@ -122,6 +129,12 @@ ...@@ -122,6 +129,12 @@
122 <version>1.9.13</version> 129 <version>1.9.13</version>
123 </dependency> 130 </dependency>
124 131
132 + <dependency>
133 + <groupId>org.easymock</groupId>
134 + <artifactId>easymock</artifactId>
135 + <version>3.2</version>
136 + <scope>test</scope>
137 + </dependency>
125 138
126 <!-- Web related --> 139 <!-- Web related -->
127 <dependency> 140 <dependency>
...@@ -480,7 +493,7 @@ ...@@ -480,7 +493,7 @@
480 <group> 493 <group>
481 <title>Core Subsystems</title> 494 <title>Core Subsystems</title>
482 <packages> 495 <packages>
483 - org.onlab.onos.cluster.impl:org.onlab.onos.net.device.impl:org.onlab.onos.net.link.impl:org.onlab.onos.net.host.impl:org.onlab.onos.net.topology.impl:org.onlab.onos.net.packet.impl:org.onlab.onos.net.flow.impl:org.onlab.onos.store.trivial.*:org.onlab.onos.net.*.impl:org.onlab.onos.event.impl:org.onlab.onos.store.* 496 + org.onlab.onos.cluster.impl:org.onlab.onos.net.device.impl:org.onlab.onos.net.link.impl:org.onlab.onos.net.host.impl:org.onlab.onos.net.topology.impl:org.onlab.onos.net.packet.impl:org.onlab.onos.net.flow.impl:org.onlab.onos.store.trivial.*:org.onlab.onos.net.*.impl:org.onlab.onos.event.impl:org.onlab.onos.store.*:org.onlab.onos.net.intent.impl
484 </packages> 497 </packages>
485 </group> 498 </group>
486 <group> 499 <group>
......
...@@ -6,11 +6,13 @@ import java.util.List; ...@@ -6,11 +6,13 @@ import java.util.List;
6 6
7 import org.onlab.onos.net.DeviceId; 7 import org.onlab.onos.net.DeviceId;
8 import org.onlab.onos.net.PortNumber; 8 import org.onlab.onos.net.PortNumber;
9 +import org.onlab.onos.net.flow.DefaultFlowEntry;
9 import org.onlab.onos.net.flow.DefaultFlowRule; 10 import org.onlab.onos.net.flow.DefaultFlowRule;
10 import org.onlab.onos.net.flow.DefaultTrafficSelector; 11 import org.onlab.onos.net.flow.DefaultTrafficSelector;
11 import org.onlab.onos.net.flow.DefaultTrafficTreatment; 12 import org.onlab.onos.net.flow.DefaultTrafficTreatment;
13 +import org.onlab.onos.net.flow.FlowEntry;
14 +import org.onlab.onos.net.flow.FlowEntry.FlowEntryState;
12 import org.onlab.onos.net.flow.FlowRule; 15 import org.onlab.onos.net.flow.FlowRule;
13 -import org.onlab.onos.net.flow.FlowRule.FlowRuleState;
14 import org.onlab.onos.net.flow.TrafficSelector; 16 import org.onlab.onos.net.flow.TrafficSelector;
15 import org.onlab.onos.net.flow.TrafficTreatment; 17 import org.onlab.onos.net.flow.TrafficTreatment;
16 import org.onlab.onos.openflow.controller.Dpid; 18 import org.onlab.onos.openflow.controller.Dpid;
...@@ -18,8 +20,8 @@ import org.onlab.packet.IpPrefix; ...@@ -18,8 +20,8 @@ import org.onlab.packet.IpPrefix;
18 import org.onlab.packet.MacAddress; 20 import org.onlab.packet.MacAddress;
19 import org.onlab.packet.VlanId; 21 import org.onlab.packet.VlanId;
20 import org.projectfloodlight.openflow.protocol.OFFlowRemoved; 22 import org.projectfloodlight.openflow.protocol.OFFlowRemoved;
21 -import org.projectfloodlight.openflow.protocol.OFFlowRemovedReason;
22 import org.projectfloodlight.openflow.protocol.OFFlowStatsEntry; 23 import org.projectfloodlight.openflow.protocol.OFFlowStatsEntry;
24 +import org.projectfloodlight.openflow.protocol.OFInstructionType;
23 import org.projectfloodlight.openflow.protocol.action.OFAction; 25 import org.projectfloodlight.openflow.protocol.action.OFAction;
24 import org.projectfloodlight.openflow.protocol.action.OFActionOutput; 26 import org.projectfloodlight.openflow.protocol.action.OFActionOutput;
25 import org.projectfloodlight.openflow.protocol.action.OFActionSetDlDst; 27 import org.projectfloodlight.openflow.protocol.action.OFActionSetDlDst;
...@@ -28,12 +30,16 @@ import org.projectfloodlight.openflow.protocol.action.OFActionSetNwDst; ...@@ -28,12 +30,16 @@ import org.projectfloodlight.openflow.protocol.action.OFActionSetNwDst;
28 import org.projectfloodlight.openflow.protocol.action.OFActionSetNwSrc; 30 import org.projectfloodlight.openflow.protocol.action.OFActionSetNwSrc;
29 import org.projectfloodlight.openflow.protocol.action.OFActionSetVlanPcp; 31 import org.projectfloodlight.openflow.protocol.action.OFActionSetVlanPcp;
30 import org.projectfloodlight.openflow.protocol.action.OFActionSetVlanVid; 32 import org.projectfloodlight.openflow.protocol.action.OFActionSetVlanVid;
33 +import org.projectfloodlight.openflow.protocol.instruction.OFInstruction;
34 +import org.projectfloodlight.openflow.protocol.instruction.OFInstructionApplyActions;
31 import org.projectfloodlight.openflow.protocol.match.Match; 35 import org.projectfloodlight.openflow.protocol.match.Match;
32 import org.projectfloodlight.openflow.protocol.match.MatchField; 36 import org.projectfloodlight.openflow.protocol.match.MatchField;
33 import org.projectfloodlight.openflow.types.IPv4Address; 37 import org.projectfloodlight.openflow.types.IPv4Address;
34 import org.slf4j.Logger; 38 import org.slf4j.Logger;
35 39
36 -public class FlowRuleBuilder { 40 +import com.google.common.collect.Lists;
41 +
42 +public class FlowEntryBuilder {
37 private final Logger log = getLogger(getClass()); 43 private final Logger log = getLogger(getClass());
38 44
39 private final OFFlowStatsEntry stat; 45 private final OFFlowStatsEntry stat;
...@@ -44,49 +50,70 @@ public class FlowRuleBuilder { ...@@ -44,49 +50,70 @@ public class FlowRuleBuilder {
44 50
45 private final Dpid dpid; 51 private final Dpid dpid;
46 52
53 + private final boolean addedRule;
47 54
48 55
49 - 56 + public FlowEntryBuilder(Dpid dpid, OFFlowStatsEntry entry) {
50 - public FlowRuleBuilder(Dpid dpid, OFFlowStatsEntry entry) {
51 this.stat = entry; 57 this.stat = entry;
52 this.match = entry.getMatch(); 58 this.match = entry.getMatch();
53 - this.actions = entry.getActions(); 59 + this.actions = getActions(entry);
54 this.dpid = dpid; 60 this.dpid = dpid;
55 this.removed = null; 61 this.removed = null;
62 + this.addedRule = true;
56 } 63 }
57 64
58 - public FlowRuleBuilder(Dpid dpid, OFFlowRemoved removed) { 65 + public FlowEntryBuilder(Dpid dpid, OFFlowRemoved removed) {
59 this.match = removed.getMatch(); 66 this.match = removed.getMatch();
60 this.removed = removed; 67 this.removed = removed;
61 68
62 this.dpid = dpid; 69 this.dpid = dpid;
63 this.actions = null; 70 this.actions = null;
64 this.stat = null; 71 this.stat = null;
72 + this.addedRule = false;
65 73
66 } 74 }
67 75
68 - public FlowRule build() { 76 + public FlowEntry build() {
69 - if (stat != null) { 77 + if (addedRule) {
70 - return new DefaultFlowRule(DeviceId.deviceId(Dpid.uri(dpid)), 78 + FlowRule rule = new DefaultFlowRule(DeviceId.deviceId(Dpid.uri(dpid)),
71 buildSelector(), buildTreatment(), stat.getPriority(), 79 buildSelector(), buildTreatment(), stat.getPriority(),
72 - FlowRuleState.ADDED, stat.getDurationNsec() / 1000000, 80 + stat.getCookie().getValue(), stat.getIdleTimeout());
73 - stat.getPacketCount().getValue(), stat.getByteCount().getValue(), 81 + return new DefaultFlowEntry(rule, FlowEntryState.ADDED,
74 - stat.getCookie().getValue(), false, stat.getIdleTimeout()); 82 + stat.getDurationSec(), stat.getPacketCount().getValue(),
83 + stat.getByteCount().getValue());
84 +
75 } else { 85 } else {
76 - // TODO: revisit potentially. 86 + FlowRule rule = new DefaultFlowRule(DeviceId.deviceId(Dpid.uri(dpid)),
77 - return new DefaultFlowRule(DeviceId.deviceId(Dpid.uri(dpid)),
78 buildSelector(), null, removed.getPriority(), 87 buildSelector(), null, removed.getPriority(),
79 - FlowRuleState.REMOVED, removed.getDurationNsec() / 1000000, 88 + removed.getCookie().getValue(), removed.getIdleTimeout());
80 - removed.getPacketCount().getValue(), removed.getByteCount().getValue(), 89 + return new DefaultFlowEntry(rule, FlowEntryState.REMOVED, removed.getDurationSec(),
81 - removed.getCookie().getValue(), 90 + removed.getPacketCount().getValue(), removed.getByteCount().getValue());
82 - removed.getReason() == OFFlowRemovedReason.IDLE_TIMEOUT.ordinal(),
83 - stat.getIdleTimeout());
84 } 91 }
85 } 92 }
86 93
94 + private List<OFAction> getActions(OFFlowStatsEntry entry) {
95 + switch (entry.getVersion()) {
96 + case OF_10:
97 + return entry.getActions();
98 + case OF_11:
99 + case OF_12:
100 + case OF_13:
101 + List<OFInstruction> ins = entry.getInstructions();
102 + for (OFInstruction in : ins) {
103 + if (in.getType().equals(OFInstructionType.APPLY_ACTIONS)) {
104 + OFInstructionApplyActions apply = (OFInstructionApplyActions) in;
105 + return apply.getActions();
106 + }
107 + }
108 + return Lists.newLinkedList();
109 + default:
110 + log.warn("Unknown OF version {}", entry.getVersion());
111 + }
112 + return Lists.newLinkedList();
113 + }
87 114
88 private TrafficTreatment buildTreatment() { 115 private TrafficTreatment buildTreatment() {
89 - TrafficTreatment.Builder builder = new DefaultTrafficTreatment.Builder(); 116 + TrafficTreatment.Builder builder = DefaultTrafficTreatment.builder();
90 // If this is a drop rule 117 // If this is a drop rule
91 if (actions.size() == 0) { 118 if (actions.size() == 0) {
92 builder.drop(); 119 builder.drop();
...@@ -171,7 +198,7 @@ public class FlowRuleBuilder { ...@@ -171,7 +198,7 @@ public class FlowRuleBuilder {
171 } 198 }
172 199
173 private TrafficSelector buildSelector() { 200 private TrafficSelector buildSelector() {
174 - TrafficSelector.Builder builder = new DefaultTrafficSelector.Builder(); 201 + TrafficSelector.Builder builder = DefaultTrafficSelector.builder();
175 for (MatchField<?> field : match.getMatchFields()) { 202 for (MatchField<?> field : match.getMatchFields()) {
176 switch (field.id) { 203 switch (field.id) {
177 case IN_PORT: 204 case IN_PORT:
......
...@@ -68,7 +68,7 @@ public class FlowModBuilder { ...@@ -68,7 +68,7 @@ public class FlowModBuilder {
68 this.cookie = flowRule.id(); 68 this.cookie = flowRule.id();
69 } 69 }
70 70
71 - public OFFlowMod buildFlowMod() { 71 + public OFFlowMod buildFlowAdd() {
72 Match match = buildMatch(); 72 Match match = buildMatch();
73 List<OFAction> actions = buildActions(); 73 List<OFAction> actions = buildActions();
74 74
...@@ -86,6 +86,24 @@ public class FlowModBuilder { ...@@ -86,6 +86,24 @@ public class FlowModBuilder {
86 86
87 } 87 }
88 88
89 + public OFFlowMod buildFlowMod() {
90 + Match match = buildMatch();
91 + List<OFAction> actions = buildActions();
92 +
93 + //TODO: what to do without bufferid? do we assume that there will be a pktout as well?
94 + OFFlowMod fm = factory.buildFlowModify()
95 + .setCookie(U64.of(cookie.value()))
96 + .setBufferId(OFBufferId.NO_BUFFER)
97 + .setActions(actions)
98 + .setMatch(match)
99 + .setFlags(Collections.singleton(OFFlowModFlags.SEND_FLOW_REM))
100 + .setPriority(priority)
101 + .build();
102 +
103 + return fm;
104 +
105 + }
106 +
89 public OFFlowMod buildFlowDel() { 107 public OFFlowMod buildFlowDel() {
90 Match match = buildMatch(); 108 Match match = buildMatch();
91 List<OFAction> actions = buildActions(); 109 List<OFAction> actions = buildActions();
......
...@@ -2,7 +2,17 @@ package org.onlab.onos.provider.of.flow.impl; ...@@ -2,7 +2,17 @@ package org.onlab.onos.provider.of.flow.impl;
2 2
3 import static org.slf4j.LoggerFactory.getLogger; 3 import static org.slf4j.LoggerFactory.getLogger;
4 4
5 +import java.util.HashSet;
6 +import java.util.List;
5 import java.util.Map; 7 import java.util.Map;
8 +import java.util.Set;
9 +import java.util.concurrent.ConcurrentHashMap;
10 +import java.util.concurrent.CountDownLatch;
11 +import java.util.concurrent.ExecutionException;
12 +import java.util.concurrent.Future;
13 +import java.util.concurrent.TimeUnit;
14 +import java.util.concurrent.TimeoutException;
15 +import java.util.concurrent.atomic.AtomicBoolean;
6 16
7 import org.apache.felix.scr.annotations.Activate; 17 import org.apache.felix.scr.annotations.Activate;
8 import org.apache.felix.scr.annotations.Component; 18 import org.apache.felix.scr.annotations.Component;
...@@ -11,10 +21,13 @@ import org.apache.felix.scr.annotations.Reference; ...@@ -11,10 +21,13 @@ import org.apache.felix.scr.annotations.Reference;
11 import org.apache.felix.scr.annotations.ReferenceCardinality; 21 import org.apache.felix.scr.annotations.ReferenceCardinality;
12 import org.onlab.onos.ApplicationId; 22 import org.onlab.onos.ApplicationId;
13 import org.onlab.onos.net.DeviceId; 23 import org.onlab.onos.net.DeviceId;
24 +import org.onlab.onos.net.flow.FlowEntry;
14 import org.onlab.onos.net.flow.FlowRule; 25 import org.onlab.onos.net.flow.FlowRule;
26 +import org.onlab.onos.net.flow.FlowRuleBatchEntry;
15 import org.onlab.onos.net.flow.FlowRuleProvider; 27 import org.onlab.onos.net.flow.FlowRuleProvider;
16 import org.onlab.onos.net.flow.FlowRuleProviderRegistry; 28 import org.onlab.onos.net.flow.FlowRuleProviderRegistry;
17 import org.onlab.onos.net.flow.FlowRuleProviderService; 29 import org.onlab.onos.net.flow.FlowRuleProviderService;
30 +import org.onlab.onos.net.intent.BatchOperation;
18 import org.onlab.onos.net.provider.AbstractProvider; 31 import org.onlab.onos.net.provider.AbstractProvider;
19 import org.onlab.onos.net.provider.ProviderId; 32 import org.onlab.onos.net.provider.ProviderId;
20 import org.onlab.onos.net.topology.TopologyService; 33 import org.onlab.onos.net.topology.TopologyService;
...@@ -24,17 +37,29 @@ import org.onlab.onos.openflow.controller.OpenFlowEventListener; ...@@ -24,17 +37,29 @@ import org.onlab.onos.openflow.controller.OpenFlowEventListener;
24 import org.onlab.onos.openflow.controller.OpenFlowSwitch; 37 import org.onlab.onos.openflow.controller.OpenFlowSwitch;
25 import org.onlab.onos.openflow.controller.OpenFlowSwitchListener; 38 import org.onlab.onos.openflow.controller.OpenFlowSwitchListener;
26 import org.onlab.onos.openflow.controller.RoleState; 39 import org.onlab.onos.openflow.controller.RoleState;
40 +import org.projectfloodlight.openflow.protocol.OFActionType;
41 +import org.projectfloodlight.openflow.protocol.OFBarrierRequest;
42 +import org.projectfloodlight.openflow.protocol.OFErrorMsg;
27 import org.projectfloodlight.openflow.protocol.OFFlowRemoved; 43 import org.projectfloodlight.openflow.protocol.OFFlowRemoved;
28 import org.projectfloodlight.openflow.protocol.OFFlowStatsEntry; 44 import org.projectfloodlight.openflow.protocol.OFFlowStatsEntry;
29 import org.projectfloodlight.openflow.protocol.OFFlowStatsReply; 45 import org.projectfloodlight.openflow.protocol.OFFlowStatsReply;
46 +import org.projectfloodlight.openflow.protocol.OFInstructionType;
30 import org.projectfloodlight.openflow.protocol.OFMessage; 47 import org.projectfloodlight.openflow.protocol.OFMessage;
31 import org.projectfloodlight.openflow.protocol.OFPortStatus; 48 import org.projectfloodlight.openflow.protocol.OFPortStatus;
32 import org.projectfloodlight.openflow.protocol.OFStatsReply; 49 import org.projectfloodlight.openflow.protocol.OFStatsReply;
33 import org.projectfloodlight.openflow.protocol.OFStatsReplyFlags; 50 import org.projectfloodlight.openflow.protocol.OFStatsReplyFlags;
34 import org.projectfloodlight.openflow.protocol.OFStatsType; 51 import org.projectfloodlight.openflow.protocol.OFStatsType;
52 +import org.projectfloodlight.openflow.protocol.OFVersion;
53 +import org.projectfloodlight.openflow.protocol.action.OFAction;
54 +import org.projectfloodlight.openflow.protocol.action.OFActionOutput;
55 +import org.projectfloodlight.openflow.protocol.instruction.OFInstruction;
56 +import org.projectfloodlight.openflow.protocol.instruction.OFInstructionApplyActions;
57 +import org.projectfloodlight.openflow.types.OFPort;
58 +import org.projectfloodlight.openflow.types.U32;
35 import org.slf4j.Logger; 59 import org.slf4j.Logger;
36 60
37 import com.google.common.collect.ArrayListMultimap; 61 import com.google.common.collect.ArrayListMultimap;
62 +import com.google.common.collect.Lists;
38 import com.google.common.collect.Maps; 63 import com.google.common.collect.Maps;
39 import com.google.common.collect.Multimap; 64 import com.google.common.collect.Multimap;
40 65
...@@ -60,6 +85,9 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr ...@@ -60,6 +85,9 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr
60 85
61 private final InternalFlowProvider listener = new InternalFlowProvider(); 86 private final InternalFlowProvider listener = new InternalFlowProvider();
62 87
88 + private final Map<Long, InstallationFuture> pendingFutures =
89 + new ConcurrentHashMap<Long, InstallationFuture>();
90 +
63 /** 91 /**
64 * Creates an OpenFlow host provider. 92 * Creates an OpenFlow host provider.
65 */ 93 */
...@@ -91,7 +119,7 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr ...@@ -91,7 +119,7 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr
91 119
92 private void applyRule(FlowRule flowRule) { 120 private void applyRule(FlowRule flowRule) {
93 OpenFlowSwitch sw = controller.getSwitch(Dpid.dpid(flowRule.deviceId().uri())); 121 OpenFlowSwitch sw = controller.getSwitch(Dpid.dpid(flowRule.deviceId().uri()));
94 - sw.sendMsg(new FlowModBuilder(flowRule, sw.factory()).buildFlowMod()); 122 + sw.sendMsg(new FlowModBuilder(flowRule, sw.factory()).buildFlowAdd());
95 } 123 }
96 124
97 125
...@@ -122,7 +150,7 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr ...@@ -122,7 +150,7 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr
122 implements OpenFlowSwitchListener, OpenFlowEventListener { 150 implements OpenFlowSwitchListener, OpenFlowEventListener {
123 151
124 private final Map<Dpid, FlowStatsCollector> collectors = Maps.newHashMap(); 152 private final Map<Dpid, FlowStatsCollector> collectors = Maps.newHashMap();
125 - private final Multimap<DeviceId, FlowRule> completeEntries = 153 + private final Multimap<DeviceId, FlowEntry> completeEntries =
126 ArrayListMultimap.create(); 154 ArrayListMultimap.create();
127 155
128 @Override 156 @Override
...@@ -144,19 +172,30 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr ...@@ -144,19 +172,30 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr
144 172
145 @Override 173 @Override
146 public void handleMessage(Dpid dpid, OFMessage msg) { 174 public void handleMessage(Dpid dpid, OFMessage msg) {
175 + InstallationFuture future = null;
147 switch (msg.getType()) { 176 switch (msg.getType()) {
148 case FLOW_REMOVED: 177 case FLOW_REMOVED:
149 //TODO: make this better 178 //TODO: make this better
150 OFFlowRemoved removed = (OFFlowRemoved) msg; 179 OFFlowRemoved removed = (OFFlowRemoved) msg;
151 180
152 - FlowRule fr = new FlowRuleBuilder(dpid, removed).build(); 181 + FlowEntry fr = new FlowEntryBuilder(dpid, removed).build();
153 providerService.flowRemoved(fr); 182 providerService.flowRemoved(fr);
154 break; 183 break;
155 case STATS_REPLY: 184 case STATS_REPLY:
156 pushFlowMetrics(dpid, (OFStatsReply) msg); 185 pushFlowMetrics(dpid, (OFStatsReply) msg);
157 break; 186 break;
158 case BARRIER_REPLY: 187 case BARRIER_REPLY:
188 + future = pendingFutures.get(msg.getXid());
189 + if (future != null) {
190 + future.satisfyRequirement(dpid);
191 + }
192 + break;
159 case ERROR: 193 case ERROR:
194 + future = pendingFutures.get(msg.getXid());
195 + if (future != null) {
196 + future.fail((OFErrorMsg) msg, dpid);
197 + }
198 + break;
160 default: 199 default:
161 log.debug("Unhandled message type: {}", msg.getType()); 200 log.debug("Unhandled message type: {}", msg.getType());
162 } 201 }
...@@ -178,8 +217,9 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr ...@@ -178,8 +217,9 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr
178 //final List<FlowRule> entries = Lists.newLinkedList(); 217 //final List<FlowRule> entries = Lists.newLinkedList();
179 218
180 for (OFFlowStatsEntry reply : replies.getEntries()) { 219 for (OFFlowStatsEntry reply : replies.getEntries()) {
181 - completeEntries.put(did, new FlowRuleBuilder(dpid, reply).build()); 220 + if (!tableMissRule(dpid, reply)) {
182 - //entries.add(new FlowRuleBuilder(dpid, reply).build()); 221 + completeEntries.put(did, new FlowEntryBuilder(dpid, reply).build());
222 + }
183 } 223 }
184 224
185 if (!stats.getFlags().contains(OFStatsReplyFlags.REPLY_MORE)) { 225 if (!stats.getFlags().contains(OFStatsReplyFlags.REPLY_MORE)) {
...@@ -189,9 +229,170 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr ...@@ -189,9 +229,170 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr
189 } 229 }
190 } 230 }
191 231
232 + private boolean tableMissRule(Dpid dpid, OFFlowStatsEntry reply) {
233 + // TODO NEED TO FIND A BETTER WAY TO AVOID DOING THIS
234 + if (reply.getVersion().equals(OFVersion.OF_10) ||
235 + reply.getMatch().getMatchFields().iterator().hasNext()) {
236 + return false;
237 + }
238 + for (OFInstruction ins : reply.getInstructions()) {
239 + if (ins.getType() == OFInstructionType.APPLY_ACTIONS) {
240 + OFInstructionApplyActions apply = (OFInstructionApplyActions) ins;
241 + List<OFAction> acts = apply.getActions();
242 + for (OFAction act : acts) {
243 + if (act.getType() == OFActionType.OUTPUT) {
244 + OFActionOutput out = (OFActionOutput) act;
245 + if (out.getPort() == OFPort.CONTROLLER) {
246 + return true;
247 + }
248 + }
249 + }
250 + }
251 + }
252 + return false;
253 + }
254 +
255 + }
256 +
257 +
258 + @Override
259 + public Future<Void> executeBatch(BatchOperation<FlowRuleBatchEntry> batch) {
260 + final Set<Dpid> sws = new HashSet<Dpid>();
261 +
262 + for (FlowRuleBatchEntry fbe : batch.getOperations()) {
263 + FlowRule flowRule = fbe.getTarget();
264 + OpenFlowSwitch sw = controller.getSwitch(Dpid.dpid(flowRule.deviceId().uri()));
265 + sws.add(new Dpid(sw.getId()));
266 + switch (fbe.getOperator()) {
267 + case ADD:
268 + //TODO: Track XID for each flowmod
269 + sw.sendMsg(new FlowModBuilder(flowRule, sw.factory()).buildFlowAdd());
270 + break;
271 + case REMOVE:
272 + //TODO: Track XID for each flowmod
273 + sw.sendMsg(new FlowModBuilder(flowRule, sw.factory()).buildFlowDel());
274 + break;
275 + case MODIFY:
276 + //TODO: Track XID for each flowmod
277 + sw.sendMsg(new FlowModBuilder(flowRule, sw.factory()).buildFlowMod());
278 + break;
279 + default:
280 + log.error("Unsupported batch operation {}", fbe.getOperator());
281 + }
192 } 282 }
283 + InstallationFuture installation = new InstallationFuture(sws);
284 + pendingFutures.put(U32.f(batch.hashCode()), installation);
285 + installation.verify(batch.hashCode());
286 + return installation;
287 + }
288 +
289 + private class InstallationFuture implements Future<Void> {
193 290
291 + private final Set<Dpid> sws;
292 + private final AtomicBoolean ok = new AtomicBoolean(true);
293 + private final List<FlowEntry> offendingFlowMods = Lists.newLinkedList();
194 294
295 + private final CountDownLatch countDownLatch;
195 296
297 + public InstallationFuture(Set<Dpid> sws) {
298 + this.sws = sws;
299 + countDownLatch = new CountDownLatch(sws.size());
300 + }
301 +
302 + public void fail(OFErrorMsg msg, Dpid dpid) {
303 + ok.set(false);
304 + //TODO add reason to flowentry
305 + //TODO handle specific error msgs
306 + //offendingFlowMods.add(new FlowEntryBuilder(dpid, msg.));
307 + switch (msg.getErrType()) {
308 + case BAD_ACTION:
309 + break;
310 + case BAD_INSTRUCTION:
311 + break;
312 + case BAD_MATCH:
313 + break;
314 + case BAD_REQUEST:
315 + break;
316 + case EXPERIMENTER:
317 + break;
318 + case FLOW_MOD_FAILED:
319 + break;
320 + case GROUP_MOD_FAILED:
321 + break;
322 + case HELLO_FAILED:
323 + break;
324 + case METER_MOD_FAILED:
325 + break;
326 + case PORT_MOD_FAILED:
327 + break;
328 + case QUEUE_OP_FAILED:
329 + break;
330 + case ROLE_REQUEST_FAILED:
331 + break;
332 + case SWITCH_CONFIG_FAILED:
333 + break;
334 + case TABLE_FEATURES_FAILED:
335 + break;
336 + case TABLE_MOD_FAILED:
337 + break;
338 + default:
339 + break;
340 +
341 + }
342 +
343 + }
344 +
345 + public void satisfyRequirement(Dpid dpid) {
346 + log.warn("Satisfaction from switch {}", dpid);
347 + sws.remove(dpid);
348 + countDownLatch.countDown();
349 + }
350 +
351 + public void verify(Integer id) {
352 + for (Dpid dpid : sws) {
353 + OpenFlowSwitch sw = controller.getSwitch(dpid);
354 + OFBarrierRequest.Builder builder = sw.factory()
355 + .buildBarrierRequest()
356 + .setXid(id);
357 + sw.sendMsg(builder.build());
358 + }
359 +
360 +
361 + }
362 +
363 + @Override
364 + public boolean cancel(boolean mayInterruptIfRunning) {
365 + // TODO Auto-generated method stub
366 + return false;
367 + }
368 +
369 + @Override
370 + public boolean isCancelled() {
371 + // TODO Auto-generated method stub
372 + return false;
373 + }
374 +
375 + @Override
376 + public boolean isDone() {
377 + return sws.isEmpty();
378 + }
379 +
380 + @Override
381 + public Void get() throws InterruptedException, ExecutionException {
382 + countDownLatch.await();
383 + //return offendingFlowMods;
384 + return null;
385 + }
386 +
387 + @Override
388 + public Void get(long timeout, TimeUnit unit)
389 + throws InterruptedException, ExecutionException,
390 + TimeoutException {
391 + countDownLatch.await(timeout, unit);
392 + //return offendingFlowMods;
393 + return null;
394 + }
395 +
396 + }
196 397
197 } 398 }
......
...@@ -181,7 +181,7 @@ public class OpenFlowPacketProviderTest { ...@@ -181,7 +181,7 @@ public class OpenFlowPacketProviderTest {
181 } 181 }
182 182
183 private static TrafficTreatment treatment(Instruction ... insts) { 183 private static TrafficTreatment treatment(Instruction ... insts) {
184 - TrafficTreatment.Builder builder = new DefaultTrafficTreatment.Builder(); 184 + TrafficTreatment.Builder builder = DefaultTrafficTreatment.builder();
185 for (Instruction i : insts) { 185 for (Instruction i : insts) {
186 builder.add(i); 186 builder.add(i);
187 } 187 }
......
...@@ -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
......
...@@ -21,7 +21,7 @@ export PATH="$PATH:." ...@@ -21,7 +21,7 @@ export PATH="$PATH:."
21 # e.g. 'o api', 'o dev', 'o' 21 # e.g. 'o api', 'o dev', 'o'
22 function o { 22 function o {
23 cd $(find $ONOS_ROOT/ -type d | egrep -v '\.git|target' | \ 23 cd $(find $ONOS_ROOT/ -type d | egrep -v '\.git|target' | \
24 - egrep "${1:-$ONOS_ROOT}" | head -n 1) 24 + egrep "${1:-$ONOS_ROOT}" | egrep -v "$ONOS_ROOT/.+/src/" | head -n 1)
25 } 25 }
26 26
27 # Short-hand for 'mvn clean install' for us lazy folk 27 # Short-hand for 'mvn clean install' for us lazy folk
...@@ -32,6 +32,9 @@ alias ob='onos-build' ...@@ -32,6 +32,9 @@ alias ob='onos-build'
32 alias obs='onos-build-selective' 32 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'
36 +alias go='ob && ot && onos -w'
37 +alias pub='onos-push-update-bundle'
35 38
36 # Short-hand for tailing the ONOS (karaf) log 39 # Short-hand for tailing the ONOS (karaf) log
37 alias tl='$ONOS_ROOT/tools/dev/bin/onos-local-log' 40 alias tl='$ONOS_ROOT/tools/dev/bin/onos-local-log'
...@@ -88,5 +91,5 @@ function spy { ...@@ -88,5 +91,5 @@ function spy {
88 } 91 }
89 92
90 function nuke { 93 function nuke {
91 - spy | cut -c7-11 | xargs kill 94 + spy "$@" | cut -c7-11 | xargs kill
92 } 95 }
......
...@@ -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 "$@"
......
...@@ -32,6 +32,10 @@ ssh $remote " ...@@ -32,6 +32,10 @@ ssh $remote "
32 32
33 # Remove any previous ON.Lab bits from ~/.m2 repo 33 # Remove any previous ON.Lab bits from ~/.m2 repo
34 rm -fr ~/.m2/repository/org/onlab 34 rm -fr ~/.m2/repository/org/onlab
35 +
36 + # Drop log level for the console
37 + echo "log4j.logger.org.apache.sshd = WARN" >> $ONOS_INSTALL_DIR/$KARAF_DIST/etc/org.ops4j.pax.logging.cfg
38 +
35 " 39 "
36 40
37 # Configure the ONOS installation 41 # Configure the ONOS installation
......
...@@ -15,7 +15,7 @@ name=${2:-onos-1} ...@@ -15,7 +15,7 @@ name=${2:-onos-1}
15 15
16 ssh $remote " 16 ssh $remote "
17 sudo perl -pi.bak -e \"s/127.0.1.1.*/127.0.1.1 $name/g\" /etc/hosts 17 sudo perl -pi.bak -e \"s/127.0.1.1.*/127.0.1.1 $name/g\" /etc/hosts
18 - sudo perl -pi.bak -e \"s/.*/$name/g\" /etc/hostname 18 + sudo bash -c \"echo $name >/etc/hostname\"
19 sudo hostname $name 19 sudo hostname $name
20 " 2>/dev/null 20 " 2>/dev/null
21 21
......
...@@ -9,5 +9,9 @@ ...@@ -9,5 +9,9 @@
9 remote=$ONOS_USER@${1:-$OCI} 9 remote=$ONOS_USER@${1:-$OCI}
10 10
11 scp -q ~/.ssh/id_rsa.pub $remote:/tmp 11 scp -q ~/.ssh/id_rsa.pub $remote:/tmp
12 -ssh $remote "cat /tmp/id_rsa.pub >> ~/.ssh/authorized_keys" 12 +ssh $remote "
13 + cat /tmp/id_rsa.pub >> ~/.ssh/authorized_keys
14 + sort -u ~/.ssh/authorized_keys > ~/.ssh/authorized_keys.bak
15 + mv ~/.ssh/authorized_keys.bak ~/.ssh/authorized_keys
16 +"
13 ssh -n -o PasswordAuthentication=no $remote true 17 ssh -n -o PasswordAuthentication=no $remote true
......
1 +#!/bin/bash
2 +#-------------------------------------------------------------------------------
3 +# Pushes the specified bundle to the remote ONOS cell machines and updates it.
4 +#-------------------------------------------------------------------------------
5 +
6 +[ ! -d "$ONOS_ROOT" ] && echo "ONOS_ROOT is not defined" >&2 && exit 1
7 +. $ONOS_ROOT/tools/build/envDefaults
8 +
9 +cd ~/.m2/repository
10 +jar=$(find org/onlab -type f -name '*.jar' | grep $1 | grep -v -e -tests | head -n 1)
11 +
12 +[ -z "$jar" ] && echo "No bundle $1 found for" && exit 1
13 +
14 +bundle=$(echo $(basename $jar .jar) | sed 's/-[0-9].*//g')
15 +
16 +nodes=$(env | sort | egrep "OC[0-9]+" | cut -d= -f2)
17 +for node in $nodes; do
18 + scp -q $jar $ONOS_USER@$node:.m2/repository/$jar
19 + scp -q $jar $ONOS_USER@$node:$ONOS_INSTALL_DIR/$KARAF_DIST/system/$jar
20 + ssh $ONOS_USER@$node "$ONOS_INSTALL_DIR/bin/onos \"bundle:update -f $bundle\"" 2>/dev/null
21 +done
1 +#!/usr/bin/env python
2 +from mininet.cli import CLI
3 +from mininet.net import Mininet
4 +from mininet.node import RemoteController, OVSKernelSwitch
5 +
6 +MAC = 12
7 +DPID = 16
8 +
9 +def string_to_hex(s, length):
10 + """ Convert a string like 00:00 in to hex 0x0000 format"""
11 + tmp = '{0:#x}'.format(int(s.replace(':', '').lstrip('0'),length))
12 + return tmp
13 +
14 +def hex_to_string(h, length):
15 + """Convert a hex number from 0x0000 to 00:00 format"""
16 + tmp = h.lstrip('0x').zfill(length)
17 + tmp = ':'.join(a+b for a,b in zip(tmp[::2], tmp[1::2]))
18 + return tmp
19 +
20 +class Tower(object):
21 + """ Create a tower topology from semi-scratch in Mininet """
22 +
23 + def __init__(self, cname='flare', cip='15.255.126.183', k=4, h=6,
24 + proto=None):
25 + """Create tower topology for mininet
26 + cname: controller name
27 + cip: controller ip
28 + k: number of leaf switches
29 + h: number of hosts perl leaf switch
30 + """
31 +
32 + # We are creating the controller with local-loopback on purpose to avoid
33 + # having the switches connect immediately. Instead, we'll set controller
34 + # explicitly for each switch after configuring it as we want.
35 + self.flare = RemoteController(cname, '127.0.0.1', 6633)
36 + self.net = Mininet(controller=self.flare, switch = OVSKernelSwitch,
37 + build=False)
38 +
39 + self.cip = cip
40 + self.spines = []
41 + self.leaves = []
42 + self.hosts = []
43 + self.proto = proto
44 +
45 + # Create the two spine switches
46 + self.spines.append(self.net.addSwitch('s1'))
47 + self.spines.append(self.net.addSwitch('s2'))
48 +
49 + # Create two links between the spine switches
50 + self.net.addLink(self.spines[0], self.spines[1])
51 + self.net.addLink(self.spines[1], self.spines[0])
52 +
53 + # Now create the leaf switches, their hosts and connect them together
54 + i = 1
55 + c = 0
56 + while i <= k:
57 + self.leaves.append(self.net.addSwitch('s1%d' % i))
58 + for spine in self.spines:
59 + self.net.addLink(self.leaves[i-1], spine)
60 +
61 + j = 1
62 + while j <= h:
63 + self.hosts.append(self.net.addHost('h%d%d' % (i, j)))
64 + self.net.addLink(self.hosts[c], self.leaves[i-1])
65 + j+=1
66 + c+=1
67 +
68 + i+=1
69 +
70 + def run(self):
71 + """ Runs the created network topology and launches mininet cli"""
72 + self.run_silent()
73 + CLI(self.net)
74 + self.net.stop()
75 +
76 + def run_silent(self):
77 + """ Runs silently - for unit testing """
78 + self.net.build()
79 +
80 + # Start the switches, configure them with desired protocols and only
81 + # then set the controller
82 + for sw in self.spines:
83 + sw.start([self.flare])
84 + if self.proto:
85 + sw.cmd('ovs-vsctl set bridge %(sw)s protocols=%(proto)s' % \
86 + { 'sw': sw.name, 'proto': self.proto})
87 + sw.cmdPrint('ovs-vsctl set-controller %(sw)s tcp:%(ctl)s:6633' % \
88 + {'sw': sw.name, 'ctl': self.cip})
89 +
90 + for sw in self.leaves:
91 + sw.start([self.flare])
92 + sw.cmdPrint('ovs-vsctl set-controller %(sw)s tcp:%(ctl)s:6633' % \
93 + {'sw': sw.name, 'ctl': self.cip})
94 +
95 + def pingAll(self):
96 + """ PingAll to create flows - for unit testing """
97 + self.net.pingAll()
98 +
99 + def stop(self):
100 + "Stops the topology. You should call this after run_silent"
101 + self.net.stop()
1 +#!/usr/bin/python
2 +# Launches mininet with Tower topology configuration.
3 +import sys, tower
4 +net = tower.Tower(cip=sys.argv[1])
5 +net.run()
...@@ -11,7 +11,7 @@ public class MetricsFeature { ...@@ -11,7 +11,7 @@ public class MetricsFeature {
11 * 11 *
12 * @param newName name of the Feature 12 * @param newName name of the Feature
13 */ 13 */
14 - MetricsFeature(final String newName) { 14 + public MetricsFeature(final String newName) {
15 name = newName; 15 name = newName;
16 } 16 }
17 17
......
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,27 +70,31 @@ public final class MetricsManager implements MetricsService { ...@@ -69,27 +70,31 @@ 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.componentsRegistry = new ConcurrentHashMap<>();
76 this.metricsRegistry = new MetricRegistry(); 77 this.metricsRegistry = new MetricRegistry();
77 - 78 +// this.reporter = Slf4jReporter.forRegistry(this.metricsRegistry)
78 - this.reporter = CsvReporter.forRegistry(metricsRegistry) 79 +// .outputTo(log)
79 - .formatFor(Locale.US) 80 +// .convertRatesTo(TimeUnit.SECONDS)
81 +// .convertDurationsTo(TimeUnit.NANOSECONDS)
82 +// .build();
83 + this.reporter = ConsoleReporter.forRegistry(this.metricsRegistry)
80 .convertRatesTo(TimeUnit.SECONDS) 84 .convertRatesTo(TimeUnit.SECONDS)
81 - .convertDurationsTo(TimeUnit.MICROSECONDS) 85 + .convertDurationsTo(TimeUnit.NANOSECONDS)
82 - .build(new File("/tmp/")); 86 + .build();
83 -
84 - reporter.start(10, TimeUnit.SECONDS);
85 } 87 }
86 88
87 @Activate 89 @Activate
88 public void activate() { 90 public void activate() {
91 + this.componentsRegistry = new ConcurrentHashMap<>();
92 + reporter.start(10, TimeUnit.SECONDS);
89 } 93 }
90 94
91 @Deactivate 95 @Deactivate
92 public void deactivate() { 96 public void deactivate() {
97 + reporter.stop();
93 } 98 }
94 99
95 /** 100 /**
......
...@@ -22,6 +22,12 @@ import java.util.Arrays; ...@@ -22,6 +22,12 @@ import java.util.Arrays;
22 * 22 *
23 */ 23 */
24 public class MacAddress { 24 public class MacAddress {
25 + public static final byte[] ZERO_MAC_ADDRESS =
26 + MacAddress.valueOf("00:00:00:00:00:00").getAddress();
27 +
28 + public static final byte[] BROADCAST_MAC =
29 + MacAddress.valueOf("ff:ff:ff:ff:ff:ff").getAddress();
30 +
25 public static final int MAC_ADDRESS_LENGTH = 6; 31 public static final int MAC_ADDRESS_LENGTH = 6;
26 private byte[] address = new byte[MacAddress.MAC_ADDRESS_LENGTH]; 32 private byte[] address = new byte[MacAddress.MAC_ADDRESS_LENGTH];
27 33
......
1 +package org.onlab.util;
2 +
3 +import java.util.concurrent.ConcurrentHashMap;
4 +import java.util.concurrent.ConcurrentMap;
5 +
6 +import org.apache.commons.lang3.concurrent.ConcurrentException;
7 +import org.apache.commons.lang3.concurrent.ConcurrentInitializer;
8 +
9 +/**
10 + * Creates an instance of new ConcurrentHashMap on each {@link #get()} call.
11 + * <p>
12 + * To be used with
13 + * {@link org.apache.commons.lang3.concurrent.ConcurrentUtils#createIfAbsent()
14 + * ConcurrentUtils#createIfAbsent}
15 + *
16 + * @param <K> ConcurrentHashMap key type
17 + * @param <V> ConcurrentHashMap value type
18 + */
19 +public final class NewConcurrentHashMap<K, V>
20 + implements ConcurrentInitializer<ConcurrentMap<K, V>> {
21 +
22 + public static final NewConcurrentHashMap<?, ?> INSTANCE = new NewConcurrentHashMap<>();
23 +
24 + @SuppressWarnings("unchecked")
25 + public static <K, V> NewConcurrentHashMap<K, V> ifNeeded() {
26 + return (NewConcurrentHashMap<K, V>) INSTANCE;
27 + }
28 +
29 + @Override
30 + public ConcurrentMap<K, V> get() throws ConcurrentException {
31 + return new ConcurrentHashMap<>();
32 + }
33 +}
...@@ -8,7 +8,7 @@ import org.jboss.netty.util.HashedWheelTimer; ...@@ -8,7 +8,7 @@ import org.jboss.netty.util.HashedWheelTimer;
8 */ 8 */
9 public final class Timer { 9 public final class Timer {
10 10
11 - private static HashedWheelTimer timer; 11 + private static volatile HashedWheelTimer timer;
12 12
13 // Ban public construction 13 // Ban public construction
14 private Timer() { 14 private Timer() {
...@@ -21,10 +21,17 @@ public final class Timer { ...@@ -21,10 +21,17 @@ public final class Timer {
21 */ 21 */
22 public static HashedWheelTimer getTimer() { 22 public static HashedWheelTimer getTimer() {
23 if (Timer.timer == null) { 23 if (Timer.timer == null) {
24 - Timer.timer = new HashedWheelTimer(); 24 + initTimer();
25 - Timer.timer.start();
26 } 25 }
27 return Timer.timer; 26 return Timer.timer;
28 } 27 }
29 28
29 + private static synchronized void initTimer() {
30 + if (Timer.timer == null) {
31 + HashedWheelTimer hwTimer = new HashedWheelTimer();
32 + hwTimer.start();
33 + Timer.timer = hwTimer;
34 + }
35 + }
36 +
30 } 37 }
......
...@@ -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,17 +8,16 @@ import java.util.concurrent.TimeoutException; ...@@ -8,17 +8,16 @@ import java.util.concurrent.TimeoutException;
8 * This class provides a base implementation of Response, with methods to retrieve the 8 * This class provides a base implementation of Response, with methods to retrieve the
9 * result and query to see if the result is ready. The result can only be retrieved when 9 * result and query to see if the result is ready. The result can only be retrieved when
10 * it is ready and the get methods will block if the result is not ready yet. 10 * it is ready and the get methods will block if the result is not ready yet.
11 - * @param <T> type of response.
12 */ 11 */
13 -public class AsyncResponse<T> implements Response<T> { 12 +public class AsyncResponse implements Response {
14 13
15 - private T value; 14 + private byte[] value;
16 private boolean done = false; 15 private boolean done = false;
17 private final long start = System.nanoTime(); 16 private final long start = System.nanoTime();
18 17
19 @Override 18 @Override
20 - public T get(long timeout, TimeUnit tu) throws TimeoutException { 19 + public byte[] get(long timeout, TimeUnit timeUnit) throws TimeoutException {
21 - timeout = tu.toNanos(timeout); 20 + timeout = timeUnit.toNanos(timeout);
22 boolean interrupted = false; 21 boolean interrupted = false;
23 try { 22 try {
24 synchronized (this) { 23 synchronized (this) {
...@@ -43,7 +42,7 @@ public class AsyncResponse<T> implements Response<T> { ...@@ -43,7 +42,7 @@ public class AsyncResponse<T> implements Response<T> {
43 } 42 }
44 43
45 @Override 44 @Override
46 - public T get() throws InterruptedException { 45 + public byte[] get() throws InterruptedException {
47 throw new UnsupportedOperationException(); 46 throw new UnsupportedOperationException();
48 } 47 }
49 48
...@@ -57,11 +56,10 @@ public class AsyncResponse<T> implements Response<T> { ...@@ -57,11 +56,10 @@ public class AsyncResponse<T> implements Response<T> {
57 * available. 56 * available.
58 * @param data response data. 57 * @param data response data.
59 */ 58 */
60 - @SuppressWarnings("unchecked") 59 + public synchronized void setResponse(byte[] data) {
61 - public synchronized void setResponse(Object data) {
62 if (!done) { 60 if (!done) {
63 done = true; 61 done = true;
64 - value = (T) data; 62 + value = data;
65 this.notifyAll(); 63 this.notifyAll();
66 } 64 }
67 } 65 }
......
1 +package org.onlab.netty;
2 +
3 +/**
4 + * State transitions a decoder goes through as it is decoding an incoming message.
5 + */
6 +public enum DecoderState {
7 + READ_HEADER_VERSION,
8 + READ_PREAMBLE,
9 + READ_CONTENT_LENGTH,
10 + READ_SERIALIZER_VERSION,
11 + READ_CONTENT
12 +}
...@@ -2,14 +2,20 @@ package org.onlab.netty; ...@@ -2,14 +2,20 @@ package org.onlab.netty;
2 2
3 import java.io.IOException; 3 import java.io.IOException;
4 4
5 +import org.slf4j.Logger;
6 +import org.slf4j.LoggerFactory;
7 +
8 +//FIXME: Should be move out to test or app
5 /** 9 /**
6 * Message handler that echos the message back to the sender. 10 * Message handler that echos the message back to the sender.
7 */ 11 */
8 public class EchoHandler implements MessageHandler { 12 public class EchoHandler implements MessageHandler {
9 13
14 + private final Logger log = LoggerFactory.getLogger(getClass());
15 +
10 @Override 16 @Override
11 public void handle(Message message) throws IOException { 17 public void handle(Message message) throws IOException {
12 - System.out.println("Received: " + message.payload() + ". Echoing it back to the sender."); 18 + log.info("Received message. Echoing it back to the sender.");
13 message.respond(message.payload()); 19 message.respond(message.payload());
14 } 20 }
15 } 21 }
......
1 package org.onlab.netty; 1 package org.onlab.netty;
2 2
3 +import java.util.Objects;
4 +
5 +import com.google.common.base.MoreObjects;
6 +
3 /** 7 /**
4 * Representation of a TCP/UDP communication end point. 8 * Representation of a TCP/UDP communication end point.
5 */ 9 */
...@@ -8,6 +12,15 @@ public class Endpoint { ...@@ -8,6 +12,15 @@ public class Endpoint {
8 private final int port; 12 private final int port;
9 private final String host; 13 private final String host;
10 14
15 + /**
16 + * Used for serialization.
17 + */
18 + @SuppressWarnings("unused")
19 + private Endpoint() {
20 + port = 0;
21 + host = null;
22 + }
23 +
11 public Endpoint(String host, int port) { 24 public Endpoint(String host, int port) {
12 this.host = host; 25 this.host = host;
13 this.port = port; 26 this.port = port;
...@@ -23,16 +36,15 @@ public class Endpoint { ...@@ -23,16 +36,15 @@ public class Endpoint {
23 36
24 @Override 37 @Override
25 public String toString() { 38 public String toString() {
26 - return "Endpoint [port=" + port + ", host=" + host + "]"; 39 + return MoreObjects.toStringHelper(getClass())
40 + .add("port", port)
41 + .add("host", host)
42 + .toString();
27 } 43 }
28 44
29 @Override 45 @Override
30 public int hashCode() { 46 public int hashCode() {
31 - final int prime = 31; 47 + return Objects.hash(host, port);
32 - int result = 1;
33 - result = prime * result + ((host == null) ? 0 : host.hashCode());
34 - result = prime * result + port;
35 - return result;
36 } 48 }
37 49
38 @Override 50 @Override
...@@ -46,17 +58,8 @@ public class Endpoint { ...@@ -46,17 +58,8 @@ public class Endpoint {
46 if (getClass() != obj.getClass()) { 58 if (getClass() != obj.getClass()) {
47 return false; 59 return false;
48 } 60 }
49 - Endpoint other = (Endpoint) obj; 61 + Endpoint that = (Endpoint) obj;
50 - if (host == null) { 62 + return Objects.equals(this.port, that.port) &&
51 - if (other.host != null) { 63 + Objects.equals(this.host, that.host);
52 - return false;
53 - }
54 - } else if (!host.equals(other.host)) {
55 - return false;
56 - }
57 - if (port != other.port) {
58 - return false;
59 - }
60 - return true;
61 } 64 }
62 } 65 }
......
...@@ -8,12 +8,13 @@ import java.io.IOException; ...@@ -8,12 +8,13 @@ import java.io.IOException;
8 */ 8 */
9 public final class InternalMessage implements Message { 9 public final class InternalMessage implements Message {
10 10
11 + public static final String REPLY_MESSAGE_TYPE = "NETTY_MESSAGIG_REQUEST_REPLY";
12 +
11 private long id; 13 private long id;
12 private Endpoint sender; 14 private Endpoint sender;
13 private String type; 15 private String type;
14 - private Object payload; 16 + private byte[] payload;
15 private transient NettyMessagingService messagingService; 17 private transient NettyMessagingService messagingService;
16 - public static final String REPLY_MESSAGE_TYPE = "NETTY_MESSAGIG_REQUEST_REPLY";
17 18
18 // Must be created using the Builder. 19 // Must be created using the Builder.
19 private InternalMessage() {} 20 private InternalMessage() {}
...@@ -31,12 +32,16 @@ public final class InternalMessage implements Message { ...@@ -31,12 +32,16 @@ public final class InternalMessage implements Message {
31 } 32 }
32 33
33 @Override 34 @Override
34 - public Object payload() { 35 + public byte[] payload() {
35 return payload; 36 return payload;
36 } 37 }
37 38
39 + protected void setMessagingService(NettyMessagingService messagingService) {
40 + this.messagingService = messagingService;
41 + }
42 +
38 @Override 43 @Override
39 - public void respond(Object data) throws IOException { 44 + public void respond(byte[] data) throws IOException {
40 Builder builder = new Builder(messagingService); 45 Builder builder = new Builder(messagingService);
41 InternalMessage message = builder.withId(this.id) 46 InternalMessage message = builder.withId(this.id)
42 // FIXME: Sender should be messagingService.localEp. 47 // FIXME: Sender should be messagingService.localEp.
...@@ -51,7 +56,7 @@ public final class InternalMessage implements Message { ...@@ -51,7 +56,7 @@ public final class InternalMessage implements Message {
51 /** 56 /**
52 * Builder for InternalMessages. 57 * Builder for InternalMessages.
53 */ 58 */
54 - public static class Builder { 59 + public static final class Builder {
55 private InternalMessage message; 60 private InternalMessage message;
56 61
57 public Builder(NettyMessagingService messagingService) { 62 public Builder(NettyMessagingService messagingService) {
...@@ -73,7 +78,7 @@ public final class InternalMessage implements Message { ...@@ -73,7 +78,7 @@ public final class InternalMessage implements Message {
73 message.sender = sender; 78 message.sender = sender;
74 return this; 79 return this;
75 } 80 }
76 - public Builder withPayload(Object payload) { 81 + public Builder withPayload(byte[] payload) {
77 message.payload = payload; 82 message.payload = payload;
78 return this; 83 return this;
79 } 84 }
......
1 package org.onlab.netty; 1 package org.onlab.netty;
2 2
3 import org.onlab.util.KryoPool; 3 import org.onlab.util.KryoPool;
4 -import org.slf4j.Logger;
5 -import org.slf4j.LoggerFactory;
6 4
5 +import java.nio.ByteBuffer;
7 import java.util.ArrayList; 6 import java.util.ArrayList;
8 import java.util.HashMap; 7 import java.util.HashMap;
9 8
9 +//FIXME: Should be move out to test or app
10 /** 10 /**
11 * Kryo Serializer. 11 * Kryo Serializer.
12 */ 12 */
13 -public class KryoSerializer implements Serializer { 13 +public class KryoSerializer {
14 -
15 - private final Logger log = LoggerFactory.getLogger(getClass());
16 14
17 private KryoPool serializerPool; 15 private KryoPool serializerPool;
18 16
...@@ -28,20 +26,29 @@ public class KryoSerializer implements Serializer { ...@@ -28,20 +26,29 @@ public class KryoSerializer implements Serializer {
28 serializerPool = KryoPool.newBuilder() 26 serializerPool = KryoPool.newBuilder()
29 .register(ArrayList.class, 27 .register(ArrayList.class,
30 HashMap.class, 28 HashMap.class,
31 - ArrayList.class 29 + ArrayList.class,
30 + InternalMessage.class,
31 + Endpoint.class,
32 + byte[].class
32 ) 33 )
33 .build() 34 .build()
34 .populate(1); 35 .populate(1);
35 } 36 }
36 37
37 38
38 - @Override 39 + public <T> T decode(byte[] data) {
39 - public Object decode(byte[] data) {
40 return serializerPool.deserialize(data); 40 return serializerPool.deserialize(data);
41 } 41 }
42 42
43 - @Override
44 public byte[] encode(Object payload) { 43 public byte[] encode(Object payload) {
45 return serializerPool.serialize(payload); 44 return serializerPool.serialize(payload);
46 } 45 }
46 +
47 + public <T> T decode(ByteBuffer buffer) {
48 + return serializerPool.deserialize(buffer);
49 + }
50 +
51 + public void encode(Object obj, ByteBuffer buffer) {
52 + serializerPool.serialize(obj, buffer);
53 + }
47 } 54 }
......
1 package org.onlab.netty; 1 package org.onlab.netty;
2 2
3 +import org.slf4j.Logger;
4 +import org.slf4j.LoggerFactory;
5 +
3 /** 6 /**
4 * A MessageHandler that simply logs the information. 7 * A MessageHandler that simply logs the information.
5 */ 8 */
6 public class LoggingHandler implements MessageHandler { 9 public class LoggingHandler implements MessageHandler {
7 10
11 + private final Logger log = LoggerFactory.getLogger(getClass());
12 +
8 @Override 13 @Override
9 public void handle(Message message) { 14 public void handle(Message message) {
10 - System.out.println("Received: " + message.payload()); 15 + log.info("Received message. Payload has {} bytes", message.payload().length);
11 } 16 }
12 } 17 }
......
...@@ -12,12 +12,12 @@ public interface Message { ...@@ -12,12 +12,12 @@ public interface Message {
12 * Returns the payload of this message. 12 * Returns the payload of this message.
13 * @return message payload. 13 * @return message payload.
14 */ 14 */
15 - public Object payload(); 15 + public byte[] payload();
16 16
17 /** 17 /**
18 - * Sends a reply back to the sender of this messge. 18 + * Sends a reply back to the sender of this message.
19 * @param data payload of the response. 19 * @param data payload of the response.
20 * @throws IOException if there is a communication error. 20 * @throws IOException if there is a communication error.
21 */ 21 */
22 - public void respond(Object data) throws IOException; 22 + public void respond(byte[] data) throws IOException;
23 } 23 }
......
1 package org.onlab.netty; 1 package org.onlab.netty;
2 2
3 -import java.util.Arrays;
4 -import java.util.List;
5 -
6 import static com.google.common.base.Preconditions.checkState; 3 import static com.google.common.base.Preconditions.checkState;
7 -
8 import io.netty.buffer.ByteBuf; 4 import io.netty.buffer.ByteBuf;
9 import io.netty.channel.ChannelHandlerContext; 5 import io.netty.channel.ChannelHandlerContext;
10 -import io.netty.handler.codec.ByteToMessageDecoder; 6 +import io.netty.handler.codec.ReplayingDecoder;
7 +
8 +import java.util.Arrays;
9 +import java.util.List;
10 +
11 +import org.slf4j.Logger;
12 +import org.slf4j.LoggerFactory;
11 13
12 /** 14 /**
13 - * Decode bytes into a InternalMessage. 15 + * Decoder for inbound messages.
14 */ 16 */
15 -public class MessageDecoder extends ByteToMessageDecoder { 17 +public class MessageDecoder extends ReplayingDecoder<DecoderState> {
18 +
19 + private final Logger log = LoggerFactory.getLogger(getClass());
16 20
17 private final NettyMessagingService messagingService; 21 private final NettyMessagingService messagingService;
18 - private final Serializer serializer;
19 22
20 - public MessageDecoder(NettyMessagingService messagingService, Serializer serializer) { 23 + private static final KryoSerializer SERIALIZER = new KryoSerializer();
24 +
25 + private int contentLength;
26 +
27 + public MessageDecoder(NettyMessagingService messagingService) {
28 + super(DecoderState.READ_HEADER_VERSION);
21 this.messagingService = messagingService; 29 this.messagingService = messagingService;
22 - this.serializer = serializer;
23 } 30 }
24 31
25 @Override 32 @Override
26 - protected void decode(ChannelHandlerContext context, ByteBuf in, 33 + protected void decode(
27 - List<Object> messages) throws Exception { 34 + ChannelHandlerContext context,
28 - 35 + ByteBuf buffer,
29 - byte[] preamble = in.readBytes(MessageEncoder.PREAMBLE.length).array(); 36 + List<Object> out) throws Exception {
37 +
38 + switch(state()) {
39 + case READ_HEADER_VERSION:
40 + int headerVersion = buffer.readInt();
41 + checkState(headerVersion == MessageEncoder.HEADER_VERSION, "Unexpected header version");
42 + checkpoint(DecoderState.READ_PREAMBLE);
43 + case READ_PREAMBLE:
44 + byte[] preamble = new byte[MessageEncoder.PREAMBLE.length];
45 + buffer.readBytes(preamble);
30 checkState(Arrays.equals(MessageEncoder.PREAMBLE, preamble), "Message has wrong preamble"); 46 checkState(Arrays.equals(MessageEncoder.PREAMBLE, preamble), "Message has wrong preamble");
47 + checkpoint(DecoderState.READ_CONTENT_LENGTH);
48 + case READ_CONTENT_LENGTH:
49 + contentLength = buffer.readInt();
50 + checkpoint(DecoderState.READ_SERIALIZER_VERSION);
51 + case READ_SERIALIZER_VERSION:
52 + int serializerVersion = buffer.readInt();
53 + checkState(serializerVersion == MessageEncoder.SERIALIZER_VERSION, "Unexpected serializer version");
54 + checkpoint(DecoderState.READ_CONTENT);
55 + case READ_CONTENT:
56 + InternalMessage message = SERIALIZER.decode(buffer.readBytes(contentLength).nioBuffer());
57 + message.setMessagingService(messagingService);
58 + out.add(message);
59 + checkpoint(DecoderState.READ_HEADER_VERSION);
60 + break;
61 + default:
62 + checkState(false, "Must not be here");
63 + }
64 + }
31 65
32 - // read message Id. 66 + @Override
33 - long id = in.readLong(); 67 + public void exceptionCaught(ChannelHandlerContext context, Throwable cause) {
34 - 68 + log.error("Exception inside channel handling pipeline.", cause);
35 - // read message type; first read size and then bytes. 69 + context.close();
36 - String type = new String(in.readBytes(in.readInt()).array());
37 -
38 - // read sender host name; first read size and then bytes.
39 - String host = new String(in.readBytes(in.readInt()).array());
40 -
41 - // read sender port.
42 - int port = in.readInt();
43 -
44 - Endpoint sender = new Endpoint(host, port);
45 -
46 - // read message payload; first read size and then bytes.
47 - Object payload = serializer.decode(in.readBytes(in.readInt()).array());
48 -
49 - InternalMessage message = new InternalMessage.Builder(messagingService)
50 - .withId(id)
51 - .withSender(sender)
52 - .withType(type)
53 - .withPayload(payload)
54 - .build();
55 -
56 - messages.add(message);
57 } 70 }
58 } 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;
7 +import io.netty.channel.ChannelHandler.Sharable;
4 import io.netty.channel.ChannelHandlerContext; 8 import io.netty.channel.ChannelHandlerContext;
5 import io.netty.handler.codec.MessageToByteEncoder; 9 import io.netty.handler.codec.MessageToByteEncoder;
6 10
7 /** 11 /**
8 * Encode InternalMessage out into a byte buffer. 12 * Encode InternalMessage out into a byte buffer.
9 */ 13 */
14 +@Sharable
10 public class MessageEncoder extends MessageToByteEncoder<InternalMessage> { 15 public class MessageEncoder extends MessageToByteEncoder<InternalMessage> {
11 16
17 + private final Logger log = LoggerFactory.getLogger(getClass());
18 +
12 // onosiscool in ascii 19 // onosiscool in ascii
13 public static final byte[] PREAMBLE = "onosiscool".getBytes(); 20 public static final byte[] PREAMBLE = "onosiscool".getBytes();
21 + public static final int HEADER_VERSION = 1;
22 + public static final int SERIALIZER_VERSION = 1;
14 23
15 - private final Serializer serializer;
16 24
17 - public MessageEncoder(Serializer serializer) { 25 + private static final KryoSerializer SERIALIZER = new KryoSerializer();
18 - this.serializer = serializer;
19 - }
20 26
21 @Override 27 @Override
22 - protected void encode(ChannelHandlerContext context, InternalMessage message, 28 + protected void encode(
29 + ChannelHandlerContext context,
30 + InternalMessage message,
23 ByteBuf out) throws Exception { 31 ByteBuf out) throws Exception {
24 32
33 + // write version
34 + out.writeInt(HEADER_VERSION);
35 +
25 // write preamble 36 // write preamble
26 out.writeBytes(PREAMBLE); 37 out.writeBytes(PREAMBLE);
27 38
28 - // write id 39 + byte[] payload = SERIALIZER.encode(message);
29 - out.writeLong(message.id());
30 -
31 - // write type length
32 - out.writeInt(message.type().length());
33 -
34 - // write type
35 - out.writeBytes(message.type().getBytes());
36 -
37 - // write sender host name size
38 - out.writeInt(message.sender().host().length());
39 40
40 - // write sender host name. 41 + // write payload length
41 - out.writeBytes(message.sender().host().getBytes()); 42 + out.writeInt(payload.length);
42 43
43 - // write port 44 + // write payloadSerializer version
44 - out.writeInt(message.sender().port()); 45 + out.writeInt(SERIALIZER_VERSION);
45 46
46 - try { 47 + // write payload.
47 - serializer.encode(message.payload()); 48 + out.writeBytes(payload);
48 - } catch (Exception e) {
49 - e.printStackTrace();
50 } 49 }
51 50
52 - byte[] payload = serializer.encode(message.payload()); 51 + @Override
53 - 52 + public void exceptionCaught(ChannelHandlerContext context, Throwable cause) {
54 - // write payload length. 53 + log.error("Exception inside channel handling pipeline.", cause);
55 - out.writeInt(payload.length); 54 + context.close();
56 -
57 - // write payload bytes
58 - out.writeBytes(payload);
59 } 55 }
60 } 56 }
......
...@@ -11,10 +11,10 @@ public interface MessagingService { ...@@ -11,10 +11,10 @@ public interface MessagingService {
11 * The message is specified using the type and payload. 11 * The message is specified using the type and payload.
12 * @param ep end point to send the message to. 12 * @param ep end point to send the message to.
13 * @param type type of message. 13 * @param type type of message.
14 - * @param payload message payload. 14 + * @param payload message payload bytes.
15 * @throws IOException 15 * @throws IOException
16 */ 16 */
17 - public void sendAsync(Endpoint ep, String type, Object payload) throws IOException; 17 + public void sendAsync(Endpoint ep, String type, byte[] payload) throws IOException;
18 18
19 /** 19 /**
20 * Sends a message synchronously and waits for a response. 20 * Sends a message synchronously and waits for a response.
...@@ -24,7 +24,7 @@ public interface MessagingService { ...@@ -24,7 +24,7 @@ public interface MessagingService {
24 * @return a response future 24 * @return a response future
25 * @throws IOException 25 * @throws IOException
26 */ 26 */
27 - public <T> Response<T> sendAndReceive(Endpoint ep, String type, Object payload) throws IOException; 27 + public Response sendAndReceive(Endpoint ep, String type, byte[] payload) throws IOException;
28 28
29 /** 29 /**
30 * Registers a new message handler for message type. 30 * Registers a new message handler for message type.
......
...@@ -11,6 +11,7 @@ import io.netty.bootstrap.ServerBootstrap; ...@@ -11,6 +11,7 @@ import io.netty.bootstrap.ServerBootstrap;
11 import io.netty.buffer.PooledByteBufAllocator; 11 import io.netty.buffer.PooledByteBufAllocator;
12 import io.netty.channel.Channel; 12 import io.netty.channel.Channel;
13 import io.netty.channel.ChannelFuture; 13 import io.netty.channel.ChannelFuture;
14 +import io.netty.channel.ChannelHandler;
14 import io.netty.channel.ChannelHandlerContext; 15 import io.netty.channel.ChannelHandlerContext;
15 import io.netty.channel.ChannelInitializer; 16 import io.netty.channel.ChannelInitializer;
16 import io.netty.channel.ChannelOption; 17 import io.netty.channel.ChannelOption;
...@@ -22,7 +23,6 @@ import io.netty.channel.socket.nio.NioServerSocketChannel; ...@@ -22,7 +23,6 @@ import io.netty.channel.socket.nio.NioServerSocketChannel;
22 import io.netty.channel.socket.nio.NioSocketChannel; 23 import io.netty.channel.socket.nio.NioSocketChannel;
23 24
24 import org.apache.commons.lang.math.RandomUtils; 25 import org.apache.commons.lang.math.RandomUtils;
25 -import org.apache.commons.pool.KeyedObjectPool;
26 import org.apache.commons.pool.KeyedPoolableObjectFactory; 26 import org.apache.commons.pool.KeyedPoolableObjectFactory;
27 import org.apache.commons.pool.impl.GenericKeyedObjectPool; 27 import org.apache.commons.pool.impl.GenericKeyedObjectPool;
28 import org.slf4j.Logger; 28 import org.slf4j.Logger;
...@@ -38,16 +38,19 @@ public class NettyMessagingService implements MessagingService { ...@@ -38,16 +38,19 @@ public class NettyMessagingService implements MessagingService {
38 38
39 private final Logger log = LoggerFactory.getLogger(getClass()); 39 private final Logger log = LoggerFactory.getLogger(getClass());
40 40
41 - private KeyedObjectPool<Endpoint, Channel> channels =
42 - new GenericKeyedObjectPool<Endpoint, Channel>(new OnosCommunicationChannelFactory());
43 private final int port; 41 private final int port;
42 + private final Endpoint localEp;
44 private final EventLoopGroup bossGroup = new NioEventLoopGroup(); 43 private final EventLoopGroup bossGroup = new NioEventLoopGroup();
45 private final EventLoopGroup workerGroup = new NioEventLoopGroup(); 44 private final EventLoopGroup workerGroup = new NioEventLoopGroup();
46 private final ConcurrentMap<String, MessageHandler> handlers = new ConcurrentHashMap<>(); 45 private final ConcurrentMap<String, MessageHandler> handlers = new ConcurrentHashMap<>();
47 - private Cache<Long, AsyncResponse<?>> responseFutures; 46 + private final Cache<Long, AsyncResponse> responseFutures = CacheBuilder.newBuilder()
48 - private final Endpoint localEp; 47 + .maximumSize(100000)
49 - 48 + .weakValues()
50 - protected Serializer serializer; 49 + // TODO: Once the entry expires, notify blocking threads (if any).
50 + .expireAfterWrite(10, TimeUnit.MINUTES)
51 + .build();
52 + private final GenericKeyedObjectPool<Endpoint, Channel> channels
53 + = new GenericKeyedObjectPool<Endpoint, Channel>(new OnosCommunicationChannelFactory());
51 54
52 public NettyMessagingService() { 55 public NettyMessagingService() {
53 // TODO: Default port should be configurable. 56 // TODO: Default port should be configurable.
...@@ -66,12 +69,8 @@ public class NettyMessagingService implements MessagingService { ...@@ -66,12 +69,8 @@ public class NettyMessagingService implements MessagingService {
66 } 69 }
67 70
68 public void activate() throws Exception { 71 public void activate() throws Exception {
69 - responseFutures = CacheBuilder.newBuilder() 72 + channels.setTestOnBorrow(true);
70 - .maximumSize(100000) 73 + channels.setTestOnReturn(true);
71 - .weakValues()
72 - // TODO: Once the entry expires, notify blocking threads (if any).
73 - .expireAfterWrite(10, TimeUnit.MINUTES)
74 - .build();
75 startAcceptingConnections(); 74 startAcceptingConnections();
76 } 75 }
77 76
...@@ -82,7 +81,7 @@ public class NettyMessagingService implements MessagingService { ...@@ -82,7 +81,7 @@ public class NettyMessagingService implements MessagingService {
82 } 81 }
83 82
84 @Override 83 @Override
85 - public void sendAsync(Endpoint ep, String type, Object payload) throws IOException { 84 + public void sendAsync(Endpoint ep, String type, byte[] payload) throws IOException {
86 InternalMessage message = new InternalMessage.Builder(this) 85 InternalMessage message = new InternalMessage.Builder(this)
87 .withId(RandomUtils.nextLong()) 86 .withId(RandomUtils.nextLong())
88 .withSender(localEp) 87 .withSender(localEp)
...@@ -95,24 +94,21 @@ public class NettyMessagingService implements MessagingService { ...@@ -95,24 +94,21 @@ public class NettyMessagingService implements MessagingService {
95 protected void sendAsync(Endpoint ep, InternalMessage message) throws IOException { 94 protected void sendAsync(Endpoint ep, InternalMessage message) throws IOException {
96 Channel channel = null; 95 Channel channel = null;
97 try { 96 try {
97 + try {
98 channel = channels.borrowObject(ep); 98 channel = channels.borrowObject(ep);
99 channel.eventLoop().execute(new WriteTask(channel, message)); 99 channel.eventLoop().execute(new WriteTask(channel, message));
100 - } catch (Exception e) {
101 - throw new IOException(e);
102 } finally { 100 } finally {
103 - try {
104 channels.returnObject(ep, channel); 101 channels.returnObject(ep, channel);
105 - } catch (Exception e) {
106 - log.warn("Error returning object back to the pool", e);
107 - // ignored.
108 } 102 }
103 + } catch (Exception e) {
104 + throw new IOException(e);
109 } 105 }
110 } 106 }
111 107
112 @Override 108 @Override
113 - public <T> Response<T> sendAndReceive(Endpoint ep, String type, Object payload) 109 + public Response sendAndReceive(Endpoint ep, String type, byte[] payload)
114 throws IOException { 110 throws IOException {
115 - AsyncResponse<T> futureResponse = new AsyncResponse<T>(); 111 + AsyncResponse futureResponse = new AsyncResponse();
116 Long messageId = RandomUtils.nextLong(); 112 Long messageId = RandomUtils.nextLong();
117 responseFutures.put(messageId, futureResponse); 113 responseFutures.put(messageId, futureResponse);
118 InternalMessage message = new InternalMessage.Builder(this) 114 InternalMessage message = new InternalMessage.Builder(this)
...@@ -141,6 +137,9 @@ public class NettyMessagingService implements MessagingService { ...@@ -141,6 +137,9 @@ public class NettyMessagingService implements MessagingService {
141 137
142 private void startAcceptingConnections() throws InterruptedException { 138 private void startAcceptingConnections() throws InterruptedException {
143 ServerBootstrap b = new ServerBootstrap(); 139 ServerBootstrap b = new ServerBootstrap();
140 + b.option(ChannelOption.WRITE_BUFFER_HIGH_WATER_MARK, 32 * 1024);
141 + b.option(ChannelOption.WRITE_BUFFER_LOW_WATER_MARK, 8 * 1024);
142 + // TODO: Need JVM options to configure PooledByteBufAllocator.
144 b.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT); 143 b.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);
145 b.group(bossGroup, workerGroup) 144 b.group(bossGroup, workerGroup)
146 .channel(NioServerSocketChannel.class) 145 .channel(NioServerSocketChannel.class)
...@@ -167,17 +166,18 @@ public class NettyMessagingService implements MessagingService { ...@@ -167,17 +166,18 @@ public class NettyMessagingService implements MessagingService {
167 166
168 @Override 167 @Override
169 public Channel makeObject(Endpoint ep) throws Exception { 168 public Channel makeObject(Endpoint ep) throws Exception {
170 - Bootstrap b = new Bootstrap(); 169 + Bootstrap bootstrap = new Bootstrap();
171 - b.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT); 170 + bootstrap.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);
172 - b.group(workerGroup); 171 + bootstrap.option(ChannelOption.WRITE_BUFFER_HIGH_WATER_MARK, 32 * 1024);
172 + bootstrap.option(ChannelOption.WRITE_BUFFER_LOW_WATER_MARK, 8 * 1024);
173 + bootstrap.group(workerGroup);
173 // TODO: Make this faster: 174 // TODO: Make this faster:
174 // http://normanmaurer.me/presentations/2014-facebook-eng-netty/slides.html#37.0 175 // http://normanmaurer.me/presentations/2014-facebook-eng-netty/slides.html#37.0
175 - b.channel(NioSocketChannel.class); 176 + bootstrap.channel(NioSocketChannel.class);
176 - b.option(ChannelOption.SO_KEEPALIVE, true); 177 + bootstrap.option(ChannelOption.SO_KEEPALIVE, true);
177 - b.handler(new OnosCommunicationChannelInitializer()); 178 + bootstrap.handler(new OnosCommunicationChannelInitializer());
178 -
179 // Start the client. 179 // Start the client.
180 - ChannelFuture f = b.connect(ep.host(), ep.port()).sync(); 180 + ChannelFuture f = bootstrap.connect(ep.host(), ep.port()).sync();
181 return f.channel(); 181 return f.channel();
182 } 182 }
183 183
...@@ -194,31 +194,35 @@ public class NettyMessagingService implements MessagingService { ...@@ -194,31 +194,35 @@ public class NettyMessagingService implements MessagingService {
194 194
195 private class OnosCommunicationChannelInitializer extends ChannelInitializer<SocketChannel> { 195 private class OnosCommunicationChannelInitializer extends ChannelInitializer<SocketChannel> {
196 196
197 + private final ChannelHandler dispatcher = new InboundMessageDispatcher();
198 + private final ChannelHandler encoder = new MessageEncoder();
199 +
197 @Override 200 @Override
198 protected void initChannel(SocketChannel channel) throws Exception { 201 protected void initChannel(SocketChannel channel) throws Exception {
199 channel.pipeline() 202 channel.pipeline()
200 - .addLast(new MessageEncoder(serializer)) 203 + .addLast("encoder", encoder)
201 - .addLast(new MessageDecoder(NettyMessagingService.this, serializer)) 204 + .addLast("decoder", new MessageDecoder(NettyMessagingService.this))
202 - .addLast(new NettyMessagingService.InboundMessageDispatcher()); 205 + .addLast("handler", dispatcher);
203 } 206 }
204 } 207 }
205 208
206 private class WriteTask implements Runnable { 209 private class WriteTask implements Runnable {
207 210
208 - private final Object message; 211 + private final InternalMessage message;
209 private final Channel channel; 212 private final Channel channel;
210 213
211 - public WriteTask(Channel channel, Object message) { 214 + public WriteTask(Channel channel, InternalMessage message) {
212 - this.message = message;
213 this.channel = channel; 215 this.channel = channel;
216 + this.message = message;
214 } 217 }
215 218
216 @Override 219 @Override
217 public void run() { 220 public void run() {
218 - channel.writeAndFlush(message); 221 + channel.writeAndFlush(message, channel.voidPromise());
219 } 222 }
220 } 223 }
221 224
225 + @ChannelHandler.Sharable
222 private class InboundMessageDispatcher extends SimpleChannelInboundHandler<InternalMessage> { 226 private class InboundMessageDispatcher extends SimpleChannelInboundHandler<InternalMessage> {
223 227
224 @Override 228 @Override
...@@ -226,12 +230,13 @@ public class NettyMessagingService implements MessagingService { ...@@ -226,12 +230,13 @@ public class NettyMessagingService implements MessagingService {
226 String type = message.type(); 230 String type = message.type();
227 if (type.equals(InternalMessage.REPLY_MESSAGE_TYPE)) { 231 if (type.equals(InternalMessage.REPLY_MESSAGE_TYPE)) {
228 try { 232 try {
229 - AsyncResponse<?> futureResponse = 233 + AsyncResponse futureResponse =
230 NettyMessagingService.this.responseFutures.getIfPresent(message.id()); 234 NettyMessagingService.this.responseFutures.getIfPresent(message.id());
231 if (futureResponse != null) { 235 if (futureResponse != null) {
232 futureResponse.setResponse(message.payload()); 236 futureResponse.setResponse(message.payload());
233 - } 237 + } else {
234 log.warn("Received a reply. But was unable to locate the request handle"); 238 log.warn("Received a reply. But was unable to locate the request handle");
239 + }
235 } finally { 240 } finally {
236 NettyMessagingService.this.responseFutures.invalidate(message.id()); 241 NettyMessagingService.this.responseFutures.invalidate(message.id());
237 } 242 }
...@@ -240,5 +245,11 @@ public class NettyMessagingService implements MessagingService { ...@@ -240,5 +245,11 @@ public class NettyMessagingService implements MessagingService {
240 MessageHandler handler = NettyMessagingService.this.getMessageHandler(type); 245 MessageHandler handler = NettyMessagingService.this.getMessageHandler(type);
241 handler.handle(message); 246 handler.handle(message);
242 } 247 }
248 +
249 + @Override
250 + public void exceptionCaught(ChannelHandlerContext context, Throwable cause) {
251 + log.error("Exception inside channel handling pipeline.", cause);
252 + context.close();
253 + }
243 } 254 }
244 } 255 }
......
...@@ -7,26 +7,24 @@ import java.util.concurrent.TimeoutException; ...@@ -7,26 +7,24 @@ import java.util.concurrent.TimeoutException;
7 * Response object returned when making synchronous requests. 7 * Response object returned when making synchronous requests.
8 * Can you used to check is a response is ready and/or wait for a response 8 * Can you used to check is a response is ready and/or wait for a response
9 * to become available. 9 * to become available.
10 - *
11 - * @param <T> type of response.
12 */ 10 */
13 -public interface Response<T> { 11 +public interface Response {
14 12
15 /** 13 /**
16 * Gets the response waiting for a designated timeout period. 14 * Gets the response waiting for a designated timeout period.
17 * @param timeout timeout period (since request was sent out) 15 * @param timeout timeout period (since request was sent out)
18 * @param tu unit of time. 16 * @param tu unit of time.
19 - * @return response 17 + * @return response payload
20 * @throws TimeoutException if the timeout expires before the response arrives. 18 * @throws TimeoutException if the timeout expires before the response arrives.
21 */ 19 */
22 - public T get(long timeout, TimeUnit tu) throws TimeoutException; 20 + public byte[] get(long timeout, TimeUnit tu) throws TimeoutException;
23 21
24 /** 22 /**
25 * Gets the response waiting for indefinite timeout period. 23 * Gets the response waiting for indefinite timeout period.
26 - * @return response 24 + * @return response payload
27 * @throws InterruptedException if the thread is interrupted before the response arrives. 25 * @throws InterruptedException if the thread is interrupted before the response arrives.
28 */ 26 */
29 - public T get() throws InterruptedException; 27 + public byte[] get() throws InterruptedException;
30 28
31 /** 29 /**
32 * Checks if the response is ready without blocking. 30 * Checks if the response is ready without blocking.
......
1 -package org.onlab.netty;
2 -
3 -/**
4 - * Interface for encoding/decoding message payloads.
5 - */
6 -public interface Serializer {
7 -
8 - /**
9 - * Decodes the specified byte array to a POJO.
10 - *
11 - * @param data byte array.
12 - * @return POJO
13 - */
14 - Object decode(byte[] data);
15 -
16 - /**
17 - * Encodes the specified POJO into a byte array.
18 - *
19 - * @param data POJO to be encoded
20 - * @return byte array.
21 - */
22 - byte[] encode(Object message);
23 -
24 -}
1 -package org.onlab.netty;
2 -
3 -import java.util.concurrent.TimeUnit;
4 -
5 -public final class SimpleClient {
6 - private SimpleClient() {}
7 -
8 - public static void main(String... args) throws Exception {
9 - NettyMessagingService messaging = new TestNettyMessagingService(9081);
10 - messaging.activate();
11 -
12 - messaging.sendAsync(new Endpoint("localhost", 8080), "simple", "Hello World");
13 - Response<String> response = messaging.sendAndReceive(new Endpoint("localhost", 8080), "echo", "Hello World");
14 - System.out.println("Got back:" + response.get(2, TimeUnit.SECONDS));
15 - }
16 -
17 - public static class TestNettyMessagingService extends NettyMessagingService {
18 - public TestNettyMessagingService(int port) throws Exception {
19 - super(port);
20 - Serializer serializer = new KryoSerializer();
21 - this.serializer = serializer;
22 - }
23 - }
24 -}
1 -package org.onlab.netty;
2 -
3 -public final class SimpleServer {
4 - private SimpleServer() {}
5 -
6 - public static void main(String... args) throws Exception {
7 - NettyMessagingService server = new TestNettyMessagingService();
8 - server.activate();
9 - server.registerHandler("simple", new LoggingHandler());
10 - server.registerHandler("echo", new EchoHandler());
11 - }
12 -
13 - public static class TestNettyMessagingService extends NettyMessagingService {
14 - protected TestNettyMessagingService() {
15 - Serializer serializer = new KryoSerializer();
16 - this.serializer = serializer;
17 - }
18 - }
19 -}
1 +package org.onlab.netty;
2 +
3 +import java.util.concurrent.TimeUnit;
4 +
5 +import org.apache.commons.lang3.RandomUtils;
6 +import static org.junit.Assert.*;
7 +import org.junit.Test;
8 +
9 +/**
10 + * Simple ping-pong test that exercises NettyMessagingService.
11 + */
12 +public class PingPongTest {
13 +
14 + @Test
15 + public void testPingPong() throws Exception {
16 + NettyMessagingService pinger = new NettyMessagingService(8085);
17 + NettyMessagingService ponger = new NettyMessagingService(9086);
18 + try {
19 + pinger.activate();
20 + ponger.activate();
21 + ponger.registerHandler("echo", new EchoHandler());
22 + byte[] payload = RandomUtils.nextBytes(100);
23 + Response response = pinger.sendAndReceive(new Endpoint("localhost", 9086), "echo", payload);
24 + assertArrayEquals(payload, response.get(10000, TimeUnit.MILLISECONDS));
25 + } finally {
26 + pinger.deactivate();
27 + ponger.deactivate();
28 + }
29 + }
30 +}