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
diff --git a/apps/foo/pom.xml b/apps/foo/pom.xml
index 868b992..6109263 100644
--- a/apps/foo/pom.xml
+++ b/apps/foo/pom.xml
@@ -24,10 +24,20 @@
</dependency>
<dependency>
<groupId>org.onlab.onos</groupId>
+ <artifactId>onlab-osgi</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.onlab.onos</groupId>
<artifactId>onlab-nio</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
+ <groupId>org.onlab.onos</groupId>
+ <artifactId>onlab-netty</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
<groupId>org.apache.karaf.shell</groupId>
<artifactId>org.apache.karaf.shell.console</artifactId>
</dependency>
diff --git a/apps/foo/src/main/java/org/onlab/onos/foo/FooComponent.java b/apps/foo/src/main/java/org/onlab/onos/foo/FooComponent.java
index 1fafc32..473ea6e 100644
--- a/apps/foo/src/main/java/org/onlab/onos/foo/FooComponent.java
+++ b/apps/foo/src/main/java/org/onlab/onos/foo/FooComponent.java
@@ -11,6 +11,9 @@
import org.onlab.onos.net.device.DeviceEvent;
import org.onlab.onos.net.device.DeviceListener;
import org.onlab.onos.net.device.DeviceService;
+import org.onlab.onos.net.intent.IntentEvent;
+import org.onlab.onos.net.intent.IntentListener;
+import org.onlab.onos.net.intent.IntentService;
import org.slf4j.Logger;
import static org.slf4j.LoggerFactory.getLogger;
@@ -29,13 +32,18 @@
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected DeviceService deviceService;
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected IntentService intentService;
+
private final ClusterEventListener clusterListener = new InnerClusterListener();
private final DeviceListener deviceListener = new InnerDeviceListener();
+ private final IntentListener intentListener = new InnerIntentListener();
@Activate
public void activate() {
clusterService.addListener(clusterListener);
deviceService.addListener(deviceListener);
+ intentService.addListener(intentListener);
log.info("Started");
}
@@ -43,6 +51,7 @@
public void deactivate() {
clusterService.removeListener(clusterListener);
deviceService.removeListener(deviceListener);
+ intentService.removeListener(intentListener);
log.info("Stopped");
}
@@ -59,6 +68,23 @@
log.info("YEEEEHAAAAW! {}", event);
}
}
+
+ private class InnerIntentListener implements IntentListener {
+ @Override
+ public void event(IntentEvent event) {
+ String message;
+ if (event.type() == IntentEvent.Type.SUBMITTED) {
+ message = "WOW! It looks like someone has some intentions: {}";
+ } else if (event.type() == IntentEvent.Type.INSTALLED) {
+ message = "AWESOME! So far things are going great: {}";
+ } else if (event.type() == IntentEvent.Type.WITHDRAWN) {
+ message = "HMMM! Ambitions are fading apparently: {}";
+ } else {
+ message = "CRAP!!! Things are not turning out as intended: {}";
+ }
+ log.info(message, event.subject());
+ }
+ }
}
diff --git a/apps/foo/src/main/java/org/onlab/onos/foo/NettyEchoHandler.java b/apps/foo/src/main/java/org/onlab/onos/foo/NettyEchoHandler.java
new file mode 100644
index 0000000..1049a6d
--- /dev/null
+++ b/apps/foo/src/main/java/org/onlab/onos/foo/NettyEchoHandler.java
@@ -0,0 +1,23 @@
+package org.onlab.onos.foo;
+
+import java.io.IOException;
+
+import org.onlab.netty.Message;
+import org.onlab.netty.MessageHandler;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * Message handler that echos the message back to the sender.
+ */
+public class NettyEchoHandler implements MessageHandler {
+
+ private final Logger log = LoggerFactory.getLogger(getClass());
+
+ @Override
+ public void handle(Message message) throws IOException {
+ //log.info("Received message. Echoing it back to the sender.");
+ message.respond(message.payload());
+ }
+}
diff --git a/apps/foo/src/main/java/org/onlab/onos/foo/NettyLoggingHandler.java b/apps/foo/src/main/java/org/onlab/onos/foo/NettyLoggingHandler.java
new file mode 100644
index 0000000..b35a46f
--- /dev/null
+++ b/apps/foo/src/main/java/org/onlab/onos/foo/NettyLoggingHandler.java
@@ -0,0 +1,19 @@
+package org.onlab.onos.foo;
+
+import org.onlab.netty.Message;
+import org.onlab.netty.MessageHandler;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A MessageHandler that simply logs the information.
+ */
+public class NettyLoggingHandler implements MessageHandler {
+
+ private final Logger log = LoggerFactory.getLogger(getClass());
+
+ @Override
+ public void handle(Message message) {
+ //log.info("Received message. Payload has {} bytes", message.payload().length);
+ }
+}
diff --git a/apps/foo/src/main/java/org/onlab/onos/foo/SimpleNettyClient.java b/apps/foo/src/main/java/org/onlab/onos/foo/SimpleNettyClient.java
new file mode 100644
index 0000000..06daad8
--- /dev/null
+++ b/apps/foo/src/main/java/org/onlab/onos/foo/SimpleNettyClient.java
@@ -0,0 +1,82 @@
+package org.onlab.onos.foo;
+
+import java.io.IOException;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeoutException;
+
+import org.onlab.metrics.MetricsComponent;
+import org.onlab.metrics.MetricsFeature;
+import org.onlab.metrics.MetricsManager;
+import org.onlab.netty.Endpoint;
+import org.onlab.netty.NettyMessagingService;
+import org.onlab.netty.Response;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.codahale.metrics.Timer;
+
+// FIXME: Should be move out to test or app
+public final class SimpleNettyClient {
+
+private static Logger log = LoggerFactory.getLogger(SimpleNettyClient.class);
+
+ private SimpleNettyClient() {
+ }
+
+ public static void main(String[] args)
+ throws IOException, InterruptedException, ExecutionException,
+ TimeoutException {
+ try {
+ startStandalone(args);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ System.exit(0);
+ }
+ public static void startStandalone(String... args) throws Exception {
+ String host = args.length > 0 ? args[0] : "localhost";
+ int port = args.length > 1 ? Integer.parseInt(args[1]) : 8081;
+ int warmup = args.length > 2 ? Integer.parseInt(args[2]) : 1000;
+ int iterations = args.length > 3 ? Integer.parseInt(args[3]) : 50 * 100000;
+ NettyMessagingService messaging = new TestNettyMessagingService(9081);
+ MetricsManager metrics = new MetricsManager();
+ messaging.activate();
+ metrics.activate();
+ MetricsFeature feature = new MetricsFeature("latency");
+ MetricsComponent component = metrics.registerComponent("NettyMessaging");
+ log.info("warmup....");
+
+ for (int i = 0; i < warmup; i++) {
+ messaging.sendAsync(new Endpoint(host, port), "simple", "Hello World".getBytes());
+ Response response = messaging
+ .sendAndReceive(new Endpoint(host, port), "echo",
+ "Hello World".getBytes());
+ }
+
+ log.info("measuring async sender");
+ Timer sendAsyncTimer = metrics.createTimer(component, feature, "AsyncSender");
+
+ for (int i = 0; i < iterations; i++) {
+ Timer.Context context = sendAsyncTimer.time();
+ messaging.sendAsync(new Endpoint(host, port), "simple", "Hello World".getBytes());
+ context.stop();
+ }
+
+ Timer sendAndReceiveTimer = metrics.createTimer(component, feature, "SendAndReceive");
+ for (int i = 0; i < iterations; i++) {
+ Timer.Context context = sendAndReceiveTimer.time();
+ Response response = messaging
+ .sendAndReceive(new Endpoint(host, port), "echo",
+ "Hello World".getBytes());
+ // System.out.println("Got back:" + new String(response.get(2, TimeUnit.SECONDS)));
+ context.stop();
+ }
+ }
+
+ public static class TestNettyMessagingService extends NettyMessagingService {
+ public TestNettyMessagingService(int port) throws Exception {
+ super(port);
+ }
+ }
+}
diff --git a/apps/foo/src/main/java/org/onlab/onos/foo/SimpleNettyClientCommand.java b/apps/foo/src/main/java/org/onlab/onos/foo/SimpleNettyClientCommand.java
new file mode 100644
index 0000000..cce31db
--- /dev/null
+++ b/apps/foo/src/main/java/org/onlab/onos/foo/SimpleNettyClientCommand.java
@@ -0,0 +1,42 @@
+package org.onlab.onos.foo;
+
+import static org.onlab.onos.foo.SimpleNettyClient.startStandalone;
+
+import org.apache.karaf.shell.commands.Argument;
+import org.apache.karaf.shell.commands.Command;
+import org.onlab.onos.cli.AbstractShellCommand;
+
+/**
+ * Test Netty client performance.
+ */
+@Command(scope = "onos", name = "simple-netty-client",
+ description = "Starts the simple Netty client")
+public class SimpleNettyClientCommand extends AbstractShellCommand {
+
+ //FIXME: replace these arguments with proper ones needed for the test.
+ @Argument(index = 0, name = "hostname", description = "Server Hostname",
+ required = false, multiValued = false)
+ String host = "localhost";
+
+ @Argument(index = 3, name = "port", description = "Port",
+ required = false, multiValued = false)
+ String port = "8081";
+
+ @Argument(index = 1, name = "warmupCount", description = "Warm-up count",
+ required = false, multiValued = false)
+ String warmup = "1000";
+
+ @Argument(index = 2, name = "messageCount", description = "Message count",
+ required = false, multiValued = false)
+ String messageCount = "100000";
+
+ @Override
+ protected void execute() {
+ try {
+ startStandalone(new String[]{host, port, warmup, messageCount});
+ } catch (Exception e) {
+ error("Unable to start client %s", e);
+ }
+ }
+
+}
diff --git a/apps/foo/src/main/java/org/onlab/onos/foo/SimpleNettyServer.java b/apps/foo/src/main/java/org/onlab/onos/foo/SimpleNettyServer.java
new file mode 100644
index 0000000..5578fcd
--- /dev/null
+++ b/apps/foo/src/main/java/org/onlab/onos/foo/SimpleNettyServer.java
@@ -0,0 +1,27 @@
+package org.onlab.onos.foo;
+
+import org.onlab.netty.NettyMessagingService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Test to measure Messaging performance.
+ */
+ public final class SimpleNettyServer {
+ private static Logger log = LoggerFactory.getLogger(SimpleNettyServer.class);
+
+ private SimpleNettyServer() {}
+
+ public static void main(String... args) throws Exception {
+ startStandalone(args);
+ System.exit(0);
+ }
+
+ public static void startStandalone(String[] args) throws Exception {
+ NettyMessagingService server = new NettyMessagingService(8081);
+ server.activate();
+ server.registerHandler("simple", new NettyLoggingHandler());
+ server.registerHandler("echo", new NettyEchoHandler());
+ }
+ }
+
diff --git a/apps/foo/src/main/java/org/onlab/onos/foo/SimpleNettyServerCommand.java b/apps/foo/src/main/java/org/onlab/onos/foo/SimpleNettyServerCommand.java
new file mode 100644
index 0000000..17b2586
--- /dev/null
+++ b/apps/foo/src/main/java/org/onlab/onos/foo/SimpleNettyServerCommand.java
@@ -0,0 +1,38 @@
+package org.onlab.onos.foo;
+
+import static org.onlab.onos.foo.SimpleNettyServer.startStandalone;
+
+import org.apache.karaf.shell.commands.Argument;
+import org.apache.karaf.shell.commands.Command;
+import org.onlab.onos.cli.AbstractShellCommand;
+
+/**
+ * Starts the Simple Netty server.
+ */
+@Command(scope = "onos", name = "simple-netty-server",
+ description = "Starts the simple netty server")
+public class SimpleNettyServerCommand extends AbstractShellCommand {
+
+ //FIXME: Replace these with parameters for
+ @Argument(index = 0, name = "serverIp", description = "Server IP address",
+ required = false, multiValued = false)
+ String serverIp = "127.0.0.1";
+
+ @Argument(index = 1, name = "workers", description = "IO workers",
+ required = false, multiValued = false)
+ String workers = "6";
+
+ @Argument(index = 2, name = "messageLength", description = "Message length (bytes)",
+ required = false, multiValued = false)
+ String messageLength = "128";
+
+ @Override
+ protected void execute() {
+ try {
+ startStandalone(new String[]{serverIp, workers, messageLength});
+ } catch (Exception e) {
+ error("Unable to start server %s", e);
+ }
+ }
+
+}
diff --git a/apps/foo/src/main/resources/OSGI-INF/blueprint/shell-config.xml b/apps/foo/src/main/resources/OSGI-INF/blueprint/shell-config.xml
index 93bd020..f8ee2b8 100644
--- a/apps/foo/src/main/resources/OSGI-INF/blueprint/shell-config.xml
+++ b/apps/foo/src/main/resources/OSGI-INF/blueprint/shell-config.xml
@@ -7,6 +7,12 @@
<command>
<action class="org.onlab.onos.foo.TestIOServerCommand"/>
</command>
+ <command>
+ <action class="org.onlab.onos.foo.SimpleNettyServerCommand"/>
+ </command>
+ <command>
+ <action class="org.onlab.onos.foo.SimpleNettyClientCommand"/>
+ </command>
</command-bundle>
</blueprint>
diff --git a/apps/fwd/src/main/java/org/onlab/onos/fwd/ReactiveForwarding.java b/apps/fwd/src/main/java/org/onlab/onos/fwd/ReactiveForwarding.java
index aaf5350..1accddb 100644
--- a/apps/fwd/src/main/java/org/onlab/onos/fwd/ReactiveForwarding.java
+++ b/apps/fwd/src/main/java/org/onlab/onos/fwd/ReactiveForwarding.java
@@ -26,9 +26,7 @@
import org.onlab.onos.net.packet.PacketContext;
import org.onlab.onos.net.packet.PacketProcessor;
import org.onlab.onos.net.packet.PacketService;
-import org.onlab.onos.net.proxyarp.ProxyArpService;
import org.onlab.onos.net.topology.TopologyService;
-import org.onlab.packet.ARP;
import org.onlab.packet.Ethernet;
import org.slf4j.Logger;
@@ -39,6 +37,7 @@
public class ReactiveForwarding {
private static final int TIMEOUT = 10;
+ private static final int PRIORITY = 10;
private final Logger log = getLogger(getClass());
@@ -54,9 +53,6 @@
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected FlowRuleService flowRuleService;
- @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
- protected ProxyArpService proxyArpService;
-
private ReactivePacketProcessor processor = new ReactivePacketProcessor();
private ApplicationId appId;
@@ -64,7 +60,7 @@
@Activate
public void activate() {
appId = ApplicationId.getAppId();
- packetService.addProcessor(processor, PacketProcessor.ADVISOR_MAX + 1);
+ packetService.addProcessor(processor, PacketProcessor.ADVISOR_MAX + 2);
log.info("Started with Application ID {}", appId.id());
}
@@ -92,16 +88,6 @@
InboundPacket pkt = context.inPacket();
Ethernet ethPkt = pkt.parsed();
- if (ethPkt.getEtherType() == Ethernet.TYPE_ARP) {
- ARP arp = (ARP) ethPkt.getPayload();
- if (arp.getOpCode() == ARP.OP_REPLY) {
- proxyArpService.forward(ethPkt);
- } else if (arp.getOpCode() == ARP.OP_REQUEST) {
- proxyArpService.reply(ethPkt);
- }
- context.block();
- return;
- }
HostId id = HostId.hostId(ethPkt.getDestinationMAC());
@@ -180,24 +166,24 @@
// We don't yet support bufferids in the flowservice so packet out first.
packetOut(context, portNumber);
- if (context.inPacket().parsed().getEtherType() == Ethernet.TYPE_IPV4) {
- // Install the flow rule to handle this type of message from now on.
- Ethernet inPkt = context.inPacket().parsed();
- TrafficSelector.Builder builder = new DefaultTrafficSelector.Builder();
- builder.matchEthType(inPkt.getEtherType())
- .matchEthSrc(inPkt.getSourceMAC())
- .matchEthDst(inPkt.getDestinationMAC())
- .matchInport(context.inPacket().receivedFrom().port());
- TrafficTreatment.Builder treat = new DefaultTrafficTreatment.Builder();
- treat.setOutput(portNumber);
+ // Install the flow rule to handle this type of message from now on.
+ Ethernet inPkt = context.inPacket().parsed();
+ TrafficSelector.Builder builder = DefaultTrafficSelector.builder();
+ builder.matchEthType(inPkt.getEtherType())
+ .matchEthSrc(inPkt.getSourceMAC())
+ .matchEthDst(inPkt.getDestinationMAC())
+ .matchInport(context.inPacket().receivedFrom().port());
- FlowRule f = new DefaultFlowRule(context.inPacket().receivedFrom().deviceId(),
- builder.build(), treat.build(), 0, appId, TIMEOUT);
+ TrafficTreatment.Builder treat = DefaultTrafficTreatment.builder();
+ treat.setOutput(portNumber);
- flowRuleService.applyFlowRules(f);
- }
+ FlowRule f = new DefaultFlowRule(context.inPacket().receivedFrom().deviceId(),
+ builder.build(), treat.build(), PRIORITY, appId, TIMEOUT);
+
+ flowRuleService.applyFlowRules(f);
+
}
}
diff --git a/apps/ifwd/pom.xml b/apps/ifwd/pom.xml
new file mode 100644
index 0000000..bd89143
--- /dev/null
+++ b/apps/ifwd/pom.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.onlab.onos</groupId>
+ <artifactId>onos-apps</artifactId>
+ <version>1.0.0-SNAPSHOT</version>
+ <relativePath>../pom.xml</relativePath>
+ </parent>
+
+ <artifactId>onos-app-ifwd</artifactId>
+ <packaging>bundle</packaging>
+
+ <description>ONOS simple reactive forwarding app that uses intent service</description>
+
+</project>
diff --git a/apps/ifwd/src/main/java/org/onlab/onos/ifwd/IntentReactiveForwarding.java b/apps/ifwd/src/main/java/org/onlab/onos/ifwd/IntentReactiveForwarding.java
new file mode 100644
index 0000000..fded98f
--- /dev/null
+++ b/apps/ifwd/src/main/java/org/onlab/onos/ifwd/IntentReactiveForwarding.java
@@ -0,0 +1,136 @@
+package org.onlab.onos.ifwd;
+
+import static org.slf4j.LoggerFactory.getLogger;
+
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.onlab.onos.net.Host;
+import org.onlab.onos.net.HostId;
+import org.onlab.onos.net.PortNumber;
+import org.onlab.onos.net.flow.DefaultTrafficSelector;
+import org.onlab.onos.net.flow.DefaultTrafficTreatment;
+import org.onlab.onos.net.flow.TrafficSelector;
+import org.onlab.onos.net.flow.TrafficTreatment;
+import org.onlab.onos.net.host.HostService;
+import org.onlab.onos.net.intent.HostToHostIntent;
+import org.onlab.onos.net.intent.IntentId;
+import org.onlab.onos.net.intent.IntentService;
+import org.onlab.onos.net.packet.DefaultOutboundPacket;
+import org.onlab.onos.net.packet.InboundPacket;
+import org.onlab.onos.net.packet.OutboundPacket;
+import org.onlab.onos.net.packet.PacketContext;
+import org.onlab.onos.net.packet.PacketProcessor;
+import org.onlab.onos.net.packet.PacketService;
+import org.onlab.onos.net.topology.TopologyService;
+import org.onlab.packet.Ethernet;
+import org.slf4j.Logger;
+
+/**
+ * WORK-IN-PROGRESS: Sample reactive forwarding application using intent framework.
+ */
+@Component(immediate = true)
+public class IntentReactiveForwarding {
+
+ private final Logger log = getLogger(getClass());
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected TopologyService topologyService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected PacketService packetService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected IntentService intentService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected HostService hostService;
+
+ private ReactivePacketProcessor processor = new ReactivePacketProcessor();
+
+ private static long intentId = 0x123000;
+
+ @Activate
+ public void activate() {
+ packetService.addProcessor(processor, PacketProcessor.ADVISOR_MAX + 2);
+ log.info("Started");
+ }
+
+ @Deactivate
+ public void deactivate() {
+ packetService.removeProcessor(processor);
+ processor = null;
+ log.info("Stopped");
+ }
+
+ /**
+ * Packet processor responsible for forwarding packets along their paths.
+ */
+ private class ReactivePacketProcessor implements PacketProcessor {
+
+ @Override
+ public void process(PacketContext context) {
+ // Stop processing if the packet has been handled, since we
+ // can't do any more to it.
+ if (context.isHandled()) {
+ return;
+ }
+
+ InboundPacket pkt = context.inPacket();
+ Ethernet ethPkt = pkt.parsed();
+
+ HostId srcId = HostId.hostId(ethPkt.getSourceMAC());
+ HostId dstId = HostId.hostId(ethPkt.getDestinationMAC());
+
+ // Do we know who this is for? If not, flood and bail.
+ Host dst = hostService.getHost(dstId);
+ if (dst == null) {
+ flood(context);
+ return;
+ }
+
+ // Otherwise forward and be done with it.
+ setUpConnectivity(context, srcId, dstId);
+ forwardPacketToDst(context, dst);
+ }
+ }
+
+ // Floods the specified packet if permissible.
+ private void flood(PacketContext context) {
+ if (topologyService.isBroadcastPoint(topologyService.currentTopology(),
+ context.inPacket().receivedFrom())) {
+ packetOut(context, PortNumber.FLOOD);
+ } else {
+ context.block();
+ }
+ }
+
+ // Sends a packet out the specified port.
+ private void packetOut(PacketContext context, PortNumber portNumber) {
+ context.treatmentBuilder().setOutput(portNumber);
+ context.send();
+ }
+
+ private void forwardPacketToDst(PacketContext context, Host dst) {
+ TrafficTreatment treatment = DefaultTrafficTreatment.builder().setOutput(dst.location().port()).build();
+ OutboundPacket packet = new DefaultOutboundPacket(dst.location().deviceId(),
+ treatment, context.inPacket().unparsed());
+ packetService.emit(packet);
+ log.info("sending packet: {}", packet);
+ }
+
+ // Install a rule forwarding the packet to the specified port.
+ private void setUpConnectivity(PacketContext context, HostId srcId, HostId dstId) {
+ TrafficSelector selector = DefaultTrafficSelector.builder().build();
+ TrafficTreatment treatment = DefaultTrafficTreatment.builder().build();
+
+ HostToHostIntent intent =
+ new HostToHostIntent(new IntentId(intentId++), srcId, dstId,
+ selector, treatment);
+
+ intentService.submit(intent);
+ }
+
+}
diff --git a/apps/ifwd/src/main/java/org/onlab/onos/ifwd/package-info.java b/apps/ifwd/src/main/java/org/onlab/onos/ifwd/package-info.java
new file mode 100644
index 0000000..81c4a84
--- /dev/null
+++ b/apps/ifwd/src/main/java/org/onlab/onos/ifwd/package-info.java
@@ -0,0 +1,5 @@
+/**
+ * Trivial application that provides simple form of reactive forwarding
+ * using the intent service.
+ */
+package org.onlab.onos.ifwd;
diff --git a/apps/pom.xml b/apps/pom.xml
index 010c531..55a786c 100644
--- a/apps/pom.xml
+++ b/apps/pom.xml
@@ -19,8 +19,10 @@
<modules>
<module>tvue</module>
<module>fwd</module>
+ <module>ifwd</module>
<module>foo</module>
<module>mobility</module>
+ <module>proxyarp</module>
<module>config</module>
</modules>
diff --git a/apps/proxyarp/pom.xml b/apps/proxyarp/pom.xml
new file mode 100644
index 0000000..4051d19
--- /dev/null
+++ b/apps/proxyarp/pom.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.onlab.onos</groupId>
+ <artifactId>onos-apps</artifactId>
+ <version>1.0.0-SNAPSHOT</version>
+ <relativePath>../pom.xml</relativePath>
+ </parent>
+
+ <artifactId>onos-app-proxyarp</artifactId>
+ <packaging>bundle</packaging>
+
+ <description>ONOS simple proxy arp module</description>
+
+</project>
diff --git a/apps/proxyarp/src/main/java/org/onlab/onos/proxyarp/ProxyArp.java b/apps/proxyarp/src/main/java/org/onlab/onos/proxyarp/ProxyArp.java
new file mode 100644
index 0000000..a06470f
--- /dev/null
+++ b/apps/proxyarp/src/main/java/org/onlab/onos/proxyarp/ProxyArp.java
@@ -0,0 +1,70 @@
+package org.onlab.onos.proxyarp;
+
+import static org.slf4j.LoggerFactory.getLogger;
+
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.onlab.onos.ApplicationId;
+import org.onlab.onos.net.packet.PacketContext;
+import org.onlab.onos.net.packet.PacketProcessor;
+import org.onlab.onos.net.packet.PacketService;
+import org.onlab.onos.net.proxyarp.ProxyArpService;
+import org.slf4j.Logger;
+
+/**
+ * Sample reactive proxy arp application.
+ */
+@Component(immediate = true)
+public class ProxyArp {
+
+
+ private final Logger log = getLogger(getClass());
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected PacketService packetService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected ProxyArpService proxyArpService;
+
+ private ProxyArpProcessor processor = new ProxyArpProcessor();
+
+ private ApplicationId appId;
+
+ @Activate
+ public void activate() {
+ appId = ApplicationId.getAppId();
+ packetService.addProcessor(processor, PacketProcessor.ADVISOR_MAX + 1);
+ log.info("Started with Application ID {}", appId.id());
+ }
+
+ @Deactivate
+ public void deactivate() {
+ packetService.removeProcessor(processor);
+ processor = null;
+ log.info("Stopped");
+ }
+
+
+ /**
+ * Packet processor responsible for forwarding packets along their paths.
+ */
+ private class ProxyArpProcessor implements PacketProcessor {
+
+ @Override
+ public void process(PacketContext context) {
+ // Stop processing if the packet has been handled, since we
+ // can't do any more to it.
+ if (context.isHandled()) {
+ return;
+ }
+
+ //handle the arp packet.
+ proxyArpService.handleArp(context);
+ }
+ }
+}
+
+
diff --git a/apps/proxyarp/src/main/java/org/onlab/onos/proxyarp/package-info.java b/apps/proxyarp/src/main/java/org/onlab/onos/proxyarp/package-info.java
new file mode 100644
index 0000000..3f7da98
--- /dev/null
+++ b/apps/proxyarp/src/main/java/org/onlab/onos/proxyarp/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * Proxy Arp application that handles arp resolution for you.
+ */
+package org.onlab.onos.proxyarp;
diff --git a/cli/src/main/java/org/onlab/onos/cli/SummaryCommand.java b/cli/src/main/java/org/onlab/onos/cli/SummaryCommand.java
new file mode 100644
index 0000000..e843770
--- /dev/null
+++ b/cli/src/main/java/org/onlab/onos/cli/SummaryCommand.java
@@ -0,0 +1,37 @@
+package org.onlab.onos.cli;
+
+import org.apache.karaf.shell.commands.Command;
+import org.onlab.onos.CoreService;
+import org.onlab.onos.cluster.ClusterService;
+import org.onlab.onos.net.device.DeviceService;
+import org.onlab.onos.net.flow.FlowRuleService;
+import org.onlab.onos.net.host.HostService;
+import org.onlab.onos.net.intent.IntentService;
+import org.onlab.onos.net.link.LinkService;
+import org.onlab.onos.net.topology.Topology;
+import org.onlab.onos.net.topology.TopologyService;
+
+/**
+ * Provides summary of ONOS model.
+ */
+@Command(scope = "onos", name = "summary",
+ description = "Provides summary of ONOS model")
+public class SummaryCommand extends AbstractShellCommand {
+
+ @Override
+ protected void execute() {
+ TopologyService topologyService = get(TopologyService.class);
+ Topology topology = topologyService.currentTopology();
+ print("version=%s, nodes=%d, devices=%d, links=%d, hosts=%d, clusters=%s, paths=%d, flows=%d, intents=%d",
+ get(CoreService.class).version().toString(),
+ get(ClusterService.class).getNodes().size(),
+ get(DeviceService.class).getDeviceCount(),
+ get(LinkService.class).getLinkCount(),
+ get(HostService.class).getHostCount(),
+ topologyService.getClusters(topology).size(),
+ topology.pathCount(),
+ get(FlowRuleService.class).getFlowRuleCount(),
+ get(IntentService.class).getIntentCount());
+ }
+
+}
diff --git a/cli/src/main/java/org/onlab/onos/cli/net/AddHostToHostIntentCommand.java b/cli/src/main/java/org/onlab/onos/cli/net/AddHostToHostIntentCommand.java
new file mode 100644
index 0000000..837a0a7
--- /dev/null
+++ b/cli/src/main/java/org/onlab/onos/cli/net/AddHostToHostIntentCommand.java
@@ -0,0 +1,48 @@
+package org.onlab.onos.cli.net;
+
+import org.apache.karaf.shell.commands.Argument;
+import org.apache.karaf.shell.commands.Command;
+import org.onlab.onos.cli.AbstractShellCommand;
+import org.onlab.onos.net.HostId;
+import org.onlab.onos.net.flow.DefaultTrafficSelector;
+import org.onlab.onos.net.flow.DefaultTrafficTreatment;
+import org.onlab.onos.net.flow.TrafficSelector;
+import org.onlab.onos.net.flow.TrafficTreatment;
+import org.onlab.onos.net.intent.HostToHostIntent;
+import org.onlab.onos.net.intent.IntentId;
+import org.onlab.onos.net.intent.IntentService;
+
+/**
+ * Installs host-to-host connectivity intent.
+ */
+@Command(scope = "onos", name = "add-host-intent",
+ description = "Installs host-to-host connectivity intent")
+public class AddHostToHostIntentCommand extends AbstractShellCommand {
+
+ @Argument(index = 0, name = "one", description = "One host ID",
+ required = true, multiValued = false)
+ String one = null;
+
+ @Argument(index = 1, name = "two", description = "Another host ID",
+ required = true, multiValued = false)
+ String two = null;
+
+ private static long id = 0x7870001;
+
+ @Override
+ protected void execute() {
+ IntentService service = get(IntentService.class);
+
+ HostId oneId = HostId.hostId(one);
+ HostId twoId = HostId.hostId(two);
+
+ TrafficSelector selector = DefaultTrafficSelector.builder().build();
+ TrafficTreatment treatment = DefaultTrafficTreatment.builder().build();
+
+ HostToHostIntent intent =
+ new HostToHostIntent(new IntentId(id++), oneId, twoId,
+ selector, treatment);
+ service.submit(intent);
+ }
+
+}
diff --git a/cli/src/main/java/org/onlab/onos/cli/net/AddPointToPointIntentCommand.java b/cli/src/main/java/org/onlab/onos/cli/net/AddPointToPointIntentCommand.java
new file mode 100644
index 0000000..89ec7f6
--- /dev/null
+++ b/cli/src/main/java/org/onlab/onos/cli/net/AddPointToPointIntentCommand.java
@@ -0,0 +1,93 @@
+package org.onlab.onos.cli.net;
+
+import org.apache.karaf.shell.commands.Argument;
+import org.apache.karaf.shell.commands.Command;
+import org.onlab.onos.cli.AbstractShellCommand;
+import org.onlab.onos.net.ConnectPoint;
+import org.onlab.onos.net.DeviceId;
+import org.onlab.onos.net.PortNumber;
+import org.onlab.onos.net.flow.DefaultTrafficSelector;
+import org.onlab.onos.net.flow.DefaultTrafficTreatment;
+import org.onlab.onos.net.flow.TrafficSelector;
+import org.onlab.onos.net.flow.TrafficTreatment;
+import org.onlab.onos.net.intent.Intent;
+import org.onlab.onos.net.intent.IntentId;
+import org.onlab.onos.net.intent.IntentService;
+import org.onlab.onos.net.intent.PointToPointIntent;
+import org.onlab.packet.Ethernet;
+
+/**
+ * Installs point-to-point connectivity intents.
+ */
+@Command(scope = "onos", name = "add-point-intent",
+ description = "Installs point-to-point connectivity intent")
+public class AddPointToPointIntentCommand extends AbstractShellCommand {
+
+ @Argument(index = 0, name = "ingressDevice",
+ description = "Ingress Device/Port Description",
+ required = true, multiValued = false)
+ String ingressDeviceString = null;
+
+ @Argument(index = 1, name = "egressDevice",
+ description = "Egress Device/Port Description",
+ required = true, multiValued = false)
+ String egressDeviceString = null;
+
+ private static long id = 0x7470001;
+
+ @Override
+ protected void execute() {
+ IntentService service = get(IntentService.class);
+
+ DeviceId ingressDeviceId = DeviceId.deviceId(getDeviceId(ingressDeviceString));
+ PortNumber ingressPortNumber =
+ PortNumber.portNumber(getPortNumber(ingressDeviceString));
+ ConnectPoint ingress = new ConnectPoint(ingressDeviceId, ingressPortNumber);
+
+ DeviceId egressDeviceId = DeviceId.deviceId(getDeviceId(egressDeviceString));
+ PortNumber egressPortNumber =
+ PortNumber.portNumber(getPortNumber(egressDeviceString));
+ ConnectPoint egress = new ConnectPoint(egressDeviceId, egressPortNumber);
+
+ TrafficSelector selector = DefaultTrafficSelector.builder()
+ .matchEthType(Ethernet.TYPE_IPV4)
+ .build();
+ TrafficTreatment treatment = DefaultTrafficTreatment.builder().build();
+
+ Intent intent =
+ new PointToPointIntent(new IntentId(id++),
+ selector,
+ treatment,
+ ingress,
+ egress);
+ service.submit(intent);
+ }
+
+ /**
+ * Extracts the port number portion of the ConnectPoint.
+ *
+ * @param deviceString string representing the device/port
+ * @return port number as a string, empty string if the port is not found
+ */
+ private String getPortNumber(String deviceString) {
+ int slash = deviceString.indexOf('/');
+ if (slash <= 0) {
+ return "";
+ }
+ return deviceString.substring(slash + 1, deviceString.length());
+ }
+
+ /**
+ * Extracts the device ID portion of the ConnectPoint.
+ *
+ * @param deviceString string representing the device/port
+ * @return device ID string
+ */
+ private String getDeviceId(String deviceString) {
+ int slash = deviceString.indexOf('/');
+ if (slash <= 0) {
+ return "";
+ }
+ return deviceString.substring(0, slash);
+ }
+}
diff --git a/cli/src/main/java/org/onlab/onos/cli/net/ConnectPointCompleter.java b/cli/src/main/java/org/onlab/onos/cli/net/ConnectPointCompleter.java
new file mode 100644
index 0000000..29994c7
--- /dev/null
+++ b/cli/src/main/java/org/onlab/onos/cli/net/ConnectPointCompleter.java
@@ -0,0 +1,38 @@
+package org.onlab.onos.cli.net;
+
+import java.util.List;
+import java.util.SortedSet;
+
+import org.apache.karaf.shell.console.Completer;
+import org.apache.karaf.shell.console.completer.StringsCompleter;
+import org.onlab.onos.cli.AbstractShellCommand;
+import org.onlab.onos.net.Device;
+import org.onlab.onos.net.Port;
+import org.onlab.onos.net.device.DeviceService;
+
+/**
+ * ConnectPoint completer.
+ */
+public class ConnectPointCompleter implements Completer {
+ @Override
+ public int complete(String buffer, int cursor, List<String> candidates) {
+ // Delegate string completer
+ StringsCompleter delegate = new StringsCompleter();
+
+ // Fetch our service and feed it's offerings to the string completer
+ DeviceService service = AbstractShellCommand.get(DeviceService.class);
+
+ // Generate the device ID/port number identifiers
+ for (Device device : service.getDevices()) {
+ SortedSet<String> strings = delegate.getStrings();
+
+ for (Port port : service.getPorts(device.id())) {
+ strings.add(device.id().toString() + "/" + port.number());
+ }
+ }
+
+ // Now let the completer do the work for figuring out what to offer.
+ return delegate.complete(buffer, cursor, candidates);
+ }
+
+}
diff --git a/cli/src/main/java/org/onlab/onos/cli/net/DevicesListCommand.java b/cli/src/main/java/org/onlab/onos/cli/net/DevicesListCommand.java
index 6f0dd30..f34f97e 100644
--- a/cli/src/main/java/org/onlab/onos/cli/net/DevicesListCommand.java
+++ b/cli/src/main/java/org/onlab/onos/cli/net/DevicesListCommand.java
@@ -35,7 +35,7 @@
* @param service device service
* @return sorted device list
*/
- protected List<Device> getSortedDevices(DeviceService service) {
+ protected static List<Device> getSortedDevices(DeviceService service) {
List<Device> devices = newArrayList(service.getDevices());
Collections.sort(devices, Comparators.ELEMENT_COMPARATOR);
return devices;
diff --git a/cli/src/main/java/org/onlab/onos/cli/net/FlowRuleStatusCompleter.java b/cli/src/main/java/org/onlab/onos/cli/net/FlowRuleStatusCompleter.java
index 134d995..d8c795a 100644
--- a/cli/src/main/java/org/onlab/onos/cli/net/FlowRuleStatusCompleter.java
+++ b/cli/src/main/java/org/onlab/onos/cli/net/FlowRuleStatusCompleter.java
@@ -5,7 +5,7 @@
import org.apache.karaf.shell.console.Completer;
import org.apache.karaf.shell.console.completer.StringsCompleter;
-import org.onlab.onos.net.flow.FlowRule.FlowRuleState;
+import org.onlab.onos.net.flow.FlowEntry.FlowEntryState;
/**
* Device ID completer.
@@ -16,7 +16,7 @@
// Delegate string completer
StringsCompleter delegate = new StringsCompleter();
- FlowRuleState[] states = FlowRuleState.values();
+ FlowEntryState[] states = FlowEntryState.values();
SortedSet<String> strings = delegate.getStrings();
for (int i = 0; i < states.length; i++) {
strings.add(states[i].toString().toLowerCase());
diff --git a/cli/src/main/java/org/onlab/onos/cli/net/FlowsListCommand.java b/cli/src/main/java/org/onlab/onos/cli/net/FlowsListCommand.java
index 41f30a7..902b27b 100644
--- a/cli/src/main/java/org/onlab/onos/cli/net/FlowsListCommand.java
+++ b/cli/src/main/java/org/onlab/onos/cli/net/FlowsListCommand.java
@@ -1,6 +1,7 @@
package org.onlab.onos.cli.net;
import static com.google.common.collect.Lists.newArrayList;
+import static org.onlab.onos.cli.net.DevicesListCommand.getSortedDevices;
import java.util.Collections;
import java.util.List;
@@ -13,8 +14,8 @@
import org.onlab.onos.net.Device;
import org.onlab.onos.net.DeviceId;
import org.onlab.onos.net.device.DeviceService;
-import org.onlab.onos.net.flow.FlowRule;
-import org.onlab.onos.net.flow.FlowRule.FlowRuleState;
+import org.onlab.onos.net.flow.FlowEntry;
+import org.onlab.onos.net.flow.FlowEntry.FlowEntryState;
import org.onlab.onos.net.flow.FlowRuleService;
import com.google.common.collect.Maps;
@@ -45,8 +46,8 @@
protected void execute() {
DeviceService deviceService = get(DeviceService.class);
FlowRuleService service = get(FlowRuleService.class);
- Map<Device, List<FlowRule>> flows = getSortedFlows(deviceService, service);
- for (Device d : flows.keySet()) {
+ Map<Device, List<FlowEntry>> flows = getSortedFlows(deviceService, service);
+ for (Device d : getSortedDevices(deviceService)) {
printFlows(d, flows.get(d));
}
}
@@ -57,21 +58,22 @@
* @param service device service
* @return sorted device list
*/
- protected Map<Device, List<FlowRule>> getSortedFlows(DeviceService deviceService, FlowRuleService service) {
- Map<Device, List<FlowRule>> flows = Maps.newHashMap();
- List<FlowRule> rules;
- FlowRuleState s = null;
+ protected Map<Device, List<FlowEntry>> getSortedFlows(DeviceService deviceService,
+ FlowRuleService service) {
+ Map<Device, List<FlowEntry>> flows = Maps.newHashMap();
+ List<FlowEntry> rules;
+ FlowEntryState s = null;
if (state != null && !state.equals("any")) {
- s = FlowRuleState.valueOf(state.toUpperCase());
+ s = FlowEntryState.valueOf(state.toUpperCase());
}
- Iterable<Device> devices = uri == null ? deviceService.getDevices() :
+ Iterable<Device> devices = uri == null ? deviceService.getDevices() :
Collections.singletonList(deviceService.getDevice(DeviceId.deviceId(uri)));
for (Device d : devices) {
if (s == null) {
rules = newArrayList(service.getFlowEntries(d.id()));
} else {
rules = newArrayList();
- for (FlowRule f : service.getFlowEntries(d.id())) {
+ for (FlowEntry f : service.getFlowEntries(d.id())) {
if (f.state().equals(s)) {
rules.add(f);
}
@@ -88,19 +90,17 @@
* @param d the device
* @param flows the set of flows for that device.
*/
- protected void printFlows(Device d, List<FlowRule> flows) {
- print("Device: " + d.id());
- if (flows == null | flows.isEmpty()) {
- print(" %s", "No flows.");
- return;
+ protected void printFlows(Device d, List<FlowEntry> flows) {
+ boolean empty = flows == null || flows.isEmpty();
+ print("deviceId=%s, flowRuleCount=%d", d.id(), empty ? 0 : flows.size());
+ if (!empty) {
+ for (FlowEntry f : flows) {
+ print(FMT, Long.toHexString(f.id().value()), f.state(), f.bytes(),
+ f.packets(), f.life(), f.priority());
+ print(SFMT, f.selector().criteria());
+ print(TFMT, f.treatment().instructions());
+ }
}
- for (FlowRule f : flows) {
- print(FMT, Long.toHexString(f.id().value()), f.state(), f.bytes(),
- f.packets(), f.lifeMillis(), f.priority());
- print(SFMT, f.selector().criteria());
- print(TFMT, f.treatment().instructions());
- }
-
}
}
diff --git a/cli/src/main/java/org/onlab/onos/cli/net/IntentIdCompleter.java b/cli/src/main/java/org/onlab/onos/cli/net/IntentIdCompleter.java
new file mode 100644
index 0000000..5d2e952
--- /dev/null
+++ b/cli/src/main/java/org/onlab/onos/cli/net/IntentIdCompleter.java
@@ -0,0 +1,34 @@
+package org.onlab.onos.cli.net;
+
+import org.apache.karaf.shell.console.Completer;
+import org.apache.karaf.shell.console.completer.StringsCompleter;
+import org.onlab.onos.cli.AbstractShellCommand;
+import org.onlab.onos.net.intent.Intent;
+import org.onlab.onos.net.intent.IntentService;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.SortedSet;
+
+/**
+ * Intent ID completer.
+ */
+public class IntentIdCompleter implements Completer {
+ @Override
+ public int complete(String buffer, int cursor, List<String> candidates) {
+ // Delegate string completer
+ StringsCompleter delegate = new StringsCompleter();
+
+ // Fetch our service and feed it's offerings to the string completer
+ IntentService service = AbstractShellCommand.get(IntentService.class);
+ Iterator<Intent> it = service.getIntents().iterator();
+ SortedSet<String> strings = delegate.getStrings();
+ while (it.hasNext()) {
+ strings.add(it.next().id().toString());
+ }
+
+ // Now let the completer do the work for figuring out what to offer.
+ return delegate.complete(buffer, cursor, candidates);
+ }
+
+}
diff --git a/cli/src/main/java/org/onlab/onos/cli/net/IntentRemoveCommand.java b/cli/src/main/java/org/onlab/onos/cli/net/IntentRemoveCommand.java
new file mode 100644
index 0000000..7684da4
--- /dev/null
+++ b/cli/src/main/java/org/onlab/onos/cli/net/IntentRemoveCommand.java
@@ -0,0 +1,37 @@
+package org.onlab.onos.cli.net;
+
+import org.apache.karaf.shell.commands.Argument;
+import org.apache.karaf.shell.commands.Command;
+import org.onlab.onos.cli.AbstractShellCommand;
+import org.onlab.onos.net.intent.Intent;
+import org.onlab.onos.net.intent.IntentId;
+import org.onlab.onos.net.intent.IntentService;
+
+/**
+ * Removes host-to-host connectivity intent.
+ */
+@Command(scope = "onos", name = "remove-intent",
+ description = "Removes the specified intent")
+public class IntentRemoveCommand extends AbstractShellCommand {
+
+ @Argument(index = 0, name = "id", description = "Intent ID",
+ required = true, multiValued = false)
+ String id = null;
+
+ @Override
+ protected void execute() {
+ IntentService service = get(IntentService.class);
+
+ int radix = id.startsWith("0x") ? 16 : 10;
+ if (radix == 16) {
+ id = id.replaceFirst("0x", "");
+ }
+ IntentId intentId = new IntentId(Long.parseLong(id, radix));
+
+
+ Intent intent = service.getIntent(intentId);
+ if (intent != null) {
+ service.withdraw(intent);
+ }
+ }
+}
diff --git a/cli/src/main/java/org/onlab/onos/cli/net/IntentsListCommand.java b/cli/src/main/java/org/onlab/onos/cli/net/IntentsListCommand.java
new file mode 100644
index 0000000..a0c9845
--- /dev/null
+++ b/cli/src/main/java/org/onlab/onos/cli/net/IntentsListCommand.java
@@ -0,0 +1,25 @@
+package org.onlab.onos.cli.net;
+
+import org.apache.karaf.shell.commands.Command;
+import org.onlab.onos.cli.AbstractShellCommand;
+import org.onlab.onos.net.intent.Intent;
+import org.onlab.onos.net.intent.IntentService;
+import org.onlab.onos.net.intent.IntentState;
+
+/**
+ * Lists the inventory of intents and their states.
+ */
+@Command(scope = "onos", name = "intents",
+ description = "Lists the inventory of intents and their states")
+public class IntentsListCommand extends AbstractShellCommand {
+
+ @Override
+ protected void execute() {
+ IntentService service = get(IntentService.class);
+ for (Intent intent : service.getIntents()) {
+ IntentState state = service.getIntentState(intent.id());
+ print("%s %s %s", intent.id(), state, intent);
+ }
+ }
+
+}
diff --git a/cli/src/main/java/org/onlab/onos/cli/net/WipeOutCommand.java b/cli/src/main/java/org/onlab/onos/cli/net/WipeOutCommand.java
index d0e6a70..fe18ba0 100644
--- a/cli/src/main/java/org/onlab/onos/cli/net/WipeOutCommand.java
+++ b/cli/src/main/java/org/onlab/onos/cli/net/WipeOutCommand.java
@@ -1,5 +1,6 @@
package org.onlab.onos.cli.net;
+import org.apache.karaf.shell.commands.Argument;
import org.apache.karaf.shell.commands.Command;
import org.onlab.onos.net.Device;
import org.onlab.onos.net.Host;
@@ -7,28 +8,51 @@
import org.onlab.onos.net.device.DeviceService;
import org.onlab.onos.net.host.HostAdminService;
import org.onlab.onos.net.host.HostService;
+import org.onlab.onos.net.intent.Intent;
+import org.onlab.onos.net.intent.IntentService;
+import org.onlab.onos.net.intent.IntentState;
/**
- * Wipes-out the entire network information base, i.e. devices, links, hosts.
+ * Wipes-out the entire network information base, i.e. devices, links, hosts, intents.
*/
@Command(scope = "onos", name = "wipe-out",
description = "Wipes-out the entire network information base, i.e. devices, links, hosts")
public class WipeOutCommand extends ClustersListCommand {
+ private static final String DISCLAIMER = "Delete everything please.";
+
+ @Argument(index = 0, name = "disclaimer", description = "Device ID",
+ required = false, multiValued = false)
+ String disclaimer = null;
+
@Override
protected void execute() {
+ if (disclaimer == null || !disclaimer.equals(DISCLAIMER)) {
+ print("I'm afraid I can't do that!\nPlease acknowledge with phrase: '%s'",
+ DISCLAIMER);
+ return;
+ }
+
+ print("Wiping devices");
DeviceAdminService deviceAdminService = get(DeviceAdminService.class);
DeviceService deviceService = get(DeviceService.class);
for (Device device : deviceService.getDevices()) {
deviceAdminService.removeDevice(device.id());
}
+ print("Wiping hosts");
HostAdminService hostAdminService = get(HostAdminService.class);
HostService hostService = get(HostService.class);
for (Host host : hostService.getHosts()) {
hostAdminService.removeHost(host.id());
}
+
+ print("Wiping intents");
+ IntentService intentService = get(IntentService.class);
+ for (Intent intent : intentService.getIntents()) {
+ if (intentService.getIntentState(intent.id()) == IntentState.INSTALLED) {
+ intentService.withdraw(intent);
+ }
+ }
}
-
-
}
diff --git a/cli/src/main/javadoc/doc-files/intent-states.png b/cli/src/main/javadoc/doc-files/intent-states.png
new file mode 100644
index 0000000..70c333b
--- /dev/null
+++ b/cli/src/main/javadoc/doc-files/intent-states.png
Binary files differ
diff --git a/cli/src/main/resources/OSGI-INF/blueprint/shell-config.xml b/cli/src/main/resources/OSGI-INF/blueprint/shell-config.xml
index 16b5672..6120d30 100644
--- a/cli/src/main/resources/OSGI-INF/blueprint/shell-config.xml
+++ b/cli/src/main/resources/OSGI-INF/blueprint/shell-config.xml
@@ -2,6 +2,9 @@
<command-bundle xmlns="http://karaf.apache.org/xmlns/shell/v1.1.0">
<command>
+ <action class="org.onlab.onos.cli.SummaryCommand"/>
+ </command>
+ <command>
<action class="org.onlab.onos.cli.NodesListCommand"/>
</command>
<command>
@@ -56,6 +59,30 @@
<ref component-id="deviceIdCompleter"/>
</completers>
</command>
+
+ <command>
+ <action class="org.onlab.onos.cli.net.IntentsListCommand"/>
+ </command>
+ <command>
+ <action class="org.onlab.onos.cli.net.IntentRemoveCommand"/>
+ <completers>
+ <ref component-id="intentIdCompleter"/>
+ </completers>
+ </command>
+ <command>
+ <action class="org.onlab.onos.cli.net.AddHostToHostIntentCommand"/>
+ <completers>
+ <ref component-id="hostIdCompleter"/>
+ </completers>
+ </command>
+ <command>
+ <action class="org.onlab.onos.cli.net.AddPointToPointIntentCommand"/>
+ <completers>
+ <ref component-id="connectPointCompleter"/>
+ <ref component-id="connectPointCompleter"/>
+ </completers>
+ </command>
+
<command>
<action class="org.onlab.onos.cli.net.ClustersListCommand"/>
</command>
@@ -94,6 +121,8 @@
<bean id="clusterIdCompleter" class="org.onlab.onos.cli.net.ClusterIdCompleter"/>
<bean id="roleCompleter" class="org.onlab.onos.cli.net.RoleCompleter"/>
<bean id="hostIdCompleter" class="org.onlab.onos.cli.net.HostIdCompleter"/>
+ <bean id="intentIdCompleter" class="org.onlab.onos.cli.net.IntentIdCompleter"/>
<bean id="flowRuleStatusCompleter" class="org.onlab.onos.cli.net.FlowRuleStatusCompleter"/>
+ <bean id="connectPointCompleter" class="org.onlab.onos.cli.net.ConnectPointCompleter"/>
</blueprint>
diff --git a/core/api/src/main/java/org/onlab/onos/ApplicationId.java b/core/api/src/main/java/org/onlab/onos/ApplicationId.java
index f345607..433265e 100644
--- a/core/api/src/main/java/org/onlab/onos/ApplicationId.java
+++ b/core/api/src/main/java/org/onlab/onos/ApplicationId.java
@@ -8,7 +8,7 @@
*/
public final class ApplicationId {
- private static AtomicInteger idDispenser;
+ private static final AtomicInteger ID_DISPENCER = new AtomicInteger(1);
private final Integer id;
// Ban public construction
@@ -50,10 +50,7 @@
* @return app id
*/
public static ApplicationId getAppId() {
- if (ApplicationId.idDispenser == null) {
- ApplicationId.idDispenser = new AtomicInteger(1);
- }
- return new ApplicationId(ApplicationId.idDispenser.getAndIncrement());
+ return new ApplicationId(ApplicationId.ID_DISPENCER.getAndIncrement());
}
}
diff --git a/core/api/src/main/java/org/onlab/onos/CoreService.java b/core/api/src/main/java/org/onlab/onos/CoreService.java
new file mode 100644
index 0000000..32c36c5
--- /dev/null
+++ b/core/api/src/main/java/org/onlab/onos/CoreService.java
@@ -0,0 +1,15 @@
+package org.onlab.onos;
+
+/**
+ * Service for interacting with the core system of the controller.
+ */
+public interface CoreService {
+
+ /**
+ * Returns the product version.
+ *
+ * @return product version
+ */
+ Version version();
+
+}
diff --git a/core/api/src/main/java/org/onlab/onos/Version.java b/core/api/src/main/java/org/onlab/onos/Version.java
new file mode 100644
index 0000000..5d071b7
--- /dev/null
+++ b/core/api/src/main/java/org/onlab/onos/Version.java
@@ -0,0 +1,113 @@
+package org.onlab.onos;
+
+import java.util.Objects;
+
+import static java.lang.Integer.parseInt;
+
+/**
+ * Representation of the product version.
+ */
+public final class Version {
+
+ public static final String FORMAT = "%d.%d.%d.%s";
+
+ private final int major;
+ private final int minor;
+ private final int patch;
+ private final String build;
+
+ private final String format;
+
+ // Creates a new version descriptor
+ private Version(int major, int minor, int patch, String build) {
+ this.major = major;
+ this.minor = minor;
+ this.patch = patch;
+ this.build = build;
+ this.format = String.format(FORMAT, major, minor, patch, build);
+ }
+
+
+ /**
+ * Creates a new version from the specified constituent numbers.
+ *
+ * @param major major version number
+ * @param minor minod version number
+ * @param patch version patch number
+ * @param build build string
+ * @return version descriptor
+ */
+ public static Version version(int major, int minor, int patch, String build) {
+ return new Version(major, minor, patch, build);
+ }
+
+ /**
+ * Creates a new version by parsing the specified string.
+ *
+ * @param string version string
+ * @return version descriptor
+ */
+ public static Version version(String string) {
+ String[] fields = string.split("[.-]");
+ return new Version(parseInt(fields[0]), parseInt(fields[1]),
+ parseInt(fields[2]), fields[3]);
+ }
+
+ /**
+ * Returns the major version number.
+ *
+ * @return major version number
+ */
+ public int major() {
+ return major;
+ }
+
+ /**
+ * Returns the minor version number.
+ *
+ * @return minor version number
+ */
+ public int minor() {
+ return minor;
+ }
+
+ /**
+ * Returns the version patch number.
+ *
+ * @return patch number
+ */
+ public int patch() {
+ return patch;
+ }
+
+ /**
+ * Returns the version build string.
+ *
+ * @return build string
+ */
+ public String build() {
+ return build;
+ }
+
+ @Override
+ public String toString() {
+ return format;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(format);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj instanceof Version) {
+ final Version other = (Version) obj;
+ return Objects.equals(this.format, other.format);
+ }
+ return false;
+ }
+}
diff --git a/core/api/src/main/java/org/onlab/onos/cluster/MastershipService.java b/core/api/src/main/java/org/onlab/onos/cluster/MastershipService.java
index ffee162..51b6f6a 100644
--- a/core/api/src/main/java/org/onlab/onos/cluster/MastershipService.java
+++ b/core/api/src/main/java/org/onlab/onos/cluster/MastershipService.java
@@ -17,6 +17,7 @@
* Returns the role of the local node for the specified device, without
* triggering master selection.
*
+ * @param deviceId the the identifier of the device
* @return role of the current node
*/
MastershipRole getLocalRole(DeviceId deviceId);
diff --git a/core/api/src/main/java/org/onlab/onos/net/AnnotationsUtil.java b/core/api/src/main/java/org/onlab/onos/net/AnnotationsUtil.java
new file mode 100644
index 0000000..3b71d95
--- /dev/null
+++ b/core/api/src/main/java/org/onlab/onos/net/AnnotationsUtil.java
@@ -0,0 +1,27 @@
+package org.onlab.onos.net;
+
+public final class AnnotationsUtil {
+
+ public static boolean isEqual(Annotations lhs, Annotations rhs) {
+ if (lhs == rhs) {
+ return true;
+ }
+ if (lhs == null || rhs == null) {
+ return false;
+ }
+
+ if (!lhs.keys().equals(rhs.keys())) {
+ return false;
+ }
+
+ for (String key : lhs.keys()) {
+ if (!lhs.value(key).equals(rhs.value(key))) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ // not to be instantiated
+ private AnnotationsUtil() {}
+}
diff --git a/core/api/src/main/java/org/onlab/onos/net/ConnectPoint.java b/core/api/src/main/java/org/onlab/onos/net/ConnectPoint.java
index 35adde9..17f9348 100644
--- a/core/api/src/main/java/org/onlab/onos/net/ConnectPoint.java
+++ b/core/api/src/main/java/org/onlab/onos/net/ConnectPoint.java
@@ -47,7 +47,23 @@
return (DeviceId) elementId;
}
throw new IllegalStateException("Connection point not associated " +
- "with an infrastructure device");
+ "with an infrastructure device");
+ }
+
+ /**
+ * Returns the identifier of the infrastructure device if the connection
+ * point belongs to a network element which is indeed an end-station host.
+ *
+ * @return network element identifier as a host identifier
+ * @throws java.lang.IllegalStateException if connection point is not
+ * associated with a host
+ */
+ public HostId hostId() {
+ if (elementId instanceof HostId) {
+ return (HostId) elementId;
+ }
+ throw new IllegalStateException("Connection point not associated " +
+ "with an end-station host");
}
/**
diff --git a/core/api/src/main/java/org/onlab/onos/net/DefaultAnnotations.java b/core/api/src/main/java/org/onlab/onos/net/DefaultAnnotations.java
index 0c0f375..b2e7e61 100644
--- a/core/api/src/main/java/org/onlab/onos/net/DefaultAnnotations.java
+++ b/core/api/src/main/java/org/onlab/onos/net/DefaultAnnotations.java
@@ -73,31 +73,63 @@
}
/**
- * Convert Annotations to DefaultAnnotations if needed and merges.
+ * Creates the union of two given SparseAnnotations.
+ * Unlike the {@link #merge(DefaultAnnotations, SparseAnnotations)} method,
+ * result will be {@link SparseAnnotations} instead of {@link Annotations}.
*
- * @see #merge(DefaultAnnotations, SparseAnnotations)
+ * A key tagged for removal will remain in the output SparseAnnotations,
+ * if the counterpart of the input does not contain the same key.
*
* @param annotations base annotations
* @param sparseAnnotations additional sparse annotations
* @return combined annotations or the original base annotations if there
* are not additional annotations
*/
- public static DefaultAnnotations merge(Annotations annotations,
- SparseAnnotations sparseAnnotations) {
- if (annotations instanceof DefaultAnnotations) {
- return merge((DefaultAnnotations) annotations, sparseAnnotations);
+ public static SparseAnnotations union(SparseAnnotations annotations,
+ SparseAnnotations sparseAnnotations) {
+
+ if (sparseAnnotations == null || sparseAnnotations.keys().isEmpty()) {
+ return annotations;
}
- DefaultAnnotations.Builder builder = DefaultAnnotations.builder();
- for (String key : annotations.keys()) {
- builder.set(key, annotations.value(key));
+ final HashMap<String, String> newMap;
+ if (annotations instanceof DefaultAnnotations) {
+ newMap = copy(((DefaultAnnotations) annotations).map);
+ } else {
+ newMap = new HashMap<>(annotations.keys().size() +
+ sparseAnnotations.keys().size());
+ putAllSparseAnnotations(newMap, annotations);
}
- return merge(builder.build(), sparseAnnotations);
+
+ putAllSparseAnnotations(newMap, sparseAnnotations);
+ return new DefaultAnnotations(newMap);
+ }
+
+ // adds the key-values contained in sparseAnnotations to
+ // newMap, if sparseAnnotations had a key tagged for removal,
+ // and corresponding key exist in newMap, entry will be removed.
+ // if corresponding key does not exist, removal tag will be added to
+ // the newMap.
+ private static void putAllSparseAnnotations(
+ final HashMap<String, String> newMap,
+ SparseAnnotations sparseAnnotations) {
+
+ for (String key : sparseAnnotations.keys()) {
+ if (sparseAnnotations.isRemoved(key)) {
+ if (newMap.containsKey(key)) {
+ newMap.remove(key);
+ } else {
+ newMap.put(key, Builder.REMOVED);
+ }
+ } else {
+ String value = sparseAnnotations.value(key);
+ newMap.put(key, value);
+ }
+ }
}
@Override
public Set<String> keys() {
- // TODO: unmodifiable to be removed after switching to ImmutableMap;
return Collections.unmodifiableSet(map.keySet());
}
@@ -115,7 +147,7 @@
@SuppressWarnings("unchecked")
private static HashMap<String, String> copy(Map<String, String> original) {
if (original instanceof HashMap) {
- return (HashMap) ((HashMap) original).clone();
+ return (HashMap<String, String>) ((HashMap<?, ?>) original).clone();
}
throw new IllegalArgumentException("Expecting HashMap instance");
}
diff --git a/core/api/src/main/java/org/onlab/onos/net/DefaultEdgeLink.java b/core/api/src/main/java/org/onlab/onos/net/DefaultEdgeLink.java
index 6074c67..46a582a 100644
--- a/core/api/src/main/java/org/onlab/onos/net/DefaultEdgeLink.java
+++ b/core/api/src/main/java/org/onlab/onos/net/DefaultEdgeLink.java
@@ -3,6 +3,7 @@
import org.onlab.onos.net.provider.ProviderId;
import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
/**
* Default edge link model implementation.
@@ -18,9 +19,9 @@
* @param providerId provider identity
* @param hostPoint host-side connection point
* @param hostLocation location where host attaches to the network
- * @param isIngress true to indicated host-to-network direction; false
+ * @param isIngress true to indicate host-to-network direction; false
* for network-to-host direction
- * @param annotations optional key/value annotations
+ * @param annotations optional key/value annotations
*/
public DefaultEdgeLink(ProviderId providerId, ConnectPoint hostPoint,
HostLocation hostLocation, boolean isIngress,
@@ -42,4 +43,24 @@
public HostLocation hostLocation() {
return hostLocation;
}
+
+ /**
+ * Creates a phantom edge link, to an unspecified end-station. This link
+ * does not represent any actually discovered link stored in the system.
+ *
+ * @param edgePort network edge port
+ * @param isIngress true to indicate host-to-network direction; false
+ * for network-to-host direction
+ * @return new phantom edge link
+ */
+ public static DefaultEdgeLink createEdgeLink(ConnectPoint edgePort,
+ boolean isIngress) {
+ checkNotNull(edgePort, "Edge port cannot be null");
+ HostLocation location = (edgePort instanceof HostLocation) ?
+ (HostLocation) edgePort : new HostLocation(edgePort, 0);
+ return new DefaultEdgeLink(ProviderId.NONE,
+ new ConnectPoint(HostId.NONE, PortNumber.P0),
+ location, isIngress);
+ }
+
}
diff --git a/core/api/src/main/java/org/onlab/onos/net/HostId.java b/core/api/src/main/java/org/onlab/onos/net/HostId.java
index 1768f24..f2c0303 100644
--- a/core/api/src/main/java/org/onlab/onos/net/HostId.java
+++ b/core/api/src/main/java/org/onlab/onos/net/HostId.java
@@ -10,6 +10,14 @@
*/
public final class HostId extends ElementId {
+ private static final String NIC = "nic";
+
+ /**
+ * Represents either no host, or an unspecified host; used for creating
+ * open ingress/egress edge links.
+ */
+ public static final HostId NONE = hostId(NIC + ":none-0");
+
// Public construction is prohibited
private HostId(URI uri) {
super(uri);
@@ -43,8 +51,7 @@
* @return host identifier
*/
public static HostId hostId(MacAddress mac, VlanId vlanId) {
- // FIXME: use more efficient means of encoding
- return hostId("nic" + ":" + mac + "-" + vlanId);
+ return hostId(NIC + ":" + mac + "-" + vlanId);
}
/**
diff --git a/core/api/src/main/java/org/onlab/onos/net/HostLocation.java b/core/api/src/main/java/org/onlab/onos/net/HostLocation.java
index 76e2312..60c5945 100644
--- a/core/api/src/main/java/org/onlab/onos/net/HostLocation.java
+++ b/core/api/src/main/java/org/onlab/onos/net/HostLocation.java
@@ -22,6 +22,17 @@
}
/**
+ * Creates a new host location derived from the supplied connection point.
+ *
+ * @param connectPoint connection point
+ * @param time time when detected, in millis since start of epoch
+ */
+ public HostLocation(ConnectPoint connectPoint, long time) {
+ super(connectPoint.deviceId(), connectPoint.port());
+ this.time = time;
+ }
+
+ /**
* Returns the time when the location was established, given in
* milliseconds since start of epoch.
*
diff --git a/core/api/src/main/java/org/onlab/onos/net/LinkKey.java b/core/api/src/main/java/org/onlab/onos/net/LinkKey.java
index dee4e88..d3ff0f4 100644
--- a/core/api/src/main/java/org/onlab/onos/net/LinkKey.java
+++ b/core/api/src/main/java/org/onlab/onos/net/LinkKey.java
@@ -6,6 +6,7 @@
// TODO Consider renaming.
// it's an identifier for a Link, but it's not ElementId, so not using LinkId.
+
/**
* Immutable representation of a link identity.
*/
@@ -43,6 +44,15 @@
this.dst = dst;
}
+ /**
+ * Creates a link identifier for the specified link.
+ *
+ * @param link link descriptor
+ */
+ public LinkKey(Link link) {
+ this(link.src(), link.dst());
+ }
+
@Override
public int hashCode() {
return Objects.hash(src(), dst);
diff --git a/core/api/src/main/java/org/onlab/onos/net/PortNumber.java b/core/api/src/main/java/org/onlab/onos/net/PortNumber.java
index cfb11d5..60c3305 100644
--- a/core/api/src/main/java/org/onlab/onos/net/PortNumber.java
+++ b/core/api/src/main/java/org/onlab/onos/net/PortNumber.java
@@ -9,6 +9,8 @@
*/
public final class PortNumber {
+ public static final PortNumber P0 = portNumber(0);
+
// TODO: revisit the max and the logical port value assignments
private static final long MAX_NUMBER = (2L * Integer.MAX_VALUE) + 1;
diff --git a/core/api/src/main/java/org/onlab/onos/net/device/DefaultDeviceDescription.java b/core/api/src/main/java/org/onlab/onos/net/device/DefaultDeviceDescription.java
index 788d23a..ede2eb2 100644
--- a/core/api/src/main/java/org/onlab/onos/net/device/DefaultDeviceDescription.java
+++ b/core/api/src/main/java/org/onlab/onos/net/device/DefaultDeviceDescription.java
@@ -96,4 +96,13 @@
.toString();
}
+ // default constructor for serialization
+ private DefaultDeviceDescription() {
+ this.uri = null;
+ this.type = null;
+ this.manufacturer = null;
+ this.hwVersion = null;
+ this.swVersion = null;
+ this.serialNumber = null;
+ }
}
diff --git a/core/api/src/main/java/org/onlab/onos/net/device/DefaultPortDescription.java b/core/api/src/main/java/org/onlab/onos/net/device/DefaultPortDescription.java
index eb75ede..e1dcf9e 100644
--- a/core/api/src/main/java/org/onlab/onos/net/device/DefaultPortDescription.java
+++ b/core/api/src/main/java/org/onlab/onos/net/device/DefaultPortDescription.java
@@ -48,4 +48,9 @@
return isEnabled;
}
+ // default constructor for serialization
+ private DefaultPortDescription() {
+ this.number = null;
+ this.isEnabled = false;
+ }
}
diff --git a/core/api/src/main/java/org/onlab/onos/net/flow/CompletedBatchOperation.java b/core/api/src/main/java/org/onlab/onos/net/flow/CompletedBatchOperation.java
new file mode 100644
index 0000000..bde752e
--- /dev/null
+++ b/core/api/src/main/java/org/onlab/onos/net/flow/CompletedBatchOperation.java
@@ -0,0 +1,6 @@
+package org.onlab.onos.net.flow;
+
+public class CompletedBatchOperation {
+
+
+}
diff --git a/core/api/src/main/java/org/onlab/onos/net/flow/DefaultFlowEntry.java b/core/api/src/main/java/org/onlab/onos/net/flow/DefaultFlowEntry.java
new file mode 100644
index 0000000..5a0f55b
--- /dev/null
+++ b/core/api/src/main/java/org/onlab/onos/net/flow/DefaultFlowEntry.java
@@ -0,0 +1,111 @@
+package org.onlab.onos.net.flow;
+
+import static com.google.common.base.MoreObjects.toStringHelper;
+import static org.slf4j.LoggerFactory.getLogger;
+
+import org.onlab.onos.net.DeviceId;
+import org.slf4j.Logger;
+
+public class DefaultFlowEntry extends DefaultFlowRule implements FlowEntry {
+
+ private final Logger log = getLogger(getClass());
+
+ private long life;
+ private long packets;
+ private long bytes;
+ private FlowEntryState state;
+
+ private long lastSeen = -1;
+
+
+ public DefaultFlowEntry(DeviceId deviceId, TrafficSelector selector,
+ TrafficTreatment treatment, int priority, FlowEntryState state,
+ long life, long packets, long bytes, long flowId,
+ int timeout) {
+ super(deviceId, selector, treatment, priority, flowId, timeout);
+ this.state = state;
+ this.life = life;
+ this.packets = packets;
+ this.bytes = bytes;
+ this.lastSeen = System.currentTimeMillis();
+ }
+
+ public DefaultFlowEntry(FlowRule rule, FlowEntryState state,
+ long life, long packets, long bytes) {
+ super(rule);
+ this.state = state;
+ this.life = life;
+ this.packets = packets;
+ this.bytes = bytes;
+ this.lastSeen = System.currentTimeMillis();
+ }
+
+ public DefaultFlowEntry(FlowRule rule) {
+ super(rule);
+ this.state = FlowEntryState.PENDING_ADD;
+ this.life = 0;
+ this.packets = 0;
+ this.bytes = 0;
+ this.lastSeen = System.currentTimeMillis();
+ }
+
+ @Override
+ public long life() {
+ return life;
+ }
+
+ @Override
+ public long packets() {
+ return packets;
+ }
+
+ @Override
+ public long bytes() {
+ return bytes;
+ }
+
+ @Override
+ public FlowEntryState state() {
+ return this.state;
+ }
+
+ @Override
+ public long lastSeen() {
+ return lastSeen;
+ }
+
+ @Override
+ public void setLastSeen() {
+ this.lastSeen = System.currentTimeMillis();
+ }
+
+ @Override
+ public void setState(FlowEntryState newState) {
+ this.state = newState;
+ }
+
+ @Override
+ public void setLife(long life) {
+ this.life = life;
+ }
+
+ @Override
+ public void setPackets(long packets) {
+ this.packets = packets;
+ }
+
+ @Override
+ public void setBytes(long bytes) {
+ this.bytes = bytes;
+ }
+
+ @Override
+ public String toString() {
+ return toStringHelper(this)
+ .add("rule", super.toString())
+ .add("state", state)
+ .toString();
+ }
+
+
+}
diff --git a/core/api/src/main/java/org/onlab/onos/net/flow/DefaultFlowRule.java b/core/api/src/main/java/org/onlab/onos/net/flow/DefaultFlowRule.java
index bb4805b..47e9fed 100644
--- a/core/api/src/main/java/org/onlab/onos/net/flow/DefaultFlowRule.java
+++ b/core/api/src/main/java/org/onlab/onos/net/flow/DefaultFlowRule.java
@@ -18,10 +18,6 @@
private final TrafficSelector selector;
private final TrafficTreatment treatment;
private final long created;
- private final long life;
- private final long packets;
- private final long bytes;
- private final FlowRuleState state;
private final FlowId id;
@@ -29,73 +25,50 @@
private final int timeout;
+
public DefaultFlowRule(DeviceId deviceId, TrafficSelector selector,
- TrafficTreatment treatment, int priority, FlowRuleState state,
- long life, long packets, long bytes, long flowId, boolean expired,
+ TrafficTreatment treatment, int priority, long flowId,
int timeout) {
this.deviceId = deviceId;
this.priority = priority;
this.selector = selector;
this.treatment = treatment;
- this.state = state;
+ this.timeout = timeout;
+ this.created = System.currentTimeMillis();
+
this.appId = ApplicationId.valueOf((int) (flowId >> 32));
this.id = FlowId.valueOf(flowId);
- this.life = life;
- this.packets = packets;
- this.bytes = bytes;
- this.created = System.currentTimeMillis();
- this.timeout = timeout;
}
public DefaultFlowRule(DeviceId deviceId, TrafficSelector selector,
TrafficTreatment treatement, int priority, ApplicationId appId,
int timeout) {
- this(deviceId, selector, treatement, priority,
- FlowRuleState.CREATED, appId, timeout);
- }
- public DefaultFlowRule(FlowRule rule, FlowRuleState state) {
- this(rule.deviceId(), rule.selector(), rule.treatment(),
- rule.priority(), state, rule.id(), rule.appId(),
- rule.timeout());
- }
+ if (priority < FlowRule.MIN_PRIORITY) {
+ throw new IllegalArgumentException("Priority cannot be less than " + MIN_PRIORITY);
+ }
- private DefaultFlowRule(DeviceId deviceId,
- TrafficSelector selector, TrafficTreatment treatment,
- int priority, FlowRuleState state, ApplicationId appId,
- int timeout) {
this.deviceId = deviceId;
this.priority = priority;
this.selector = selector;
- this.treatment = treatment;
- this.state = state;
- this.life = 0;
- this.packets = 0;
- this.bytes = 0;
+ this.treatment = treatement;
this.appId = appId;
-
this.timeout = timeout;
+ this.created = System.currentTimeMillis();
this.id = FlowId.valueOf((((long) appId().id()) << 32) | (this.hash() & 0xffffffffL));
- this.created = System.currentTimeMillis();
}
- private DefaultFlowRule(DeviceId deviceId,
- TrafficSelector selector, TrafficTreatment treatment,
- int priority, FlowRuleState state, FlowId flowId, ApplicationId appId,
- int timeout) {
- this.deviceId = deviceId;
- this.priority = priority;
- this.selector = selector;
- this.treatment = treatment;
- this.state = state;
- this.life = 0;
- this.packets = 0;
- this.bytes = 0;
- this.appId = appId;
- this.id = flowId;
- this.timeout = timeout;
+ public DefaultFlowRule(FlowRule rule) {
+ this.deviceId = rule.deviceId();
+ this.priority = rule.priority();
+ this.selector = rule.selector();
+ this.treatment = rule.treatment();
+ this.appId = rule.appId();
+ this.id = rule.id();
+ this.timeout = rule.timeout();
this.created = System.currentTimeMillis();
+
}
@@ -129,26 +102,6 @@
return treatment;
}
- @Override
- public long lifeMillis() {
- return life;
- }
-
- @Override
- public long packets() {
- return packets;
- }
-
- @Override
- public long bytes() {
- return bytes;
- }
-
- @Override
- public FlowRuleState state() {
- return this.state;
- }
-
@Override
/*
@@ -162,7 +115,7 @@
}
public int hash() {
- return Objects.hash(deviceId, selector, id);
+ return Objects.hash(deviceId, selector, treatment);
}
@Override
@@ -179,7 +132,7 @@
if (obj instanceof DefaultFlowRule) {
DefaultFlowRule that = (DefaultFlowRule) obj;
return Objects.equals(deviceId, that.deviceId) &&
- //Objects.equals(id, that.id) &&
+ Objects.equals(id, that.id) &&
Objects.equals(priority, that.priority) &&
Objects.equals(selector, that.selector);
@@ -190,19 +143,18 @@
@Override
public String toString() {
return toStringHelper(this)
- .add("id", id)
+ .add("id", Long.toHexString(id.value()))
.add("deviceId", deviceId)
.add("priority", priority)
.add("selector", selector.criteria())
.add("treatment", treatment == null ? "N/A" : treatment.instructions())
.add("created", created)
- .add("state", state)
.toString();
}
@Override
public int timeout() {
- return timeout > MAX_TIMEOUT ? MAX_TIMEOUT : this.timeout;
+ return timeout;
}
}
diff --git a/core/api/src/main/java/org/onlab/onos/net/flow/DefaultTrafficSelector.java b/core/api/src/main/java/org/onlab/onos/net/flow/DefaultTrafficSelector.java
index d792c7e..31c53a8 100644
--- a/core/api/src/main/java/org/onlab/onos/net/flow/DefaultTrafficSelector.java
+++ b/core/api/src/main/java/org/onlab/onos/net/flow/DefaultTrafficSelector.java
@@ -1,36 +1,43 @@
package org.onlab.onos.net.flow;
-import static org.slf4j.LoggerFactory.getLogger;
-
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Objects;
-import java.util.Set;
-
+import com.google.common.collect.ImmutableSet;
import org.onlab.onos.net.PortNumber;
import org.onlab.onos.net.flow.criteria.Criteria;
import org.onlab.onos.net.flow.criteria.Criterion;
import org.onlab.packet.IpPrefix;
import org.onlab.packet.MacAddress;
import org.onlab.packet.VlanId;
-import org.slf4j.Logger;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * Default traffic selector implementation.
+ */
public final class DefaultTrafficSelector implements TrafficSelector {
- private final Set<Criterion> selector;
+ private final Set<Criterion> criteria;
- private DefaultTrafficSelector(Set<Criterion> selector) {
- this.selector = Collections.unmodifiableSet(selector);
+ /**
+ * Creates a new traffic selector with the specified criteria.
+ *
+ * @param criteria criteria
+ */
+ private DefaultTrafficSelector(Set<Criterion> criteria) {
+ this.criteria = Collections.unmodifiableSet(criteria);
}
@Override
public Set<Criterion> criteria() {
- return selector;
+ return criteria;
}
@Override
public int hashCode() {
- return Objects.hash(selector);
+ return Objects.hash(criteria);
}
@Override
@@ -40,23 +47,50 @@
}
if (obj instanceof DefaultTrafficSelector) {
DefaultTrafficSelector that = (DefaultTrafficSelector) obj;
- return Objects.equals(selector, that.selector);
+ return Objects.equals(criteria, that.criteria);
}
return false;
}
+ /**
+ * Returns a new traffic selector builder.
+ *
+ * @return traffic selector builder
+ */
+ public static TrafficSelector.Builder builder() {
+ return new Builder();
+ }
+ /**
+ * Returns a new traffic selector builder primed to produce entities
+ * patterned after the supplied selector.
+ *
+ * @return traffic selector builder
+ */
+ public static TrafficSelector.Builder builder(TrafficSelector selector) {
+ return new Builder(selector);
+ }
- public static class Builder implements TrafficSelector.Builder {
+ /**
+ * Builder of traffic selector entities.
+ */
+ public static final class Builder implements TrafficSelector.Builder {
- private final Logger log = getLogger(getClass());
+ private final Map<Criterion.Type, Criterion> selector = new HashMap<>();
- private final Set<Criterion> selector = new HashSet<>();
+ private Builder() {
+ }
+
+ private Builder(TrafficSelector selector) {
+ for (Criterion c : selector.criteria()) {
+ add(c);
+ }
+ }
@Override
public Builder add(Criterion criterion) {
- selector.add(criterion);
+ selector.put(criterion.type(), criterion);
return this;
}
@@ -107,7 +141,7 @@
@Override
public TrafficSelector build() {
- return new DefaultTrafficSelector(selector);
+ return new DefaultTrafficSelector(ImmutableSet.copyOf(selector.values()));
}
}
diff --git a/core/api/src/main/java/org/onlab/onos/net/flow/DefaultTrafficTreatment.java b/core/api/src/main/java/org/onlab/onos/net/flow/DefaultTrafficTreatment.java
index 2ce233f..7182916 100644
--- a/core/api/src/main/java/org/onlab/onos/net/flow/DefaultTrafficTreatment.java
+++ b/core/api/src/main/java/org/onlab/onos/net/flow/DefaultTrafficTreatment.java
@@ -5,6 +5,7 @@
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
+import java.util.Objects;
import org.onlab.onos.net.PortNumber;
import org.onlab.onos.net.flow.instructions.Instruction;
@@ -14,10 +15,18 @@
import org.onlab.packet.VlanId;
import org.slf4j.Logger;
+/**
+ * Default traffic treatment implementation.
+ */
public final class DefaultTrafficTreatment implements TrafficTreatment {
private final List<Instruction> instructions;
+ /**
+ * Creates a new traffic treatment from the specified list of instructions.
+ *
+ * @param instructions treatment instructions
+ */
private DefaultTrafficTreatment(List<Instruction> instructions) {
this.instructions = Collections.unmodifiableList(instructions);
}
@@ -28,12 +37,38 @@
}
/**
+ * Returns a new traffic treatment builder.
+ *
+ * @return traffic treatment builder
+ */
+ public static TrafficTreatment.Builder builder() {
+ return new Builder();
+ }
+
+ //FIXME: Order of instructions may affect hashcode
+ @Override
+ public int hashCode() {
+ return Objects.hash(instructions);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj instanceof DefaultTrafficTreatment) {
+ DefaultTrafficTreatment that = (DefaultTrafficTreatment) obj;
+ return Objects.equals(instructions, that.instructions);
+
+ }
+ return false;
+ }
+
+ /**
* Builds a list of treatments following the following order.
* Modifications -> Group -> Output (including drop)
- *
*/
-
- public static class Builder implements TrafficTreatment.Builder {
+ public static final class Builder implements TrafficTreatment.Builder {
private final Logger log = getLogger(getClass());
@@ -47,27 +82,32 @@
// TODO: should be a list of instructions based on modification objects
List<Instruction> modifications = new LinkedList<>();
+ // Creates a new builder
+ private Builder() {
+ }
+
+ @Override
public Builder add(Instruction instruction) {
if (drop) {
return this;
}
switch (instruction.type()) {
- case DROP:
- drop = true;
- break;
- case OUTPUT:
- outputs.add(instruction);
- break;
- case L2MODIFICATION:
- case L3MODIFICATION:
- // TODO: enforce modification order if any
- modifications.add(instruction);
- break;
- case GROUP:
- groups.add(instruction);
- break;
- default:
- log.warn("Unknown instruction type {}", instruction.type());
+ case DROP:
+ drop = true;
+ break;
+ case OUTPUT:
+ outputs.add(instruction);
+ break;
+ case L2MODIFICATION:
+ case L3MODIFICATION:
+ // TODO: enforce modification order if any
+ modifications.add(instruction);
+ break;
+ case GROUP:
+ groups.add(instruction);
+ break;
+ default:
+ log.warn("Unknown instruction type {}", instruction.type());
}
return this;
}
diff --git a/core/api/src/main/java/org/onlab/onos/net/flow/FlowEntry.java b/core/api/src/main/java/org/onlab/onos/net/flow/FlowEntry.java
new file mode 100644
index 0000000..5b5f89b
--- /dev/null
+++ b/core/api/src/main/java/org/onlab/onos/net/flow/FlowEntry.java
@@ -0,0 +1,98 @@
+package org.onlab.onos.net.flow;
+
+
+/**
+ * Represents a generalized match & action pair to be applied to
+ * an infrastucture device.
+ */
+public interface FlowEntry extends FlowRule {
+
+
+ public enum FlowEntryState {
+
+ /**
+ * Indicates that this rule has been submitted for addition.
+ * Not necessarily in the flow table.
+ */
+ PENDING_ADD,
+
+ /**
+ * Rule has been added which means it is in the flow table.
+ */
+ ADDED,
+
+ /**
+ * Flow has been marked for removal, might still be in flow table.
+ */
+ PENDING_REMOVE,
+
+ /**
+ * Flow has been removed from flow table and can be purged.
+ */
+ REMOVED
+ }
+
+ /**
+ * Returns the flow entry state.
+ *
+ * @return flow entry state
+ */
+ FlowEntryState state();
+
+ /**
+ * Returns the number of milliseconds this flow rule has been applied.
+ *
+ * @return number of millis
+ */
+ long life();
+
+ /**
+ * Returns the number of packets this flow rule has matched.
+ *
+ * @return number of packets
+ */
+ long packets();
+
+ /**
+ * Returns the number of bytes this flow rule has matched.
+ *
+ * @return number of bytes
+ */
+ long bytes();
+
+ /**
+ * When this flow entry was last deemed active.
+ * @return epoch time of last activity
+ */
+ long lastSeen();
+
+ /**
+ * Sets the last active epoch time.
+ */
+ void setLastSeen();
+
+ /**
+ * Sets the new state for this entry.
+ * @param newState new flow entry state.
+ */
+ void setState(FlowEntryState newState);
+
+ /**
+ * Sets how long this entry has been entered in the system.
+ * @param life epoch time
+ */
+ void setLife(long life);
+
+ /**
+ * Number of packets seen by this entry.
+ * @param packets a long value
+ */
+ void setPackets(long packets);
+
+ /**
+ * Number of bytes seen by this rule.
+ * @param bytes a long value
+ */
+ void setBytes(long bytes);
+
+}
diff --git a/core/api/src/main/java/org/onlab/onos/net/flow/FlowId.java b/core/api/src/main/java/org/onlab/onos/net/flow/FlowId.java
index 6bcf1db..52eac0a 100644
--- a/core/api/src/main/java/org/onlab/onos/net/flow/FlowId.java
+++ b/core/api/src/main/java/org/onlab/onos/net/flow/FlowId.java
@@ -26,6 +26,9 @@
if (this == obj) {
return true;
}
+ if (obj == null) {
+ return false;
+ }
if (obj.getClass() == this.getClass()) {
FlowId that = (FlowId) obj;
return Objects.equal(this.flowid, that.flowid);
diff --git a/core/api/src/main/java/org/onlab/onos/net/flow/FlowRule.java b/core/api/src/main/java/org/onlab/onos/net/flow/FlowRule.java
index 4d1b3cf..410aed4 100644
--- a/core/api/src/main/java/org/onlab/onos/net/flow/FlowRule.java
+++ b/core/api/src/main/java/org/onlab/onos/net/flow/FlowRule.java
@@ -2,49 +2,16 @@
import org.onlab.onos.ApplicationId;
import org.onlab.onos.net.DeviceId;
+import org.onlab.onos.net.intent.BatchOperationTarget;
/**
* Represents a generalized match & action pair to be applied to
* an infrastucture device.
*/
-public interface FlowRule {
+public interface FlowRule extends BatchOperationTarget {
static final int MAX_TIMEOUT = 60;
-
- public enum FlowRuleState {
- /**
- * Indicates that this rule has been created.
- */
- CREATED,
-
- /**
- * Indicates that this rule has been submitted for addition.
- * Not necessarily in the flow table.
- */
- PENDING_ADD,
-
- /**
- * Rule has been added which means it is in the flow table.
- */
- ADDED,
-
- /**
- * Flow has been marked for removal, might still be in flow table.
- */
- PENDING_REMOVE,
-
- /**
- * Flow has been removed from flow table and can be purged.
- */
- REMOVED
- }
-
- /**
- * Returns the flow rule state.
- *
- * @return flow rule state
- */
- FlowRuleState state();
+ static final int MIN_PRIORITY = 0;
//TODO: build cookie value
/**
@@ -92,27 +59,6 @@
TrafficTreatment treatment();
/**
- * Returns the number of milliseconds this flow rule has been applied.
- *
- * @return number of millis
- */
- long lifeMillis();
-
- /**
- * Returns the number of packets this flow rule has matched.
- *
- * @return number of packets
- */
- long packets();
-
- /**
- * Returns the number of bytes this flow rule has matched.
- *
- * @return number of bytes
- */
- long bytes();
-
- /**
* Returns the timeout for this flow requested by an application.
* @return integer value of the timeout
*/
diff --git a/core/api/src/main/java/org/onlab/onos/net/flow/FlowRuleBatchEntry.java b/core/api/src/main/java/org/onlab/onos/net/flow/FlowRuleBatchEntry.java
new file mode 100644
index 0000000..d5a1472
--- /dev/null
+++ b/core/api/src/main/java/org/onlab/onos/net/flow/FlowRuleBatchEntry.java
@@ -0,0 +1,20 @@
+package org.onlab.onos.net.flow;
+
+import org.onlab.onos.net.flow.FlowRuleBatchEntry.FlowRuleOperation;
+import org.onlab.onos.net.intent.BatchOperationEntry;
+
+
+public class FlowRuleBatchEntry
+ extends BatchOperationEntry<FlowRuleOperation, FlowRule> {
+
+ public FlowRuleBatchEntry(FlowRuleOperation operator, FlowRule target) {
+ super(operator, target);
+ }
+
+ public enum FlowRuleOperation {
+ ADD,
+ REMOVE,
+ MODIFY
+ }
+
+}
diff --git a/core/api/src/main/java/org/onlab/onos/net/flow/FlowRuleBatchOperation.java b/core/api/src/main/java/org/onlab/onos/net/flow/FlowRuleBatchOperation.java
new file mode 100644
index 0000000..74ef165
--- /dev/null
+++ b/core/api/src/main/java/org/onlab/onos/net/flow/FlowRuleBatchOperation.java
@@ -0,0 +1,13 @@
+package org.onlab.onos.net.flow;
+
+import java.util.Collection;
+
+import org.onlab.onos.net.intent.BatchOperation;
+
+public class FlowRuleBatchOperation
+ extends BatchOperation<FlowRuleBatchEntry> {
+
+ public FlowRuleBatchOperation(Collection<FlowRuleBatchEntry> operations) {
+ super(operations);
+ }
+}
diff --git a/core/api/src/main/java/org/onlab/onos/net/flow/FlowRuleProvider.java b/core/api/src/main/java/org/onlab/onos/net/flow/FlowRuleProvider.java
index c4e2f92..68762ac 100644
--- a/core/api/src/main/java/org/onlab/onos/net/flow/FlowRuleProvider.java
+++ b/core/api/src/main/java/org/onlab/onos/net/flow/FlowRuleProvider.java
@@ -1,6 +1,9 @@
package org.onlab.onos.net.flow;
+import java.util.concurrent.Future;
+
import org.onlab.onos.ApplicationId;
+import org.onlab.onos.net.intent.BatchOperation;
import org.onlab.onos.net.provider.Provider;
/**
@@ -34,4 +37,6 @@
*/
void removeRulesById(ApplicationId id, FlowRule... flowRules);
+ Future<Void> executeBatch(BatchOperation<FlowRuleBatchEntry> batch);
+
}
diff --git a/core/api/src/main/java/org/onlab/onos/net/flow/FlowRuleProviderService.java b/core/api/src/main/java/org/onlab/onos/net/flow/FlowRuleProviderService.java
index 2076103..8164579 100644
--- a/core/api/src/main/java/org/onlab/onos/net/flow/FlowRuleProviderService.java
+++ b/core/api/src/main/java/org/onlab/onos/net/flow/FlowRuleProviderService.java
@@ -14,7 +14,7 @@
*
* @param flowRule information about the removed flow
*/
- void flowRemoved(FlowRule flowRule);
+ void flowRemoved(FlowEntry flowEntry);
/**
* Pushes the collection of flow entries currently applied on the given
@@ -22,7 +22,7 @@
*
* @param flowRules collection of flow rules
*/
- void pushFlowMetrics(DeviceId deviceId, Iterable<FlowRule> flowRules);
+ void pushFlowMetrics(DeviceId deviceId, Iterable<FlowEntry> flowEntries);
diff --git a/core/api/src/main/java/org/onlab/onos/net/flow/FlowRuleService.java b/core/api/src/main/java/org/onlab/onos/net/flow/FlowRuleService.java
index c09a56d..6d04810 100644
--- a/core/api/src/main/java/org/onlab/onos/net/flow/FlowRuleService.java
+++ b/core/api/src/main/java/org/onlab/onos/net/flow/FlowRuleService.java
@@ -1,5 +1,7 @@
package org.onlab.onos.net.flow;
+import java.util.concurrent.Future;
+
import org.onlab.onos.ApplicationId;
import org.onlab.onos.net.DeviceId;
@@ -13,6 +15,13 @@
public interface FlowRuleService {
/**
+ * Returns the number of flow rules in the system.
+ *
+ * @return flow rule count
+ */
+ int getFlowRuleCount();
+
+ /**
* Returns the collection of flow entries applied on the specified device.
* This will include flow rules which may not yet have been applied to
* the device.
@@ -20,7 +29,7 @@
* @param deviceId device identifier
* @return collection of flow rules
*/
- Iterable<FlowRule> getFlowEntries(DeviceId deviceId);
+ Iterable<FlowEntry> getFlowEntries(DeviceId deviceId);
// TODO: add createFlowRule factory method and execute operations method
@@ -60,6 +69,13 @@
Iterable<FlowRule> getFlowRulesById(ApplicationId id);
/**
+ * Applies a batch operation of FlowRules.
+ *
+ * @return future indicating the state of the batch operation
+ */
+ Future<CompletedBatchOperation> applyBatch(FlowRuleBatchOperation batch);
+
+ /**
* Adds the specified flow rule listener.
*
* @param listener flow rule listener
@@ -72,7 +88,4 @@
* @param listener flow rule listener
*/
void removeListener(FlowRuleListener listener);
-
-
-
}
diff --git a/core/api/src/main/java/org/onlab/onos/net/flow/FlowRuleStore.java b/core/api/src/main/java/org/onlab/onos/net/flow/FlowRuleStore.java
index c6093384..5ce7eb1 100644
--- a/core/api/src/main/java/org/onlab/onos/net/flow/FlowRuleStore.java
+++ b/core/api/src/main/java/org/onlab/onos/net/flow/FlowRuleStore.java
@@ -10,11 +10,19 @@
public interface FlowRuleStore extends Store<FlowRuleEvent, FlowRuleStoreDelegate> {
/**
+ * Returns the number of flow rule in the store.
+ *
+ * @return number of flow rules
+ */
+ int getFlowRuleCount();
+
+ /**
* Returns the stored flow.
+ *
* @param rule the rule to look for
* @return a flow rule
*/
- FlowRule getFlowRule(FlowRule rule);
+ FlowEntry getFlowEntry(FlowRule rule);
/**
* Returns the flow entries associated with a device.
@@ -22,7 +30,7 @@
* @param deviceId the device ID
* @return the flow entries
*/
- Iterable<FlowRule> getFlowEntries(DeviceId deviceId);
+ Iterable<FlowEntry> getFlowEntries(DeviceId deviceId);
/**
* Returns the flow entries associated with an application.
@@ -30,7 +38,7 @@
* @param appId the application id
* @return the flow entries
*/
- Iterable<FlowRule> getFlowEntriesByAppId(ApplicationId appId);
+ Iterable<FlowRule> getFlowRulesByAppId(ApplicationId appId);
/**
* Stores a new flow rule without generating events.
@@ -40,7 +48,8 @@
void storeFlowRule(FlowRule rule);
/**
- * Deletes a flow rule without generating events.
+ * Marks a flow rule for deletion. Actual deletion will occur
+ * when the provider indicates that the flow has been removed.
*
* @param rule the flow rule to delete
*/
@@ -52,12 +61,11 @@
* @param rule the flow rule to add or update
* @return flow_added event, or null if just an update
*/
- FlowRuleEvent addOrUpdateFlowRule(FlowRule rule);
+ FlowRuleEvent addOrUpdateFlowRule(FlowEntry rule);
/**
- * @param rule the flow rule to remove
+ * @param rule the flow entry to remove
* @return flow_removed event, or null if nothing removed
*/
- FlowRuleEvent removeFlowRule(FlowRule rule);
-
+ FlowRuleEvent removeFlowRule(FlowEntry rule);
}
diff --git a/core/api/src/main/java/org/onlab/onos/net/flow/instructions/Instructions.java b/core/api/src/main/java/org/onlab/onos/net/flow/instructions/Instructions.java
index 1bf5531..b2ebdee 100644
--- a/core/api/src/main/java/org/onlab/onos/net/flow/instructions/Instructions.java
+++ b/core/api/src/main/java/org/onlab/onos/net/flow/instructions/Instructions.java
@@ -3,6 +3,8 @@
import static com.google.common.base.MoreObjects.toStringHelper;
import static com.google.common.base.Preconditions.checkNotNull;
+import java.util.Objects;
+
import org.onlab.onos.net.PortNumber;
import org.onlab.onos.net.flow.instructions.L2ModificationInstruction.L2SubType;
import org.onlab.onos.net.flow.instructions.L2ModificationInstruction.ModEtherInstruction;
@@ -117,6 +119,24 @@
return toStringHelper(type()).toString();
}
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(type());
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj instanceof DropInstruction) {
+ DropInstruction that = (DropInstruction) obj;
+ return Objects.equals(type(), that.type());
+
+ }
+ return false;
+ }
}
@@ -140,6 +160,26 @@
return toStringHelper(type().toString())
.add("port", port).toString();
}
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(port, type());
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj instanceof OutputInstruction) {
+ OutputInstruction that = (OutputInstruction) obj;
+ Objects.equals(port, that.port);
+
+ }
+ return false;
+ }
}
}
+
+
diff --git a/core/api/src/main/java/org/onlab/onos/net/flow/instructions/L2ModificationInstruction.java b/core/api/src/main/java/org/onlab/onos/net/flow/instructions/L2ModificationInstruction.java
index 8c51624..ce7e16b 100644
--- a/core/api/src/main/java/org/onlab/onos/net/flow/instructions/L2ModificationInstruction.java
+++ b/core/api/src/main/java/org/onlab/onos/net/flow/instructions/L2ModificationInstruction.java
@@ -2,6 +2,8 @@
import static com.google.common.base.MoreObjects.toStringHelper;
+import java.util.Objects;
+
import org.onlab.packet.MacAddress;
import org.onlab.packet.VlanId;
@@ -74,6 +76,25 @@
.add("mac", mac).toString();
}
+ @Override
+ public int hashCode() {
+ return Objects.hash(mac, subtype);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj instanceof ModEtherInstruction) {
+ ModEtherInstruction that = (ModEtherInstruction) obj;
+ return Objects.equals(mac, that.mac) &&
+ Objects.equals(subtype, that.subtype);
+
+ }
+ return false;
+ }
+
}
@@ -103,6 +124,25 @@
.add("id", vlanId).toString();
}
+ @Override
+ public int hashCode() {
+ return Objects.hash(vlanId, subtype());
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj instanceof ModVlanIdInstruction) {
+ ModVlanIdInstruction that = (ModVlanIdInstruction) obj;
+ return Objects.equals(vlanId, that.vlanId);
+
+ }
+ return false;
+ }
+
+
}
/**
@@ -131,6 +171,24 @@
.add("pcp", Long.toHexString(vlanPcp)).toString();
}
+ @Override
+ public int hashCode() {
+ return Objects.hash(vlanPcp, subtype());
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj instanceof ModVlanPcpInstruction) {
+ ModVlanPcpInstruction that = (ModVlanPcpInstruction) obj;
+ return Objects.equals(vlanPcp, that.vlanPcp);
+
+ }
+ return false;
+ }
+
}
diff --git a/core/api/src/main/java/org/onlab/onos/net/flow/instructions/L3ModificationInstruction.java b/core/api/src/main/java/org/onlab/onos/net/flow/instructions/L3ModificationInstruction.java
index ae82cd9..cf81f86 100644
--- a/core/api/src/main/java/org/onlab/onos/net/flow/instructions/L3ModificationInstruction.java
+++ b/core/api/src/main/java/org/onlab/onos/net/flow/instructions/L3ModificationInstruction.java
@@ -2,6 +2,8 @@
import static com.google.common.base.MoreObjects.toStringHelper;
+import java.util.Objects;
+
import org.onlab.packet.IpPrefix;
/**
@@ -66,5 +68,23 @@
.add("ip", ip).toString();
}
+ @Override
+ public int hashCode() {
+ return Objects.hash(ip, subtype());
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj instanceof ModIPInstruction) {
+ ModIPInstruction that = (ModIPInstruction) obj;
+ return Objects.equals(ip, that.ip);
+
+ }
+ return false;
+ }
+
}
}
diff --git a/core/api/src/main/java/org/onlab/onos/net/intent/AbstractIntent.java b/core/api/src/main/java/org/onlab/onos/net/intent/AbstractIntent.java
index eefe750..c8a4a05 100644
--- a/core/api/src/main/java/org/onlab/onos/net/intent/AbstractIntent.java
+++ b/core/api/src/main/java/org/onlab/onos/net/intent/AbstractIntent.java
@@ -24,7 +24,7 @@
}
@Override
- public IntentId getId() {
+ public IntentId id() {
return id;
}
diff --git a/core/api/src/main/java/org/onlab/onos/net/intent/BatchOperation.java b/core/api/src/main/java/org/onlab/onos/net/intent/BatchOperation.java
index ad34a2c..72a9847 100644
--- a/core/api/src/main/java/org/onlab/onos/net/intent/BatchOperation.java
+++ b/core/api/src/main/java/org/onlab/onos/net/intent/BatchOperation.java
@@ -3,6 +3,7 @@
import static com.google.common.base.Preconditions.checkNotNull;
+import java.util.Collection;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
@@ -11,11 +12,11 @@
* A list of BatchOperationEntry.
*
* @param <T> the enum of operators <br>
- * This enum must be defined in each sub-classes.
- *
+ * This enum must be defined in each sub-classes.
*/
public abstract class BatchOperation<T extends BatchOperationEntry<?, ?>> {
- private List<T> ops;
+
+ private final List<T> ops;
/**
* Creates new {@link BatchOperation} object.
@@ -30,7 +31,7 @@
*
* @param batchOperations the list of batch operation entries.
*/
- public BatchOperation(List<T> batchOperations) {
+ public BatchOperation(Collection<T> batchOperations) {
ops = new LinkedList<>(checkNotNull(batchOperations));
}
@@ -61,6 +62,10 @@
/**
* Adds an operation.
+ * FIXME: Brian promises that the Intent Framework
+ * will not modify the batch operation after it has submitted it.
+ * Ali would prefer immutablity, but trusts brian for better or
+ * for worse.
*
* @param entry the operation to be added
* @return this object if succeeded, null otherwise
diff --git a/core/api/src/main/java/org/onlab/onos/net/intent/BatchOperationEntry.java b/core/api/src/main/java/org/onlab/onos/net/intent/BatchOperationEntry.java
index b5dfa88..4e57d33 100644
--- a/core/api/src/main/java/org/onlab/onos/net/intent/BatchOperationEntry.java
+++ b/core/api/src/main/java/org/onlab/onos/net/intent/BatchOperationEntry.java
@@ -15,14 +15,7 @@
private final T operator;
private final U target;
- /**
- * Default constructor for serializer.
- */
- @Deprecated
- protected BatchOperationEntry() {
- this.operator = null;
- this.target = null;
- }
+
/**
* Constructs new instance for the entry of the BatchOperation.
diff --git a/core/api/src/main/java/org/onlab/onos/net/intent/ConnectivityIntent.java b/core/api/src/main/java/org/onlab/onos/net/intent/ConnectivityIntent.java
index 629a9d1..ed0c5cc 100644
--- a/core/api/src/main/java/org/onlab/onos/net/intent/ConnectivityIntent.java
+++ b/core/api/src/main/java/org/onlab/onos/net/intent/ConnectivityIntent.java
@@ -1,11 +1,10 @@
package org.onlab.onos.net.intent;
-import static com.google.common.base.Preconditions.checkNotNull;
-
+import com.google.common.base.Objects;
import org.onlab.onos.net.flow.TrafficSelector;
import org.onlab.onos.net.flow.TrafficTreatment;
-import com.google.common.base.Objects;
+import static com.google.common.base.Preconditions.checkNotNull;
/**
* Abstraction of connectivity intent for traffic matching some criteria.
@@ -26,17 +25,18 @@
/**
* Creates a connectivity intent that matches on the specified intent
- * and applies the specified action.
+ * and applies the specified treatement.
*
- * @param id intent identifier
- * @param match traffic match
- * @param action action
- * @throws NullPointerException if the match or action is null
+ * @param intentId intent identifier
+ * @param selector traffic selector
+ * @param treatement treatement
+ * @throws NullPointerException if the selector or treatement is null
*/
- protected ConnectivityIntent(IntentId id, TrafficSelector match, TrafficTreatment action) {
- super(id);
- this.selector = checkNotNull(match);
- this.treatment = checkNotNull(action);
+ protected ConnectivityIntent(IntentId intentId, TrafficSelector selector,
+ TrafficTreatment treatement) {
+ super(intentId);
+ this.selector = checkNotNull(selector);
+ this.treatment = checkNotNull(treatement);
}
/**
@@ -53,7 +53,7 @@
*
* @return traffic match
*/
- public TrafficSelector getTrafficSelector() {
+ public TrafficSelector selector() {
return selector;
}
@@ -62,7 +62,7 @@
*
* @return applied action
*/
- public TrafficTreatment getTrafficTreatment() {
+ public TrafficTreatment treatment() {
return treatment;
}
diff --git a/core/api/src/main/java/org/onlab/onos/net/intent/HostToHostIntent.java b/core/api/src/main/java/org/onlab/onos/net/intent/HostToHostIntent.java
new file mode 100644
index 0000000..f420fc2
--- /dev/null
+++ b/core/api/src/main/java/org/onlab/onos/net/intent/HostToHostIntent.java
@@ -0,0 +1,91 @@
+package org.onlab.onos.net.intent;
+
+import com.google.common.base.MoreObjects;
+import org.onlab.onos.net.HostId;
+import org.onlab.onos.net.flow.TrafficSelector;
+import org.onlab.onos.net.flow.TrafficTreatment;
+
+import java.util.Objects;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Abstraction of end-station to end-station bidirectional connectivity.
+ */
+public class HostToHostIntent extends ConnectivityIntent {
+
+ private final HostId one;
+ private final HostId two;
+
+ /**
+ * Creates a new point-to-point intent with the supplied ingress/egress
+ * ports.
+ *
+ * @param intentId intent identifier
+ * @param one first host
+ * @param two second host
+ * @param selector action
+ * @param treatment ingress port
+ * @throws NullPointerException if {@code ingressPort} or {@code egressPort}
+ * is null.
+ */
+ public HostToHostIntent(IntentId intentId, HostId one, HostId two,
+ TrafficSelector selector,
+ TrafficTreatment treatment) {
+ super(intentId, selector, treatment);
+ this.one = checkNotNull(one);
+ this.two = checkNotNull(two);
+ }
+
+ /**
+ * Returns identifier of the first host.
+ *
+ * @return first host identifier
+ */
+ public HostId one() {
+ return one;
+ }
+
+ /**
+ * Returns identifier of the second host.
+ *
+ * @return second host identifier
+ */
+ public HostId two() {
+ return two;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ if (!super.equals(o)) {
+ return false;
+ }
+
+ HostToHostIntent that = (HostToHostIntent) o;
+ return Objects.equals(this.one, that.one)
+ && Objects.equals(this.two, that.two);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(super.hashCode(), one, two);
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(getClass())
+ .add("id", id())
+ .add("selector", selector())
+ .add("treatment", treatment())
+ .add("one", one)
+ .add("two", two)
+ .toString();
+ }
+
+}
diff --git a/core/api/src/main/java/org/onlab/onos/net/intent/InstallableIntent.java b/core/api/src/main/java/org/onlab/onos/net/intent/InstallableIntent.java
index 66bc759..488695c 100644
--- a/core/api/src/main/java/org/onlab/onos/net/intent/InstallableIntent.java
+++ b/core/api/src/main/java/org/onlab/onos/net/intent/InstallableIntent.java
@@ -1,8 +1,22 @@
package org.onlab.onos.net.intent;
+import org.onlab.onos.net.Link;
+
+import java.util.Collection;
+
/**
* Abstraction of an intent that can be installed into
* the underlying system without additional compilation.
*/
public interface InstallableIntent extends Intent {
+
+ /**
+ * Returns the collection of links that are required for this installable
+ * intent to exist.
+ *
+ * @return collection of links
+ */
+ // FIXME: replace this with 'NetworkResource'
+ Collection<Link> requiredLinks();
+
}
diff --git a/core/api/src/main/java/org/onlab/onos/net/intent/Intent.java b/core/api/src/main/java/org/onlab/onos/net/intent/Intent.java
index b239ede..3e339d1 100644
--- a/core/api/src/main/java/org/onlab/onos/net/intent/Intent.java
+++ b/core/api/src/main/java/org/onlab/onos/net/intent/Intent.java
@@ -2,7 +2,7 @@
/**
* Abstraction of an application level intent.
- *
+ * <p/>
* Make sure that an Intent should be immutable when a new type is defined.
*/
public interface Intent extends BatchOperationTarget {
@@ -11,5 +11,5 @@
*
* @return intent identifier
*/
- IntentId getId();
+ IntentId id();
}
diff --git a/core/api/src/main/java/org/onlab/onos/net/intent/IntentEvent.java b/core/api/src/main/java/org/onlab/onos/net/intent/IntentEvent.java
index 27ae834..742a590 100644
--- a/core/api/src/main/java/org/onlab/onos/net/intent/IntentEvent.java
+++ b/core/api/src/main/java/org/onlab/onos/net/intent/IntentEvent.java
@@ -1,113 +1,55 @@
package org.onlab.onos.net.intent;
-import static com.google.common.base.Preconditions.checkNotNull;
-
-import java.util.Objects;
-
-import com.google.common.base.MoreObjects;
+import org.onlab.onos.event.AbstractEvent;
/**
* A class to represent an intent related event.
*/
-public class IntentEvent {
+public class IntentEvent extends AbstractEvent<IntentEvent.Type, Intent> {
- // TODO: determine a suitable parent class; if one does not exist, consider introducing one
+ public enum Type {
+ /**
+ * Signifies that a new intent has been submitted to the system.
+ */
+ SUBMITTED,
- private final long time;
- private final Intent intent;
- private final IntentState state;
- private final IntentState previous;
+ /**
+ * Signifies that an intent has been successfully installed.
+ */
+ INSTALLED,
+
+ /**
+ * Signifies that an intent has failed compilation or installation.
+ */
+ FAILED,
+
+ /**
+ * Signifies that an intent has been withdrawn from the system.
+ */
+ WITHDRAWN
+ }
/**
- * Creates an event describing a state change of an intent.
+ * Creates an event of a given type and for the specified intent and the
+ * current time.
*
+ * @param type event type
* @param intent subject intent
- * @param state new intent state
- * @param previous previous intent state
- * @param time time the event created in milliseconds since start of epoch
- * @throws NullPointerException if the intent or state is null
+ * @param time time the event created in milliseconds since start of epoch
*/
- public IntentEvent(Intent intent, IntentState state, IntentState previous, long time) {
- this.intent = checkNotNull(intent);
- this.state = checkNotNull(state);
- this.previous = previous;
- this.time = time;
+ public IntentEvent(Type type, Intent intent, long time) {
+ super(type, intent, time);
}
/**
- * Constructor for serializer.
- */
- protected IntentEvent() {
- this.intent = null;
- this.state = null;
- this.previous = null;
- this.time = 0;
- }
-
- /**
- * Returns the state of the intent which caused the event.
+ * Creates an event of a given type and for the specified intent and the
+ * current time.
*
- * @return the state of the intent
+ * @param type event type
+ * @param intent subject intent
*/
- public IntentState getState() {
- return state;
+ public IntentEvent(Type type, Intent intent) {
+ super(type, intent);
}
- /**
- * Returns the previous state of the intent which caused the event.
- *
- * @return the previous state of the intent
- */
- public IntentState getPreviousState() {
- return previous;
- }
-
- /**
- * Returns the intent associated with the event.
- *
- * @return the intent
- */
- public Intent getIntent() {
- return intent;
- }
-
- /**
- * Returns the time at which the event was created.
- *
- * @return the time in milliseconds since start of epoch
- */
- public long getTime() {
- return time;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) {
- return true;
- }
- if (o == null || getClass() != o.getClass()) {
- return false;
- }
-
- IntentEvent that = (IntentEvent) o;
- return Objects.equals(this.intent, that.intent)
- && Objects.equals(this.state, that.state)
- && Objects.equals(this.previous, that.previous)
- && Objects.equals(this.time, that.time);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(intent, state, previous, time);
- }
-
- @Override
- public String toString() {
- return MoreObjects.toStringHelper(getClass())
- .add("intent", intent)
- .add("state", state)
- .add("previous", previous)
- .add("time", time)
- .toString();
- }
}
diff --git a/core/api/src/main/java/org/onlab/onos/net/intent/IntentEventListener.java b/core/api/src/main/java/org/onlab/onos/net/intent/IntentEventListener.java
deleted file mode 100644
index f59ecfc..0000000
--- a/core/api/src/main/java/org/onlab/onos/net/intent/IntentEventListener.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package org.onlab.onos.net.intent;
-
-/**
- * Listener for {@link IntentEvent intent events}.
- */
-public interface IntentEventListener {
- /**
- * Processes the specified intent event.
- *
- * @param event the event to process
- */
- void event(IntentEvent event);
-}
diff --git a/core/api/src/main/java/org/onlab/onos/net/intent/IntentException.java b/core/api/src/main/java/org/onlab/onos/net/intent/IntentException.java
index 4148dea..fff55be 100644
--- a/core/api/src/main/java/org/onlab/onos/net/intent/IntentException.java
+++ b/core/api/src/main/java/org/onlab/onos/net/intent/IntentException.java
@@ -26,7 +26,7 @@
* Constructs an exception with the specified message and the underlying cause.
*
* @param message the message describing the specific nature of the error
- * @param cause the underlying cause of this error
+ * @param cause the underlying cause of this error
*/
public IntentException(String message, Throwable cause) {
super(message, cause);
diff --git a/core/api/src/main/java/org/onlab/onos/net/intent/IntentExtensionService.java b/core/api/src/main/java/org/onlab/onos/net/intent/IntentExtensionService.java
index c6338a7f..8deb372 100644
--- a/core/api/src/main/java/org/onlab/onos/net/intent/IntentExtensionService.java
+++ b/core/api/src/main/java/org/onlab/onos/net/intent/IntentExtensionService.java
@@ -10,9 +10,9 @@
/**
* Registers the specified compiler for the given intent class.
*
- * @param cls intent class
+ * @param cls intent class
* @param compiler intent compiler
- * @param <T> the type of intent
+ * @param <T> the type of intent
*/
<T extends Intent> void registerCompiler(Class<T> cls, IntentCompiler<T> compiler);
@@ -34,9 +34,9 @@
/**
* Registers the specified installer for the given installable intent class.
*
- * @param cls installable intent class
+ * @param cls installable intent class
* @param installer intent installer
- * @param <T> the type of installable intent
+ * @param <T> the type of installable intent
*/
<T extends InstallableIntent> void registerInstaller(Class<T> cls, IntentInstaller<T> installer);
diff --git a/core/api/src/main/java/org/onlab/onos/net/intent/IntentId.java b/core/api/src/main/java/org/onlab/onos/net/intent/IntentId.java
index 798e00c..8f132c0 100644
--- a/core/api/src/main/java/org/onlab/onos/net/intent/IntentId.java
+++ b/core/api/src/main/java/org/onlab/onos/net/intent/IntentId.java
@@ -2,7 +2,7 @@
/**
* Intent identifier suitable as an external key.
- *
+ * <p/>
* This class is immutable.
*/
public final class IntentId implements BatchOperationTarget {
diff --git a/core/api/src/main/java/org/onlab/onos/net/intent/IntentListener.java b/core/api/src/main/java/org/onlab/onos/net/intent/IntentListener.java
new file mode 100644
index 0000000..c00c1f6
--- /dev/null
+++ b/core/api/src/main/java/org/onlab/onos/net/intent/IntentListener.java
@@ -0,0 +1,9 @@
+package org.onlab.onos.net.intent;
+
+import org.onlab.onos.event.EventListener;
+
+/**
+ * Listener for {@link IntentEvent intent events}.
+ */
+public interface IntentListener extends EventListener<IntentEvent> {
+}
diff --git a/core/api/src/main/java/org/onlab/onos/net/intent/IntentService.java b/core/api/src/main/java/org/onlab/onos/net/intent/IntentService.java
index 2b4fb59..c3aae54 100644
--- a/core/api/src/main/java/org/onlab/onos/net/intent/IntentService.java
+++ b/core/api/src/main/java/org/onlab/onos/net/intent/IntentService.java
@@ -1,6 +1,5 @@
package org.onlab.onos.net.intent;
-import java.util.Set;
/**
* Service for application submitting or withdrawing their intents.
@@ -8,9 +7,9 @@
public interface IntentService {
/**
* Submits an intent into the system.
- *
- * This is an asynchronous request meaning that any compiling
- * or installation activities may be done at later time.
+ * <p/>
+ * This is an asynchronous request meaning that any compiling or
+ * installation activities may be done at later time.
*
* @param intent intent to be submitted
*/
@@ -18,9 +17,9 @@
/**
* Withdraws an intent from the system.
- *
- * This is an asynchronous request meaning that the environment
- * may be affected at later time.
+ * <p/>
+ * This is an asynchronous request meaning that the environment may be
+ * affected at later time.
*
* @param intent intent to be withdrawn
*/
@@ -29,20 +28,27 @@
/**
* Submits a batch of submit & withdraw operations. Such a batch is
* assumed to be processed together.
- *
- * This is an asynchronous request meaning that the environment
- * may be affected at later time.
+ * <p/>
+ * This is an asynchronous request meaning that the environment may be
+ * affected at later time.
*
* @param operations batch of intent operations
*/
void execute(IntentOperations operations);
/**
- * Returns immutable set of intents currently in the system.
+ * Returns an iterable of intents currently in the system.
*
* @return set of intents
*/
- Set<Intent> getIntents();
+ Iterable<Intent> getIntents();
+
+ /**
+ * Returns the number of intents currently in the system.
+ *
+ * @return number of intents
+ */
+ long getIntentCount();
/**
* Retrieves the intent specified by its identifier.
@@ -56,7 +62,8 @@
* Retrieves the state of an intent by its identifier.
*
* @param id intent identifier
- * @return the intent state or null if one with the given identifier is not found
+ * @return the intent state or null if one with the given identifier is not
+ * found
*/
IntentState getIntentState(IntentId id);
@@ -65,12 +72,12 @@
*
* @param listener listener to be added
*/
- void addListener(IntentEventListener listener);
+ void addListener(IntentListener listener);
/**
* Removes the specified listener for intent events.
*
* @param listener listener to be removed
*/
- void removeListener(IntentEventListener listener);
+ void removeListener(IntentListener listener);
}
diff --git a/core/api/src/main/java/org/onlab/onos/net/intent/IntentState.java b/core/api/src/main/java/org/onlab/onos/net/intent/IntentState.java
index 20476e5..bd140af 100644
--- a/core/api/src/main/java/org/onlab/onos/net/intent/IntentState.java
+++ b/core/api/src/main/java/org/onlab/onos/net/intent/IntentState.java
@@ -1,55 +1,70 @@
package org.onlab.onos.net.intent;
/**
- * This class represents the states of an intent.
- *
- * <p>
- * Note: The state is expressed as enum, but there is possibility
- * in the future that we define specific class instead of enum to improve
- * the extensibility of state definition.
- * </p>
+ * Representation of the phases an intent may attain during its lifecycle.
*/
public enum IntentState {
- // FIXME: requires discussion on State vs. EventType and a solid state-transition diagram
- // TODO: consider the impact of conflict detection
- // TODO: consider the impact that external events affect an installed intent
+
/**
- * The beginning state.
- *
+ * Signifies that the intent has been submitted and will start compiling
+ * shortly. However, this compilation may not necessarily occur on the
+ * local controller instance.
+ * <p/>
* All intent in the runtime take this state first.
*/
SUBMITTED,
/**
- * The intent compilation has been completed.
- *
- * An intent translation graph (tree) is completely created.
- * Leaves of the graph are installable intent type.
+ * Signifies that the intent is being compiled into installable intents.
+ * This is a transitional state after which the intent will enter either
+ * {@link #FAILED} state or {@link #INSTALLING} state.
*/
- COMPILED,
+ COMPILING,
/**
- * The intent has been successfully installed.
+ * Signifies that the resulting installable intents are being installed
+ * into the network environment. This is a transitional state after which
+ * the intent will enter either {@link #INSTALLED} state or
+ * {@link #RECOMPILING} state.
+ */
+ INSTALLING,
+
+ /**
+ * The intent has been successfully installed. This is a state where the
+ * intent may remain parked until it is withdrawn by the application or
+ * until the network environment changes in some way to make the original
+ * set of installable intents untenable.
*/
INSTALLED,
/**
- * The intent is being withdrawn.
- *
- * When {@link IntentService#withdraw(Intent)} is called,
- * the intent takes this state first.
+ * Signifies that the intent is being recompiled into installable intents
+ * as an attempt to adapt to an anomaly in the network environment.
+ * This is a transitional state after which the intent will enter either
+ * {@link #FAILED} state or {@link #INSTALLING} state.
+ * <p/>
+ * Exit to the {@link #FAILED} state may be caused by failure to compile
+ * or by compiling into the same set of installable intents which have
+ * previously failed to be installed.
+ */
+ RECOMPILING,
+
+ /**
+ * Indicates that the intent is being withdrawn. This is a transitional
+ * state, triggered by invocation of the
+ * {@link IntentService#withdraw(Intent)} but one with only one outcome,
+ * which is the the intent being placed in the {@link #WITHDRAWN} state.
*/
WITHDRAWING,
/**
- * The intent has been successfully withdrawn.
+ * Indicates that the intent has been successfully withdrawn.
*/
WITHDRAWN,
/**
- * The intent has failed to be compiled, installed, or withdrawn.
- *
- * When the intent failed to be withdrawn, it is still, at least partially installed.
+ * Signifies that the intent has failed compiling, installing or
+ * recompiling states.
*/
- FAILED,
+ FAILED
}
diff --git a/core/api/src/main/java/org/onlab/onos/net/intent/IntentStore.java b/core/api/src/main/java/org/onlab/onos/net/intent/IntentStore.java
new file mode 100644
index 0000000..fc023bb
--- /dev/null
+++ b/core/api/src/main/java/org/onlab/onos/net/intent/IntentStore.java
@@ -0,0 +1,101 @@
+package org.onlab.onos.net.intent;
+
+import org.onlab.onos.store.Store;
+
+import java.util.List;
+
+/**
+ * Manages inventory of end-station intents; not intended for direct use.
+ */
+public interface IntentStore extends Store<IntentEvent, IntentStoreDelegate> {
+
+ /**
+ * Submits a new intent into the store. If the returned event is not
+ * null, the manager is expected to dispatch the event and then to kick
+ * off intent compilation process. Otherwise, another node has been elected
+ * to perform the compilation process and the node will learn about
+ * the submittal and results of the intent compilation via the delegate
+ * mechanism.
+ *
+ * @param intent intent to be submitted
+ * @return event indicating the intent was submitted or null if no
+ * change resulted, e.g. duplicate intent
+ */
+ IntentEvent createIntent(Intent intent);
+
+ /**
+ * Removes the specified intent from the inventory.
+ *
+ * @param intentId intent identification
+ * @return removed state transition event or null if intent was not found
+ */
+ IntentEvent removeIntent(IntentId intentId);
+
+ /**
+ * Returns the number of intents in the store.
+ */
+ long getIntentCount();
+
+ /**
+ * Returns a collection of all intents in the store.
+ *
+ * @return iterable collection of all intents
+ */
+ Iterable<Intent> getIntents();
+
+ /**
+ * Returns the intent with the specified identifer.
+ *
+ * @param intentId intent identification
+ * @return intent or null if not found
+ */
+ Intent getIntent(IntentId intentId);
+
+ /**
+ * Returns the state of the specified intent.
+ *
+ * @param intentId intent identification
+ * @return current intent state
+ */
+ IntentState getIntentState(IntentId intentId);
+
+ /**
+ * Sets the state of the specified intent to the new state.
+ *
+ * @param intent intent whose state is to be changed
+ * @param newState new state
+ * @return state transition event
+ */
+ IntentEvent setState(Intent intent, IntentState newState);
+
+ /**
+ * Adds the installable intents which resulted from compilation of the
+ * specified original intent.
+ *
+ * @param intentId original intent identifier
+ * @param installableIntents compiled installable intents
+ */
+ void addInstallableIntents(IntentId intentId,
+ List<InstallableIntent> installableIntents);
+
+ /**
+ * Returns the list of the installable events associated with the specified
+ * original intent.
+ *
+ * @param intentId original intent identifier
+ * @return compiled installable intents
+ */
+ List<InstallableIntent> getInstallableIntents(IntentId intentId);
+
+ // TODO: this should be triggered from with the store as a result of removeIntent call
+
+ /**
+ * Removes any installable intents which resulted from compilation of the
+ * specified original intent.
+ *
+ * @param intentId original intent identifier
+ * @return compiled state transition event
+ */
+ void removeInstalledIntents(IntentId intentId);
+
+}
diff --git a/core/api/src/main/java/org/onlab/onos/net/intent/IntentStoreDelegate.java b/core/api/src/main/java/org/onlab/onos/net/intent/IntentStoreDelegate.java
new file mode 100644
index 0000000..6c37db8
--- /dev/null
+++ b/core/api/src/main/java/org/onlab/onos/net/intent/IntentStoreDelegate.java
@@ -0,0 +1,9 @@
+package org.onlab.onos.net.intent;
+
+import org.onlab.onos.store.StoreDelegate;
+
+/**
+ * Intent store delegate abstraction.
+ */
+public interface IntentStoreDelegate extends StoreDelegate<IntentEvent> {
+}
diff --git a/core/api/src/main/java/org/onlab/onos/net/intent/MultiPointToSinglePointIntent.java b/core/api/src/main/java/org/onlab/onos/net/intent/MultiPointToSinglePointIntent.java
index 1e421ab..be8d309 100644
--- a/core/api/src/main/java/org/onlab/onos/net/intent/MultiPointToSinglePointIntent.java
+++ b/core/api/src/main/java/org/onlab/onos/net/intent/MultiPointToSinglePointIntent.java
@@ -1,25 +1,24 @@
package org.onlab.onos.net.intent;
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.base.Preconditions.checkNotNull;
-
-import java.util.Objects;
-import java.util.Set;
-
+import com.google.common.base.MoreObjects;
+import com.google.common.collect.Sets;
import org.onlab.onos.net.ConnectPoint;
import org.onlab.onos.net.flow.TrafficSelector;
import org.onlab.onos.net.flow.TrafficTreatment;
-import com.google.common.base.MoreObjects;
-import com.google.common.collect.Sets;
+import java.util.Objects;
+import java.util.Set;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
/**
* Abstraction of multiple source to single destination connectivity intent.
*/
public class MultiPointToSinglePointIntent extends ConnectivityIntent {
- private final Set<ConnectPoint> ingressPorts;
- private final ConnectPoint egressPort;
+ private final Set<ConnectPoint> ingressPoints;
+ private final ConnectPoint egressPoint;
/**
* Creates a new multi-to-single point connectivity intent for the specified
@@ -28,23 +27,25 @@
* @param id intent identifier
* @param match traffic match
* @param action action
- * @param ingressPorts set of ports from which ingress traffic originates
- * @param egressPort port to which traffic will egress
- * @throws NullPointerException if {@code ingressPorts} or
- * {@code egressPort} is null.
- * @throws IllegalArgumentException if the size of {@code ingressPorts} is
- * not more than 1
+ * @param ingressPoints set of ports from which ingress traffic originates
+ * @param egressPoint port to which traffic will egress
+ * @throws NullPointerException if {@code ingressPoints} or
+ * {@code egressPoint} is null.
+ * @throws IllegalArgumentException if the size of {@code ingressPoints} is
+ * not more than 1
*/
- public MultiPointToSinglePointIntent(IntentId id, TrafficSelector match, TrafficTreatment action,
- Set<ConnectPoint> ingressPorts, ConnectPoint egressPort) {
+ public MultiPointToSinglePointIntent(IntentId id, TrafficSelector match,
+ TrafficTreatment action,
+ Set<ConnectPoint> ingressPoints,
+ ConnectPoint egressPoint) {
super(id, match, action);
- checkNotNull(ingressPorts);
- checkArgument(!ingressPorts.isEmpty(),
- "there should be at least one ingress port");
+ checkNotNull(ingressPoints);
+ checkArgument(!ingressPoints.isEmpty(),
+ "there should be at least one ingress port");
- this.ingressPorts = Sets.newHashSet(ingressPorts);
- this.egressPort = checkNotNull(egressPort);
+ this.ingressPoints = Sets.newHashSet(ingressPoints);
+ this.egressPoint = checkNotNull(egressPoint);
}
/**
@@ -52,8 +53,8 @@
*/
protected MultiPointToSinglePointIntent() {
super();
- this.ingressPorts = null;
- this.egressPort = null;
+ this.ingressPoints = null;
+ this.egressPoint = null;
}
/**
@@ -62,8 +63,8 @@
*
* @return set of ingress ports
*/
- public Set<ConnectPoint> getIngressPorts() {
- return ingressPorts;
+ public Set<ConnectPoint> ingressPoints() {
+ return ingressPoints;
}
/**
@@ -71,8 +72,8 @@
*
* @return egress port
*/
- public ConnectPoint getEgressPort() {
- return egressPort;
+ public ConnectPoint egressPoint() {
+ return egressPoint;
}
@Override
@@ -88,23 +89,23 @@
}
MultiPointToSinglePointIntent that = (MultiPointToSinglePointIntent) o;
- return Objects.equals(this.ingressPorts, that.ingressPorts)
- && Objects.equals(this.egressPort, that.egressPort);
+ return Objects.equals(this.ingressPoints, that.ingressPoints)
+ && Objects.equals(this.egressPoint, that.egressPoint);
}
@Override
public int hashCode() {
- return Objects.hash(super.hashCode(), ingressPorts, egressPort);
+ return Objects.hash(super.hashCode(), ingressPoints, egressPoint);
}
@Override
public String toString() {
return MoreObjects.toStringHelper(getClass())
- .add("id", getId())
- .add("match", getTrafficSelector())
- .add("action", getTrafficTreatment())
- .add("ingressPorts", getIngressPorts())
- .add("egressPort", getEgressPort())
+ .add("id", id())
+ .add("match", selector())
+ .add("action", treatment())
+ .add("ingressPoints", ingressPoints())
+ .add("egressPoint", egressPoint())
.toString();
}
}
diff --git a/core/api/src/main/java/org/onlab/onos/net/intent/OpticalConnectivityIntent.java b/core/api/src/main/java/org/onlab/onos/net/intent/OpticalConnectivityIntent.java
index d11dc7c..b99eb70 100644
--- a/core/api/src/main/java/org/onlab/onos/net/intent/OpticalConnectivityIntent.java
+++ b/core/api/src/main/java/org/onlab/onos/net/intent/OpticalConnectivityIntent.java
@@ -3,30 +3,30 @@
import org.onlab.onos.net.ConnectPoint;
// TODO: consider if this intent should be sub-class of ConnectivityIntent
+
/**
* An optical layer Intent for a connectivity from a transponder port to another
* transponder port.
- * <p>
+ * <p/>
* This class doesn't accepts lambda specifier. This class computes path between
* ports and assign lambda automatically. The lambda can be specified using
* OpticalPathFlow class.
*/
public class OpticalConnectivityIntent extends AbstractIntent {
- protected ConnectPoint srcConnectPoint;
- protected ConnectPoint dstConnectPoint;
+ protected ConnectPoint src;
+ protected ConnectPoint dst;
/**
* Constructor.
*
- * @param id ID for this new Intent object.
- * @param srcConnectPoint The source transponder port.
- * @param dstConnectPoint The destination transponder port.
+ * @param id ID for this new Intent object.
+ * @param src The source transponder port.
+ * @param dst The destination transponder port.
*/
- public OpticalConnectivityIntent(IntentId id,
- ConnectPoint srcConnectPoint, ConnectPoint dstConnectPoint) {
+ public OpticalConnectivityIntent(IntentId id, ConnectPoint src, ConnectPoint dst) {
super(id);
- this.srcConnectPoint = srcConnectPoint;
- this.dstConnectPoint = dstConnectPoint;
+ this.src = src;
+ this.dst = dst;
}
/**
@@ -34,8 +34,8 @@
*/
protected OpticalConnectivityIntent() {
super();
- this.srcConnectPoint = null;
- this.dstConnectPoint = null;
+ this.src = null;
+ this.dst = null;
}
/**
@@ -44,7 +44,7 @@
* @return The source transponder port.
*/
public ConnectPoint getSrcConnectPoint() {
- return srcConnectPoint;
+ return src;
}
/**
@@ -52,7 +52,7 @@
*
* @return The source transponder port.
*/
- public ConnectPoint getDstConnectPoint() {
- return dstConnectPoint;
+ public ConnectPoint getDst() {
+ return dst;
}
}
diff --git a/core/api/src/main/java/org/onlab/onos/net/intent/PathIntent.java b/core/api/src/main/java/org/onlab/onos/net/intent/PathIntent.java
index 39ad011..ff2e917 100644
--- a/core/api/src/main/java/org/onlab/onos/net/intent/PathIntent.java
+++ b/core/api/src/main/java/org/onlab/onos/net/intent/PathIntent.java
@@ -1,18 +1,19 @@
package org.onlab.onos.net.intent;
-import java.util.Objects;
-
+import com.google.common.base.MoreObjects;
import org.onlab.onos.net.ConnectPoint;
+import org.onlab.onos.net.Link;
import org.onlab.onos.net.Path;
import org.onlab.onos.net.flow.TrafficSelector;
import org.onlab.onos.net.flow.TrafficTreatment;
-import com.google.common.base.MoreObjects;
+import java.util.Collection;
+import java.util.Objects;
/**
* Abstraction of explicitly path specified connectivity intent.
*/
-public class PathIntent extends PointToPointIntent {
+public class PathIntent extends PointToPointIntent implements InstallableIntent {
private final Path path;
@@ -45,7 +46,7 @@
*
* @return traversed links
*/
- public Path getPath() {
+ public Path path() {
return path;
}
@@ -78,12 +79,18 @@
@Override
public String toString() {
return MoreObjects.toStringHelper(getClass())
- .add("id", getId())
- .add("match", getTrafficSelector())
- .add("action", getTrafficTreatment())
- .add("ingressPort", getIngressPort())
- .add("egressPort", getEgressPort())
+ .add("id", id())
+ .add("match", selector())
+ .add("action", treatment())
+ .add("ingressPort", ingressPoint())
+ .add("egressPort", egressPoint())
.add("path", path)
.toString();
}
+
+ @Override
+ public Collection<Link> requiredLinks() {
+ return path.links();
+ }
+
}
diff --git a/core/api/src/main/java/org/onlab/onos/net/intent/PointToPointIntent.java b/core/api/src/main/java/org/onlab/onos/net/intent/PointToPointIntent.java
index b1d18ee..7b7c18a 100644
--- a/core/api/src/main/java/org/onlab/onos/net/intent/PointToPointIntent.java
+++ b/core/api/src/main/java/org/onlab/onos/net/intent/PointToPointIntent.java
@@ -1,39 +1,40 @@
package org.onlab.onos.net.intent;
-import static com.google.common.base.Preconditions.checkNotNull;
-
-import java.util.Objects;
-
+import com.google.common.base.MoreObjects;
import org.onlab.onos.net.ConnectPoint;
import org.onlab.onos.net.flow.TrafficSelector;
import org.onlab.onos.net.flow.TrafficTreatment;
-import com.google.common.base.MoreObjects;
+import java.util.Objects;
+
+import static com.google.common.base.Preconditions.checkNotNull;
/**
* Abstraction of point-to-point connectivity.
*/
public class PointToPointIntent extends ConnectivityIntent {
- private final ConnectPoint ingressPort;
- private final ConnectPoint egressPort;
+ private final ConnectPoint ingressPoint;
+ private final ConnectPoint egressPoint;
/**
* Creates a new point-to-point intent with the supplied ingress/egress
* ports.
*
- * @param id intent identifier
- * @param match traffic match
- * @param action action
- * @param ingressPort ingress port
- * @param egressPort egress port
- * @throws NullPointerException if {@code ingressPort} or {@code egressPort} is null.
+ * @param id intent identifier
+ * @param selector traffic selector
+ * @param treatment treatment
+ * @param ingressPoint ingress port
+ * @param egressPoint egress port
+ * @throws NullPointerException if {@code ingressPoint} or {@code egressPoints} is null.
*/
- public PointToPointIntent(IntentId id, TrafficSelector match, TrafficTreatment action,
- ConnectPoint ingressPort, ConnectPoint egressPort) {
- super(id, match, action);
- this.ingressPort = checkNotNull(ingressPort);
- this.egressPort = checkNotNull(egressPort);
+ public PointToPointIntent(IntentId id, TrafficSelector selector,
+ TrafficTreatment treatment,
+ ConnectPoint ingressPoint,
+ ConnectPoint egressPoint) {
+ super(id, selector, treatment);
+ this.ingressPoint = checkNotNull(ingressPoint);
+ this.egressPoint = checkNotNull(egressPoint);
}
/**
@@ -41,8 +42,8 @@
*/
protected PointToPointIntent() {
super();
- this.ingressPort = null;
- this.egressPort = null;
+ this.ingressPoint = null;
+ this.egressPoint = null;
}
/**
@@ -51,8 +52,8 @@
*
* @return ingress port
*/
- public ConnectPoint getIngressPort() {
- return ingressPort;
+ public ConnectPoint ingressPoint() {
+ return ingressPoint;
}
/**
@@ -60,8 +61,8 @@
*
* @return egress port
*/
- public ConnectPoint getEgressPort() {
- return egressPort;
+ public ConnectPoint egressPoint() {
+ return egressPoint;
}
@Override
@@ -77,23 +78,23 @@
}
PointToPointIntent that = (PointToPointIntent) o;
- return Objects.equals(this.ingressPort, that.ingressPort)
- && Objects.equals(this.egressPort, that.egressPort);
+ return Objects.equals(this.ingressPoint, that.ingressPoint)
+ && Objects.equals(this.egressPoint, that.egressPoint);
}
@Override
public int hashCode() {
- return Objects.hash(super.hashCode(), ingressPort, egressPort);
+ return Objects.hash(super.hashCode(), ingressPoint, egressPoint);
}
@Override
public String toString() {
return MoreObjects.toStringHelper(getClass())
- .add("id", getId())
- .add("match", getTrafficSelector())
- .add("action", getTrafficTreatment())
- .add("ingressPort", ingressPort)
- .add("egressPort", egressPort)
+ .add("id", id())
+ .add("match", selector())
+ .add("action", treatment())
+ .add("ingressPoint", ingressPoint)
+ .add("egressPoints", egressPoint)
.toString();
}
diff --git a/core/api/src/main/java/org/onlab/onos/net/intent/SinglePointToMultiPointIntent.java b/core/api/src/main/java/org/onlab/onos/net/intent/SinglePointToMultiPointIntent.java
index e69a740..2a17bfe 100644
--- a/core/api/src/main/java/org/onlab/onos/net/intent/SinglePointToMultiPointIntent.java
+++ b/core/api/src/main/java/org/onlab/onos/net/intent/SinglePointToMultiPointIntent.java
@@ -1,50 +1,50 @@
package org.onlab.onos.net.intent;
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.base.Preconditions.checkNotNull;
-
-import java.util.Objects;
-import java.util.Set;
-
+import com.google.common.base.MoreObjects;
+import com.google.common.collect.Sets;
import org.onlab.onos.net.ConnectPoint;
import org.onlab.onos.net.flow.TrafficSelector;
import org.onlab.onos.net.flow.TrafficTreatment;
-import com.google.common.base.MoreObjects;
-import com.google.common.collect.Sets;
+import java.util.Objects;
+import java.util.Set;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
/**
* Abstraction of single source, multiple destination connectivity intent.
*/
public class SinglePointToMultiPointIntent extends ConnectivityIntent {
- private final ConnectPoint ingressPort;
- private final Set<ConnectPoint> egressPorts;
+ private final ConnectPoint ingressPoint;
+ private final Set<ConnectPoint> egressPoints;
/**
* Creates a new single-to-multi point connectivity intent.
*
- * @param id intent identifier
- * @param match traffic match
- * @param action action
- * @param ingressPort port on which traffic will ingress
- * @param egressPorts set of ports on which traffic will egress
- * @throws NullPointerException if {@code ingressPort} or
- * {@code egressPorts} is null
- * @throws IllegalArgumentException if the size of {@code egressPorts} is
- * not more than 1
+ * @param id intent identifier
+ * @param selector traffic selector
+ * @param treatment treatment
+ * @param ingressPoint port on which traffic will ingress
+ * @param egressPoints set of ports on which traffic will egress
+ * @throws NullPointerException if {@code ingressPoint} or
+ * {@code egressPoints} is null
+ * @throws IllegalArgumentException if the size of {@code egressPoints} is
+ * not more than 1
*/
- public SinglePointToMultiPointIntent(IntentId id, TrafficSelector match, TrafficTreatment action,
- ConnectPoint ingressPort,
- Set<ConnectPoint> egressPorts) {
- super(id, match, action);
+ public SinglePointToMultiPointIntent(IntentId id, TrafficSelector selector,
+ TrafficTreatment treatment,
+ ConnectPoint ingressPoint,
+ Set<ConnectPoint> egressPoints) {
+ super(id, selector, treatment);
- checkNotNull(egressPorts);
- checkArgument(!egressPorts.isEmpty(),
- "there should be at least one egress port");
+ checkNotNull(egressPoints);
+ checkArgument(!egressPoints.isEmpty(),
+ "there should be at least one egress port");
- this.ingressPort = checkNotNull(ingressPort);
- this.egressPorts = Sets.newHashSet(egressPorts);
+ this.ingressPoint = checkNotNull(ingressPoint);
+ this.egressPoints = Sets.newHashSet(egressPoints);
}
/**
@@ -52,8 +52,8 @@
*/
protected SinglePointToMultiPointIntent() {
super();
- this.ingressPort = null;
- this.egressPorts = null;
+ this.ingressPoint = null;
+ this.egressPoints = null;
}
/**
@@ -61,8 +61,8 @@
*
* @return ingress port
*/
- public ConnectPoint getIngressPort() {
- return ingressPort;
+ public ConnectPoint ingressPoint() {
+ return ingressPoint;
}
/**
@@ -70,8 +70,8 @@
*
* @return set of egress ports
*/
- public Set<ConnectPoint> getEgressPorts() {
- return egressPorts;
+ public Set<ConnectPoint> egressPoints() {
+ return egressPoints;
}
@Override
@@ -87,23 +87,23 @@
}
SinglePointToMultiPointIntent that = (SinglePointToMultiPointIntent) o;
- return Objects.equals(this.ingressPort, that.ingressPort)
- && Objects.equals(this.egressPorts, that.egressPorts);
+ return Objects.equals(this.ingressPoint, that.ingressPoint)
+ && Objects.equals(this.egressPoints, that.egressPoints);
}
@Override
public int hashCode() {
- return Objects.hash(super.hashCode(), ingressPort, egressPorts);
+ return Objects.hash(super.hashCode(), ingressPoint, egressPoints);
}
@Override
public String toString() {
return MoreObjects.toStringHelper(getClass())
- .add("id", getId())
- .add("match", getTrafficSelector())
- .add("action", getTrafficTreatment())
- .add("ingressPort", ingressPort)
- .add("egressPort", egressPorts)
+ .add("id", id())
+ .add("match", selector())
+ .add("action", treatment())
+ .add("ingressPoint", ingressPoint)
+ .add("egressPort", egressPoints)
.toString();
}
diff --git a/core/api/src/main/java/org/onlab/onos/net/intent/package-info.java b/core/api/src/main/java/org/onlab/onos/net/intent/package-info.java
index e1e6782..ff97f5b 100644
--- a/core/api/src/main/java/org/onlab/onos/net/intent/package-info.java
+++ b/core/api/src/main/java/org/onlab/onos/net/intent/package-info.java
@@ -1,5 +1,56 @@
/**
- * Intent Package. TODO
+ * Set of abstractions for conveying high-level intents for treatment of
+ * selected network traffic by allowing applications to express the
+ * <em>what</em> rather than the <em>how</em>. This makes such instructions
+ * largely independent of topology and device specifics, thus allowing them to
+ * survive topology mutations.
+ * <p/>
+ * The controller core provides a suite of built-in intents and their compilers
+ * and installers. However, the intent framework is extensible in that it allows
+ * additional intents and their compilers or installers to be added
+ * dynamically at run-time. This allows others to enhance the initial arsenal of
+ * connectivity and policy-based intents available in base controller software.
+ * <p/>
+ * The following diagram depicts the state transition diagram for each top-level intent:<br>
+ * <img src="doc-files/intent-states.png" alt="ONOS intent states">
+ * <p/>
+ * The controller core accepts the intent specifications and translates them, via a
+ * process referred to as intent compilation, to installable intents, which are
+ * essentially actionable operations on the network environment.
+ * These actions are carried out by intent installation process, which results
+ * in some changes to the environment, e.g. tunnel links being provisioned,
+ * flow rules being installed on the data-plane, optical lambdas being reserved.
+ * <p/>
+ * After an intent is submitted by an application, it will be sent immediately
+ * (but asynchronously) into a compiling phase, then to installing phase and if
+ * all goes according to plan into installed state. Once an application decides
+ * it no longer wishes the intent to hold, it can withdraw it. This describes
+ * the nominal flow. However, it may happen that some issue is encountered.
+ * For example, an application may ask for an objective that is not currently
+ * achievable, e.g. connectivity across to unconnected network segments.
+ * If this is the case, the compiling phase may fail to produce a set of
+ * installable intents and instead result in a failed compile. If this occurs,
+ * only a change in the environment can trigger a transition back to the
+ * compiling state.
+ * <p/>
+ * Similarly, an issue may be encountered during the installation phase in
+ * which case the framework will attempt to recompile the intent to see if an
+ * alternate approach is available. If so, the intent will be sent back to
+ * installing phase. Otherwise, it will be parked in the failed state. Another
+ * scenario that’s very likely to be encountered is where the intent is
+ * successfully compiled and installed, but due to some topology event, such
+ * as a downed or downgraded link, loss of throughput may occur or connectivity
+ * may be lost altogether, thus impacting the viability of a previously
+ * satisfied intent. If this occurs, the framework will attempt to recompile
+ * the intent, and if an alternate approach is available, its installation
+ * will be attempted. Otherwise, the original top-level intent will be parked
+ * in the failed state.
+ * <p/>
+ * Please note that all *ing states, depicted in orange, are transitional and
+ * are expected to last only a brief amount of time. The rest of the states
+ * are parking states where the intent may spent some time; except for the
+ * submitted state of course. There, the intent may pause, but only briefly,
+ * while the system determines where to perform the compilation or while it
+ * performs global recomputation/optimization across all prior intents.
*/
-
package org.onlab.onos.net.intent;
\ No newline at end of file
diff --git a/core/api/src/main/java/org/onlab/onos/net/link/LinkDescription.java b/core/api/src/main/java/org/onlab/onos/net/link/LinkDescription.java
index b1be82c..4b34ad6 100644
--- a/core/api/src/main/java/org/onlab/onos/net/link/LinkDescription.java
+++ b/core/api/src/main/java/org/onlab/onos/net/link/LinkDescription.java
@@ -1,12 +1,13 @@
package org.onlab.onos.net.link;
import org.onlab.onos.net.ConnectPoint;
+import org.onlab.onos.net.Description;
import org.onlab.onos.net.Link;
/**
* Describes an infrastructure link.
*/
-public interface LinkDescription {
+public interface LinkDescription extends Description {
/**
* Returns the link source.
diff --git a/core/api/src/main/java/org/onlab/onos/net/packet/DefaultPacketContext.java b/core/api/src/main/java/org/onlab/onos/net/packet/DefaultPacketContext.java
index 6f1b708..75100c6 100644
--- a/core/api/src/main/java/org/onlab/onos/net/packet/DefaultPacketContext.java
+++ b/core/api/src/main/java/org/onlab/onos/net/packet/DefaultPacketContext.java
@@ -24,7 +24,7 @@
this.inPkt = inPkt;
this.outPkt = outPkt;
this.block = new AtomicBoolean(block);
- this.builder = new DefaultTrafficTreatment.Builder();
+ this.builder = DefaultTrafficTreatment.builder();
}
@Override
diff --git a/core/api/src/main/java/org/onlab/onos/net/provider/ProviderId.java b/core/api/src/main/java/org/onlab/onos/net/provider/ProviderId.java
index afaecbe..c2a3133 100644
--- a/core/api/src/main/java/org/onlab/onos/net/provider/ProviderId.java
+++ b/core/api/src/main/java/org/onlab/onos/net/provider/ProviderId.java
@@ -3,6 +3,7 @@
import java.util.Objects;
import static com.google.common.base.MoreObjects.toStringHelper;
+import static com.google.common.base.Preconditions.checkNotNull;
/**
* External identity of a {@link org.onlab.onos.net.provider.Provider} family.
@@ -19,10 +20,22 @@
*/
public class ProviderId {
+ /**
+ * Represents no provider ID.
+ */
+ public static final ProviderId NONE = new ProviderId();
+
private final String scheme;
private final String id;
private final boolean ancillary;
+ // For serialization
+ private ProviderId() {
+ scheme = null;
+ id = null;
+ ancillary = false;
+ }
+
/**
* Creates a new primary provider identifier from the specified string.
* The providers are expected to follow the reverse DNS convention, e.g.
@@ -45,8 +58,8 @@
* @param ancillary ancillary provider indicator
*/
public ProviderId(String scheme, String id, boolean ancillary) {
- this.scheme = scheme;
- this.id = id;
+ this.scheme = checkNotNull(scheme, "Scheme cannot be null");
+ this.id = checkNotNull(id, "ID cannot be null");
this.ancillary = ancillary;
}
diff --git a/core/api/src/main/java/org/onlab/onos/net/proxyarp/ProxyArpService.java b/core/api/src/main/java/org/onlab/onos/net/proxyarp/ProxyArpService.java
index 4ee43c7..77d1208 100644
--- a/core/api/src/main/java/org/onlab/onos/net/proxyarp/ProxyArpService.java
+++ b/core/api/src/main/java/org/onlab/onos/net/proxyarp/ProxyArpService.java
@@ -1,5 +1,6 @@
package org.onlab.onos.net.proxyarp;
+import org.onlab.onos.net.packet.PacketContext;
import org.onlab.packet.Ethernet;
import org.onlab.packet.IpPrefix;
@@ -33,4 +34,12 @@
*/
void forward(Ethernet eth);
+ /**
+ * Handles a arp packet.
+ * Replies to arp requests and forwards request to the right place.
+ * @param context the packet context to handle
+ * @return true if handled, false otherwise.
+ */
+ boolean handleArp(PacketContext context);
+
}
diff --git a/core/api/src/main/java/org/onlab/onos/net/topology/TopologyEvent.java b/core/api/src/main/java/org/onlab/onos/net/topology/TopologyEvent.java
index 0be5323..268b6ac 100644
--- a/core/api/src/main/java/org/onlab/onos/net/topology/TopologyEvent.java
+++ b/core/api/src/main/java/org/onlab/onos/net/topology/TopologyEvent.java
@@ -1,12 +1,17 @@
package org.onlab.onos.net.topology;
import org.onlab.onos.event.AbstractEvent;
+import org.onlab.onos.event.Event;
+
+import java.util.List;
/**
* Describes network topology event.
*/
public class TopologyEvent extends AbstractEvent<TopologyEvent.Type, Topology> {
+ private final List<Event> reasons;
+
/**
* Type of topology events.
*/
@@ -23,9 +28,11 @@
*
* @param type topology event type
* @param topology event topology subject
+ * @param reasons list of events that triggered topology change
*/
- public TopologyEvent(Type type, Topology topology) {
+ public TopologyEvent(Type type, Topology topology, List<Event> reasons) {
super(type, topology);
+ this.reasons = reasons;
}
/**
@@ -33,10 +40,24 @@
*
* @param type link event type
* @param topology event topology subject
+ * @param reasons list of events that triggered topology change
* @param time occurrence time
*/
- public TopologyEvent(Type type, Topology topology, long time) {
+ public TopologyEvent(Type type, Topology topology, List<Event> reasons,
+ long time) {
super(type, topology, time);
+ this.reasons = reasons;
+ }
+
+
+ /**
+ * Returns the list of events that triggered the topology change.
+ *
+ * @return list of events responsible for change in topology; null if
+ * initial topology computation
+ */
+ public List<Event> reasons() {
+ return reasons;
}
}
diff --git a/core/api/src/main/java/org/onlab/onos/store/ClockProviderService.java b/core/api/src/main/java/org/onlab/onos/store/ClockProviderService.java
new file mode 100644
index 0000000..a5f81c7
--- /dev/null
+++ b/core/api/src/main/java/org/onlab/onos/store/ClockProviderService.java
@@ -0,0 +1,20 @@
+package org.onlab.onos.store;
+
+import org.onlab.onos.cluster.MastershipTerm;
+import org.onlab.onos.net.DeviceId;
+
+//TODO: Consider renaming to DeviceClockProviderService?
+/**
+* Interface for feeding term information to a logical clock service
+* that vends per device timestamps.
+*/
+public interface ClockProviderService {
+
+ /**
+ * Updates the mastership term for the specified deviceId.
+ *
+ * @param deviceId device identifier.
+ * @param term mastership term.
+ */
+ public void setMastershipTerm(DeviceId deviceId, MastershipTerm term);
+}
diff --git a/core/api/src/main/java/org/onlab/onos/store/ClockService.java b/core/api/src/main/java/org/onlab/onos/store/ClockService.java
index 2446ab7..20549e8 100644
--- a/core/api/src/main/java/org/onlab/onos/store/ClockService.java
+++ b/core/api/src/main/java/org/onlab/onos/store/ClockService.java
@@ -1,6 +1,5 @@
package org.onlab.onos.store;
-import org.onlab.onos.cluster.MastershipTerm;
import org.onlab.onos.net.DeviceId;
// TODO: Consider renaming to DeviceClockService?
@@ -15,12 +14,4 @@
* @return timestamp.
*/
public Timestamp getTimestamp(DeviceId deviceId);
-
- // TODO: Should this be here or separate as Admin service, etc.?
- /**
- * Updates the mastership term for the specified deviceId.
- * @param deviceId device identifier.
- * @param term mastership term.
- */
- public void setMastershipTerm(DeviceId deviceId, MastershipTerm term);
}
diff --git a/core/api/src/main/java/org/onlab/onos/store/Timestamp.java b/core/api/src/main/java/org/onlab/onos/store/Timestamp.java
index b9d3648..b3caf85 100644
--- a/core/api/src/main/java/org/onlab/onos/store/Timestamp.java
+++ b/core/api/src/main/java/org/onlab/onos/store/Timestamp.java
@@ -2,7 +2,15 @@
/**
* Opaque version structure.
+ * <p>
+ * Classes implementing this interface must also implement
+ * {@link #hashCode()} and {@link #equals(Object)}.
*/
public interface Timestamp extends Comparable<Timestamp> {
+ @Override
+ public abstract int hashCode();
+
+ @Override
+ public abstract boolean equals(Object obj);
}
diff --git a/core/api/src/main/javadoc/org/onlab/onos/net/intent/doc-files/intent-states.png b/core/api/src/main/javadoc/org/onlab/onos/net/intent/doc-files/intent-states.png
new file mode 100644
index 0000000..9a3e1dc
--- /dev/null
+++ b/core/api/src/main/javadoc/org/onlab/onos/net/intent/doc-files/intent-states.png
Binary files differ
diff --git a/core/api/src/test/java/org/onlab/onos/VersionTest.java b/core/api/src/test/java/org/onlab/onos/VersionTest.java
new file mode 100644
index 0000000..e357f9d
--- /dev/null
+++ b/core/api/src/test/java/org/onlab/onos/VersionTest.java
@@ -0,0 +1,50 @@
+package org.onlab.onos;
+
+import com.google.common.testing.EqualsTester;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+import static org.onlab.onos.Version.version;
+
+/**
+ * Tests of the version descriptor.
+ */
+public class VersionTest {
+
+ @Test
+ public void fromParts() {
+ Version v = version(1, 2, 3, "4321");
+ assertEquals("wrong major", 1, v.major());
+ assertEquals("wrong minor", 2, v.minor());
+ assertEquals("wrong patch", 3, v.patch());
+ assertEquals("wrong build", "4321", v.build());
+ }
+
+ @Test
+ public void fromString() {
+ Version v = version("1.2.3.4321");
+ assertEquals("wrong major", 1, v.major());
+ assertEquals("wrong minor", 2, v.minor());
+ assertEquals("wrong patch", 3, v.patch());
+ assertEquals("wrong build", "4321", v.build());
+ }
+
+ @Test
+ public void snapshot() {
+ Version v = version("1.2.3-SNAPSHOT");
+ assertEquals("wrong major", 1, v.major());
+ assertEquals("wrong minor", 2, v.minor());
+ assertEquals("wrong patch", 3, v.patch());
+ assertEquals("wrong build", "SNAPSHOT", v.build());
+ }
+
+ @Test
+ public void testEquals() {
+ new EqualsTester()
+ .addEqualityGroup(version("1.2.3.4321"), version(1, 2, 3, "4321"))
+ .addEqualityGroup(version("1.9.3.4321"), version(1, 9, 3, "4321"))
+ .addEqualityGroup(version("1.2.8.4321"), version(1, 2, 8, "4321"))
+ .addEqualityGroup(version("1.2.3.x"), version(1, 2, 3, "x"))
+ .testEquals();
+ }
+}
\ No newline at end of file
diff --git a/core/api/src/test/java/org/onlab/onos/net/ConnectPointTest.java b/core/api/src/test/java/org/onlab/onos/net/ConnectPointTest.java
index 6d3e793..6b9f777 100644
--- a/core/api/src/test/java/org/onlab/onos/net/ConnectPointTest.java
+++ b/core/api/src/test/java/org/onlab/onos/net/ConnectPointTest.java
@@ -33,4 +33,4 @@
.addEqualityGroup(new ConnectPoint(DID2, P1), new ConnectPoint(DID2, P1))
.testEquals();
}
-}
\ No newline at end of file
+}
diff --git a/core/api/src/test/java/org/onlab/onos/net/DefaultAnnotationsTest.java b/core/api/src/test/java/org/onlab/onos/net/DefaultAnnotationsTest.java
index 274f4b8..9132126 100644
--- a/core/api/src/test/java/org/onlab/onos/net/DefaultAnnotationsTest.java
+++ b/core/api/src/test/java/org/onlab/onos/net/DefaultAnnotationsTest.java
@@ -36,6 +36,23 @@
}
@Test
+ public void union() {
+ annotations = builder().set("foo", "1").set("bar", "2").remove("buz").build();
+ assertEquals("incorrect keys", of("foo", "bar", "buz"), annotations.keys());
+
+ SparseAnnotations updates = builder().remove("foo").set("bar", "3").set("goo", "4").remove("fuzz").build();
+
+ SparseAnnotations result = DefaultAnnotations.union(annotations, updates);
+
+ assertTrue("remove instruction in original remains", result.isRemoved("buz"));
+ assertTrue("remove instruction in update remains", result.isRemoved("fuzz"));
+ assertEquals("incorrect keys", of("buz", "goo", "bar", "fuzz"), result.keys());
+ assertNull("incorrect value", result.value("foo"));
+ assertEquals("incorrect value", "3", result.value("bar"));
+ assertEquals("incorrect value", "4", result.value("goo"));
+ }
+
+ @Test
public void merge() {
annotations = builder().set("foo", "1").set("bar", "2").build();
assertEquals("incorrect keys", of("foo", "bar"), annotations.keys());
@@ -65,4 +82,4 @@
DefaultAnnotations.merge(null, null);
}
-}
\ No newline at end of file
+}
diff --git a/core/api/src/test/java/org/onlab/onos/net/DefaultDeviceTest.java b/core/api/src/test/java/org/onlab/onos/net/DefaultDeviceTest.java
index b4018e3..329e128 100644
--- a/core/api/src/test/java/org/onlab/onos/net/DefaultDeviceTest.java
+++ b/core/api/src/test/java/org/onlab/onos/net/DefaultDeviceTest.java
@@ -58,7 +58,5 @@
assertEquals("incorrect hw", HW, device.hwVersion());
assertEquals("incorrect sw", SW, device.swVersion());
assertEquals("incorrect serial", SN1, device.serialNumber());
- assertEquals("incorrect serial", SN1, device.serialNumber());
}
-
-}
\ No newline at end of file
+}
diff --git a/core/api/src/test/java/org/onlab/onos/net/DefaultEdgeLinkTest.java b/core/api/src/test/java/org/onlab/onos/net/DefaultEdgeLinkTest.java
index b3891f1..fd63797 100644
--- a/core/api/src/test/java/org/onlab/onos/net/DefaultEdgeLinkTest.java
+++ b/core/api/src/test/java/org/onlab/onos/net/DefaultEdgeLinkTest.java
@@ -5,6 +5,7 @@
import org.onlab.onos.net.provider.ProviderId;
import static org.junit.Assert.assertEquals;
+import static org.onlab.onos.net.DefaultEdgeLink.createEdgeLink;
import static org.onlab.onos.net.DefaultLinkTest.cp;
import static org.onlab.onos.net.DeviceId.deviceId;
import static org.onlab.onos.net.HostId.hostId;
@@ -55,4 +56,24 @@
assertEquals("incorrect time", 123L, link.hostLocation().time());
}
+ @Test
+ public void phantomIngress() {
+ HostLocation hostLocation = new HostLocation(DID1, P1, 123L);
+ EdgeLink link = createEdgeLink(hostLocation, true);
+ assertEquals("incorrect dst", hostLocation, link.dst());
+ assertEquals("incorrect type", Link.Type.EDGE, link.type());
+ assertEquals("incorrect connect point", hostLocation, link.hostLocation());
+ assertEquals("incorrect time", 123L, link.hostLocation().time());
+ }
+
+ @Test
+ public void phantomEgress() {
+ ConnectPoint hostLocation = new ConnectPoint(DID1, P1);
+ EdgeLink link = createEdgeLink(hostLocation, false);
+ assertEquals("incorrect src", hostLocation, link.src());
+ assertEquals("incorrect type", Link.Type.EDGE, link.type());
+ assertEquals("incorrect connect point", hostLocation, link.hostLocation());
+ assertEquals("incorrect time", 0L, link.hostLocation().time());
+ }
+
}
diff --git a/core/api/src/test/java/org/onlab/onos/net/device/DeviceEventTest.java b/core/api/src/test/java/org/onlab/onos/net/device/DeviceEventTest.java
index df19365..9c45b96 100644
--- a/core/api/src/test/java/org/onlab/onos/net/device/DeviceEventTest.java
+++ b/core/api/src/test/java/org/onlab/onos/net/device/DeviceEventTest.java
@@ -39,7 +39,7 @@
Device device = createDevice();
Port port = new DefaultPort(device, PortNumber.portNumber(123), true);
long before = System.currentTimeMillis();
- DeviceEvent event = new DeviceEvent(DeviceEvent.Type.DEVICE_ADDED, device);
+ DeviceEvent event = new DeviceEvent(DeviceEvent.Type.DEVICE_ADDED, device, port);
long after = System.currentTimeMillis();
validateEvent(event, DeviceEvent.Type.DEVICE_ADDED, device, before, after);
}
diff --git a/core/api/src/test/java/org/onlab/onos/net/intent/ConnectivityIntentTest.java b/core/api/src/test/java/org/onlab/onos/net/intent/ConnectivityIntentTest.java
index fb1efee..10a0069 100644
--- a/core/api/src/test/java/org/onlab/onos/net/intent/ConnectivityIntentTest.java
+++ b/core/api/src/test/java/org/onlab/onos/net/intent/ConnectivityIntentTest.java
@@ -16,8 +16,8 @@
public abstract class ConnectivityIntentTest extends IntentTest {
public static final IntentId IID = new IntentId(123);
- public static final TrafficSelector MATCH = (new DefaultTrafficSelector.Builder()).build();
- public static final TrafficTreatment NOP = (new DefaultTrafficTreatment.Builder()).build();
+ public static final TrafficSelector MATCH = DefaultTrafficSelector.builder().build();
+ public static final TrafficTreatment NOP = DefaultTrafficTreatment.builder().build();
public static final ConnectPoint P1 = new ConnectPoint(DeviceId.deviceId("111"), PortNumber.portNumber(0x1));
public static final ConnectPoint P2 = new ConnectPoint(DeviceId.deviceId("222"), PortNumber.portNumber(0x2));
diff --git a/core/api/src/test/java/org/onlab/onos/net/intent/FakeIntentManager.java b/core/api/src/test/java/org/onlab/onos/net/intent/FakeIntentManager.java
index df46ec5..58c5a9c 100644
--- a/core/api/src/test/java/org/onlab/onos/net/intent/FakeIntentManager.java
+++ b/core/api/src/test/java/org/onlab/onos/net/intent/FakeIntentManager.java
@@ -11,19 +11,19 @@
import java.util.concurrent.Executors;
/**
- * Fake implementation of the intent service to assist in developing tests
- * of the interface contract.
+ * Fake implementation of the intent service to assist in developing tests of
+ * the interface contract.
*/
public class FakeIntentManager implements TestableIntentService {
private final Map<IntentId, Intent> intents = new HashMap<>();
private final Map<IntentId, IntentState> intentStates = new HashMap<>();
private final Map<IntentId, List<InstallableIntent>> installables = new HashMap<>();
- private final Set<IntentEventListener> listeners = new HashSet<>();
+ private final Set<IntentListener> listeners = new HashSet<>();
private final Map<Class<? extends Intent>, IntentCompiler<? extends Intent>> compilers = new HashMap<>();
- private final Map<Class<? extends InstallableIntent>,
- IntentInstaller<? extends InstallableIntent>> installers = new HashMap<>();
+ private final Map<Class<? extends InstallableIntent>, IntentInstaller<? extends InstallableIntent>> installers
+ = new HashMap<>();
private final ExecutorService executor = Executors.newSingleThreadExecutor();
private final List<IntentException> exceptions = new ArrayList<>();
@@ -40,8 +40,7 @@
@Override
public void run() {
try {
- List<InstallableIntent> installable = compileIntent(intent);
- installIntents(intent, installable);
+ executeCompilingPhase(intent);
} catch (IntentException e) {
exceptions.add(e);
}
@@ -55,8 +54,8 @@
@Override
public void run() {
try {
- List<InstallableIntent> installable = getInstallable(intent.getId());
- uninstallIntents(intent, installable);
+ List<InstallableIntent> installable = getInstallable(intent.id());
+ executeWithdrawingPhase(intent, installable);
} catch (IntentException e) {
exceptions.add(e);
}
@@ -76,61 +75,68 @@
private <T extends InstallableIntent> IntentInstaller<T> getInstaller(T intent) {
@SuppressWarnings("unchecked")
- IntentInstaller<T> installer = (IntentInstaller<T>) installers.get(intent.getClass());
+ IntentInstaller<T> installer = (IntentInstaller<T>) installers.get(intent
+ .getClass());
if (installer == null) {
throw new IntentException("no installer for class " + intent.getClass());
}
return installer;
}
- private <T extends Intent> List<InstallableIntent> compileIntent(T intent) {
+ private <T extends Intent> void executeCompilingPhase(T intent) {
+ setState(intent, IntentState.COMPILING);
try {
// For the fake, we compile using a single level pass
List<InstallableIntent> installable = new ArrayList<>();
for (Intent compiled : getCompiler(intent).compile(intent)) {
installable.add((InstallableIntent) compiled);
}
- setState(intent, IntentState.COMPILED);
- return installable;
+ executeInstallingPhase(intent, installable);
+
} catch (IntentException e) {
setState(intent, IntentState.FAILED);
- throw e;
+ dispatch(new IntentEvent(IntentEvent.Type.FAILED, intent));
}
}
- private void installIntents(Intent intent, List<InstallableIntent> installable) {
+ private void executeInstallingPhase(Intent intent,
+ List<InstallableIntent> installable) {
+ setState(intent, IntentState.INSTALLING);
try {
for (InstallableIntent ii : installable) {
registerSubclassInstallerIfNeeded(ii);
getInstaller(ii).install(ii);
}
setState(intent, IntentState.INSTALLED);
- putInstallable(intent.getId(), installable);
+ putInstallable(intent.id(), installable);
+ dispatch(new IntentEvent(IntentEvent.Type.INSTALLED, intent));
+
} catch (IntentException e) {
setState(intent, IntentState.FAILED);
- throw e;
+ dispatch(new IntentEvent(IntentEvent.Type.FAILED, intent));
}
}
- private void uninstallIntents(Intent intent, List<InstallableIntent> installable) {
+ private void executeWithdrawingPhase(Intent intent,
+ List<InstallableIntent> installable) {
+ setState(intent, IntentState.WITHDRAWING);
try {
for (InstallableIntent ii : installable) {
getInstaller(ii).uninstall(ii);
}
+ removeInstallable(intent.id());
setState(intent, IntentState.WITHDRAWN);
- removeInstallable(intent.getId());
+ dispatch(new IntentEvent(IntentEvent.Type.WITHDRAWN, intent));
} catch (IntentException e) {
+ // FIXME: Rework this to always go from WITHDRAWING to WITHDRAWN!
setState(intent, IntentState.FAILED);
- throw e;
+ dispatch(new IntentEvent(IntentEvent.Type.FAILED, intent));
}
}
-
// Sets the internal state for the given intent and dispatches an event
private void setState(Intent intent, IntentState state) {
- IntentState previous = intentStates.get(intent.getId());
- intentStates.put(intent.getId(), state);
- dispatch(new IntentEvent(intent, state, previous, System.currentTimeMillis()));
+ intentStates.put(intent.id(), state);
}
private void putInstallable(IntentId id, List<InstallableIntent> installable) {
@@ -152,15 +158,15 @@
@Override
public void submit(Intent intent) {
- intents.put(intent.getId(), intent);
+ intents.put(intent.id(), intent);
setState(intent, IntentState.SUBMITTED);
+ dispatch(new IntentEvent(IntentEvent.Type.SUBMITTED, intent));
executeSubmit(intent);
}
@Override
public void withdraw(Intent intent) {
- intents.remove(intent.getId());
- setState(intent, IntentState.WITHDRAWING);
+ intents.remove(intent.id());
executeWithdraw(intent);
}
@@ -175,6 +181,11 @@
}
@Override
+ public long getIntentCount() {
+ return intents.size();
+ }
+
+ @Override
public Intent getIntent(IntentId id) {
return intents.get(id);
}
@@ -185,23 +196,24 @@
}
@Override
- public void addListener(IntentEventListener listener) {
+ public void addListener(IntentListener listener) {
listeners.add(listener);
}
@Override
- public void removeListener(IntentEventListener listener) {
+ public void removeListener(IntentListener listener) {
listeners.remove(listener);
}
private void dispatch(IntentEvent event) {
- for (IntentEventListener listener : listeners) {
+ for (IntentListener listener : listeners) {
listener.event(event);
}
}
@Override
- public <T extends Intent> void registerCompiler(Class<T> cls, IntentCompiler<T> compiler) {
+ public <T extends Intent> void registerCompiler(Class<T> cls,
+ IntentCompiler<T> compiler) {
compilers.put(cls, compiler);
}
@@ -216,7 +228,8 @@
}
@Override
- public <T extends InstallableIntent> void registerInstaller(Class<T> cls, IntentInstaller<T> installer) {
+ public <T extends InstallableIntent> void registerInstaller(Class<T> cls,
+ IntentInstaller<T> installer) {
installers.put(cls, installer);
}
@@ -227,7 +240,7 @@
@Override
public Map<Class<? extends InstallableIntent>,
- IntentInstaller<? extends InstallableIntent>> getInstallers() {
+ IntentInstaller<? extends InstallableIntent>> getInstallers() {
return Collections.unmodifiableMap(installers);
}
@@ -252,7 +265,8 @@
if (!installers.containsKey(intent.getClass())) {
Class<?> cls = intent.getClass();
while (cls != Object.class) {
- // As long as we're within the InstallableIntent class descendants
+ // As long as we're within the InstallableIntent class
+ // descendants
if (InstallableIntent.class.isAssignableFrom(cls)) {
IntentInstaller<?> installer = installers.get(cls);
if (installer != null) {
diff --git a/core/api/src/test/java/org/onlab/onos/net/intent/IntentServiceTest.java b/core/api/src/test/java/org/onlab/onos/net/intent/IntentServiceTest.java
index c7682b1..7eb0e19 100644
--- a/core/api/src/test/java/org/onlab/onos/net/intent/IntentServiceTest.java
+++ b/core/api/src/test/java/org/onlab/onos/net/intent/IntentServiceTest.java
@@ -10,11 +10,9 @@
import java.util.Iterator;
import java.util.List;
-import static org.onlab.onos.net.intent.IntentState.*;
import static org.junit.Assert.*;
+import static org.onlab.onos.net.intent.IntentEvent.Type.*;
-// TODO: consider make it categorized as integration test when it become
-// slow test or fragile test
/**
* Suite of tests for the intent service contract.
*/
@@ -51,7 +49,7 @@
@Test
public void basics() {
// Make sure there are no intents
- assertEquals("incorrect intent count", 0, service.getIntents().size());
+ assertEquals("incorrect intent count", 0, service.getIntentCount());
// Register a compiler and an installer both setup for success.
service.registerCompiler(TestIntent.class, new TestCompiler(new TestInstallableIntent(INSTALLABLE_IID)));
@@ -64,17 +62,16 @@
TestTools.assertAfter(GRACE_MS, new Runnable() {
@Override
public void run() {
- assertEquals("incorrect intent state", INSTALLED,
- service.getIntentState(intent.getId()));
+ assertEquals("incorrect intent state", IntentState.INSTALLED,
+ service.getIntentState(intent.id()));
}
});
// Make sure that all expected events have been emitted
- validateEvents(intent, SUBMITTED, COMPILED, INSTALLED);
+ validateEvents(intent, SUBMITTED, INSTALLED);
// Make sure there is just one intent (and is ours)
- assertEquals("incorrect intent count", 1, service.getIntents().size());
- assertEquals("incorrect intent", intent, service.getIntent(intent.getId()));
+ assertEquals("incorrect intent count", 1, service.getIntentCount());
// Reset the listener events
listener.events.clear();
@@ -86,19 +83,19 @@
TestTools.assertAfter(GRACE_MS, new Runnable() {
@Override
public void run() {
- assertEquals("incorrect intent state", WITHDRAWN,
- service.getIntentState(intent.getId()));
+ assertEquals("incorrect intent state", IntentState.WITHDRAWN,
+ service.getIntentState(intent.id()));
}
});
// Make sure that all expected events have been emitted
- validateEvents(intent, WITHDRAWING, WITHDRAWN);
+ validateEvents(intent, WITHDRAWN);
// TODO: discuss what is the fate of intents after they have been withdrawn
// Make sure that the intent is no longer in the system
// assertEquals("incorrect intent count", 0, service.getIntents().size());
-// assertNull("intent should not be found", service.getIntent(intent.getId()));
-// assertNull("intent state should not be found", service.getIntentState(intent.getId()));
+// assertNull("intent should not be found", service.getIntent(intent.id()));
+// assertNull("intent state should not be found", service.getIntentState(intent.id()));
}
@Test
@@ -114,8 +111,8 @@
TestTools.assertAfter(GRACE_MS, new Runnable() {
@Override
public void run() {
- assertEquals("incorrect intent state", FAILED,
- service.getIntentState(intent.getId()));
+ assertEquals("incorrect intent state", IntentState.FAILED,
+ service.getIntentState(intent.id()));
}
});
@@ -137,13 +134,13 @@
TestTools.assertAfter(GRACE_MS, new Runnable() {
@Override
public void run() {
- assertEquals("incorrect intent state", FAILED,
- service.getIntentState(intent.getId()));
+ assertEquals("incorrect intent state", IntentState.FAILED,
+ service.getIntentState(intent.id()));
}
});
// Make sure that all expected events have been emitted
- validateEvents(intent, SUBMITTED, COMPILED, FAILED);
+ validateEvents(intent, SUBMITTED, FAILED);
}
/**
@@ -152,23 +149,23 @@
* considered.
*
* @param intent intent subject
- * @param states list of states for which events are expected
+ * @param types list of event types for which events are expected
*/
- protected void validateEvents(Intent intent, IntentState... states) {
+ protected void validateEvents(Intent intent, IntentEvent.Type... types) {
Iterator<IntentEvent> events = listener.events.iterator();
- for (IntentState state : states) {
+ for (IntentEvent.Type type : types) {
IntentEvent event = events.hasNext() ? events.next() : null;
if (event == null) {
- fail("expected event not found: " + state);
- } else if (intent.equals(event.getIntent())) {
- assertEquals("incorrect state", state, event.getState());
+ fail("expected event not found: " + type);
+ } else if (intent.equals(event.subject())) {
+ assertEquals("incorrect state", type, event.type());
}
}
// Remainder of events should not apply to this intent; make sure.
while (events.hasNext()) {
assertFalse("unexpected event for intent",
- intent.equals(events.next().getIntent()));
+ intent.equals(events.next().subject()));
}
}
@@ -229,8 +226,8 @@
TestTools.assertAfter(GRACE_MS, new Runnable() {
@Override
public void run() {
- assertEquals("incorrect intent state", INSTALLED,
- service.getIntentState(intent.getId()));
+ assertEquals("incorrect intent state", IntentState.INSTALLED,
+ service.getIntentState(intent.id()));
}
});
@@ -250,7 +247,7 @@
// Fixture to track emitted intent events
- protected class TestListener implements IntentEventListener {
+ protected class TestListener implements IntentListener {
final List<IntentEvent> events = new ArrayList<>();
@Override
diff --git a/core/api/src/test/java/org/onlab/onos/net/intent/MultiPointToSinglePointIntentTest.java b/core/api/src/test/java/org/onlab/onos/net/intent/MultiPointToSinglePointIntentTest.java
index d971ba2..66d294a 100644
--- a/core/api/src/test/java/org/onlab/onos/net/intent/MultiPointToSinglePointIntentTest.java
+++ b/core/api/src/test/java/org/onlab/onos/net/intent/MultiPointToSinglePointIntentTest.java
@@ -12,10 +12,10 @@
@Test
public void basics() {
MultiPointToSinglePointIntent intent = createOne();
- assertEquals("incorrect id", IID, intent.getId());
- assertEquals("incorrect match", MATCH, intent.getTrafficSelector());
- assertEquals("incorrect ingress", PS1, intent.getIngressPorts());
- assertEquals("incorrect egress", P2, intent.getEgressPort());
+ assertEquals("incorrect id", IID, intent.id());
+ assertEquals("incorrect match", MATCH, intent.selector());
+ assertEquals("incorrect ingress", PS1, intent.ingressPoints());
+ assertEquals("incorrect egress", P2, intent.egressPoint());
}
@Override
diff --git a/core/api/src/test/java/org/onlab/onos/net/intent/PathIntentTest.java b/core/api/src/test/java/org/onlab/onos/net/intent/PathIntentTest.java
index bd8dc08..7c15c37 100644
--- a/core/api/src/test/java/org/onlab/onos/net/intent/PathIntentTest.java
+++ b/core/api/src/test/java/org/onlab/onos/net/intent/PathIntentTest.java
@@ -16,12 +16,12 @@
@Test
public void basics() {
PathIntent intent = createOne();
- assertEquals("incorrect id", IID, intent.getId());
- assertEquals("incorrect match", MATCH, intent.getTrafficSelector());
- assertEquals("incorrect action", NOP, intent.getTrafficTreatment());
- assertEquals("incorrect ingress", P1, intent.getIngressPort());
- assertEquals("incorrect egress", P2, intent.getEgressPort());
- assertEquals("incorrect path", PATH1, intent.getPath());
+ assertEquals("incorrect id", IID, intent.id());
+ assertEquals("incorrect match", MATCH, intent.selector());
+ assertEquals("incorrect action", NOP, intent.treatment());
+ assertEquals("incorrect ingress", P1, intent.ingressPoint());
+ assertEquals("incorrect egress", P2, intent.egressPoint());
+ assertEquals("incorrect path", PATH1, intent.path());
}
@Override
diff --git a/core/api/src/test/java/org/onlab/onos/net/intent/PointToPointIntentTest.java b/core/api/src/test/java/org/onlab/onos/net/intent/PointToPointIntentTest.java
index 426a3d9..e0c5562 100644
--- a/core/api/src/test/java/org/onlab/onos/net/intent/PointToPointIntentTest.java
+++ b/core/api/src/test/java/org/onlab/onos/net/intent/PointToPointIntentTest.java
@@ -12,10 +12,10 @@
@Test
public void basics() {
PointToPointIntent intent = createOne();
- assertEquals("incorrect id", IID, intent.getId());
- assertEquals("incorrect match", MATCH, intent.getTrafficSelector());
- assertEquals("incorrect ingress", P1, intent.getIngressPort());
- assertEquals("incorrect egress", P2, intent.getEgressPort());
+ assertEquals("incorrect id", IID, intent.id());
+ assertEquals("incorrect match", MATCH, intent.selector());
+ assertEquals("incorrect ingress", P1, intent.ingressPoint());
+ assertEquals("incorrect egress", P2, intent.egressPoint());
}
@Override
diff --git a/core/api/src/test/java/org/onlab/onos/net/intent/SinglePointToMultiPointIntentTest.java b/core/api/src/test/java/org/onlab/onos/net/intent/SinglePointToMultiPointIntentTest.java
index 0561a87..64c9292 100644
--- a/core/api/src/test/java/org/onlab/onos/net/intent/SinglePointToMultiPointIntentTest.java
+++ b/core/api/src/test/java/org/onlab/onos/net/intent/SinglePointToMultiPointIntentTest.java
@@ -12,10 +12,10 @@
@Test
public void basics() {
SinglePointToMultiPointIntent intent = createOne();
- assertEquals("incorrect id", IID, intent.getId());
- assertEquals("incorrect match", MATCH, intent.getTrafficSelector());
- assertEquals("incorrect ingress", P1, intent.getIngressPort());
- assertEquals("incorrect egress", PS2, intent.getEgressPorts());
+ assertEquals("incorrect id", IID, intent.id());
+ assertEquals("incorrect match", MATCH, intent.selector());
+ assertEquals("incorrect ingress", P1, intent.ingressPoint());
+ assertEquals("incorrect egress", PS2, intent.egressPoints());
}
@Override
diff --git a/core/api/src/test/java/org/onlab/onos/net/intent/TestInstallableIntent.java b/core/api/src/test/java/org/onlab/onos/net/intent/TestInstallableIntent.java
index a6ce52e..3265925 100644
--- a/core/api/src/test/java/org/onlab/onos/net/intent/TestInstallableIntent.java
+++ b/core/api/src/test/java/org/onlab/onos/net/intent/TestInstallableIntent.java
@@ -1,6 +1,10 @@
package org.onlab.onos.net.intent;
//TODO is this the right package?
+import org.onlab.onos.net.Link;
+
+import java.util.Collection;
+
/**
* An installable intent used in the unit test.
*
@@ -25,4 +29,8 @@
super();
}
+ @Override
+ public Collection<Link> requiredLinks() {
+ return null;
+ }
}
diff --git a/core/api/src/test/java/org/onlab/onos/net/topology/DefaultGraphDescriptionTest.java b/core/api/src/test/java/org/onlab/onos/net/topology/DefaultGraphDescriptionTest.java
index a968abf..5f7d47b 100644
--- a/core/api/src/test/java/org/onlab/onos/net/topology/DefaultGraphDescriptionTest.java
+++ b/core/api/src/test/java/org/onlab/onos/net/topology/DefaultGraphDescriptionTest.java
@@ -37,5 +37,4 @@
new DefaultGraphDescription(4321L, ImmutableSet.of(DEV1, DEV3),
ImmutableSet.of(L1, L2));
}
-
-}
\ No newline at end of file
+}
diff --git a/core/api/src/test/java/org/onlab/onos/net/topology/DefaultTopologyEdgeTest.java b/core/api/src/test/java/org/onlab/onos/net/topology/DefaultTopologyEdgeTest.java
index 6c3c112..5d64c83 100644
--- a/core/api/src/test/java/org/onlab/onos/net/topology/DefaultTopologyEdgeTest.java
+++ b/core/api/src/test/java/org/onlab/onos/net/topology/DefaultTopologyEdgeTest.java
@@ -50,5 +50,4 @@
new DefaultTopologyEdge(V2, V1, L2))
.testEquals();
}
-
-}
\ No newline at end of file
+}
diff --git a/core/api/src/test/java/org/onlab/onos/net/topology/DefaultTopologyVertexTest.java b/core/api/src/test/java/org/onlab/onos/net/topology/DefaultTopologyVertexTest.java
index 1284624..7210ff3 100644
--- a/core/api/src/test/java/org/onlab/onos/net/topology/DefaultTopologyVertexTest.java
+++ b/core/api/src/test/java/org/onlab/onos/net/topology/DefaultTopologyVertexTest.java
@@ -26,5 +26,4 @@
.addEqualityGroup(new DefaultTopologyVertex(D2),
new DefaultTopologyVertex(D2)).testEquals();
}
-
-}
\ No newline at end of file
+}
diff --git a/core/net/pom.xml b/core/net/pom.xml
index c075147..6518068 100644
--- a/core/net/pom.xml
+++ b/core/net/pom.xml
@@ -36,6 +36,12 @@
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>org.easymock</groupId>
+ <artifactId>easymock</artifactId>
+ <scope>test</scope>
+ </dependency>
+
<!-- TODO Consider removing store dependency.
Currently required for DistributedDeviceManagerTest. -->
<dependency>
diff --git a/core/net/src/main/java/org/onlab/onos/cluster/impl/CoreManager.java b/core/net/src/main/java/org/onlab/onos/cluster/impl/CoreManager.java
new file mode 100644
index 0000000..4b1191f
--- /dev/null
+++ b/core/net/src/main/java/org/onlab/onos/cluster/impl/CoreManager.java
@@ -0,0 +1,38 @@
+package org.onlab.onos.cluster.impl;
+
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Service;
+import org.onlab.onos.CoreService;
+import org.onlab.onos.Version;
+import org.onlab.util.Tools;
+
+import java.io.File;
+import java.util.List;
+
+/**
+ * Core service implementation.
+ */
+@Component
+@Service
+public class CoreManager implements CoreService {
+
+ private static final File VERSION_FILE = new File("../VERSION");
+ private static Version version = Version.version("1.0.0-SNAPSHOT");
+
+ // TODO: work in progress
+
+ @Activate
+ public void activate() {
+ List<String> versionLines = Tools.slurp(VERSION_FILE);
+ if (versionLines != null && !versionLines.isEmpty()) {
+ version = Version.version(versionLines.get(0));
+ }
+ }
+
+ @Override
+ public Version version() {
+ return version;
+ }
+
+}
diff --git a/core/net/src/main/java/org/onlab/onos/net/device/impl/DeviceManager.java b/core/net/src/main/java/org/onlab/onos/net/device/impl/DeviceManager.java
index 5400fb0..a8d63c1 100644
--- a/core/net/src/main/java/org/onlab/onos/net/device/impl/DeviceManager.java
+++ b/core/net/src/main/java/org/onlab/onos/net/device/impl/DeviceManager.java
@@ -18,6 +18,7 @@
import org.onlab.onos.cluster.MastershipService;
import org.onlab.onos.cluster.MastershipTermService;
import org.onlab.onos.cluster.MastershipTerm;
+import org.onlab.onos.cluster.NodeId;
import org.onlab.onos.event.AbstractListenerRegistry;
import org.onlab.onos.event.EventDeliveryService;
import org.onlab.onos.net.Device;
@@ -38,7 +39,7 @@
import org.onlab.onos.net.device.PortDescription;
import org.onlab.onos.net.provider.AbstractProviderRegistry;
import org.onlab.onos.net.provider.AbstractProviderService;
-import org.onlab.onos.store.ClockService;
+import org.onlab.onos.store.ClockProviderService;
import org.slf4j.Logger;
/**
@@ -80,7 +81,7 @@
protected MastershipTermService termService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
- protected ClockService clockService;
+ protected ClockProviderService clockProviderService;
@Activate
public void activate() {
@@ -144,6 +145,10 @@
private void applyRole(DeviceId deviceId, MastershipRole newRole) {
if (newRole.equals(MastershipRole.NONE)) {
Device device = store.getDevice(deviceId);
+ // FIXME: Device might not be there yet. (eventual consistent)
+ if (device == null) {
+ return;
+ }
DeviceProvider provider = getProvider(device.providerId());
if (provider != null) {
provider.roleChanged(device, newRole);
@@ -193,13 +198,50 @@
checkNotNull(deviceId, DEVICE_ID_NULL);
checkNotNull(deviceDescription, DEVICE_DESCRIPTION_NULL);
checkValidity();
+
+ log.info("Device {} connected", deviceId);
+ // check my Role
+ MastershipRole role = mastershipService.requestRoleFor(deviceId);
+
+ if (role != MastershipRole.MASTER) {
+ // TODO: Do we need to explicitly tell the Provider that
+ // this instance is no longer the MASTER? probably not
+ return;
+ }
+
+ MastershipTerm term = mastershipService.requestTermService()
+ .getMastershipTerm(deviceId);
+ if (!term.master().equals(clusterService.getLocalNode().id())) {
+ // lost mastership after requestRole told this instance was MASTER.
+ return;
+ }
+ // tell clock provider if this instance is the master
+ clockProviderService.setMastershipTerm(deviceId, term);
+
DeviceEvent event = store.createOrUpdateDevice(provider().id(),
deviceId, deviceDescription);
+ // If there was a change of any kind, tell the provider
+ // that this instance is the master.
+ // Note: event can be null, if mastership was lost between
+ // roleRequest and store update calls.
if (event != null) {
- log.info("Device {} connected", deviceId);
- provider().roleChanged(event.subject(),
- mastershipService.requestRoleFor(deviceId));
+ // TODO: Check switch reconnected case. Is it assured that
+ // event will never be null?
+ // Could there be a situation MastershipService told this
+ // instance is the new Master, but
+ // event returned from the store is null?
+
+ // TODO: Confirm: Mastership could be lost after requestRole
+ // and createOrUpdateDevice call.
+ // In that case STANDBY node can
+ // claim itself to be master against the Device.
+ // Will the Node, chosen by the MastershipService, retry
+ // to get the MASTER role when that happen?
+
+ // FIXME: 1st argument should be deviceId, to allow setting
+ // certain roles even if the store returned null.
+ provider().roleChanged(event.subject(), role);
post(event);
}
}
@@ -208,6 +250,15 @@
public void deviceDisconnected(DeviceId deviceId) {
checkNotNull(deviceId, DEVICE_ID_NULL);
checkValidity();
+
+ // FIXME: only the MASTER should be marking off-line in normal cases,
+ // but if I was the last STANDBY connection, etc. and no one else
+ // was there to mark the device offline, this instance may need to
+ // temporarily request for Master Role and mark offline.
+ if (!mastershipService.getLocalRole(deviceId).equals(MastershipRole.MASTER)) {
+ log.debug("Device {} disconnected, but I am not the master", deviceId);
+ return;
+ }
DeviceEvent event = store.markOffline(deviceId);
//we're no longer capable of being master or a candidate.
mastershipService.relinquishMastership(deviceId);
@@ -253,6 +304,9 @@
// FIXME: implement response to this notification
log.warn("Failed to assert role [{}] onto Device {}", role,
deviceId);
+ if (role == MastershipRole.MASTER) {
+ mastershipService.relinquishMastership(deviceId);
+ }
}
}
@@ -268,11 +322,17 @@
@Override
public void event(MastershipEvent event) {
- DeviceId did = event.subject();
+ final DeviceId did = event.subject();
if (isAvailable(did)) {
- if (event.master().equals(clusterService.getLocalNode().id())) {
+ final NodeId myNodeId = clusterService.getLocalNode().id();
+
+ if (myNodeId.equals(event.master())) {
MastershipTerm term = termService.getMastershipTerm(did);
- clockService.setMastershipTerm(did, term);
+
+ if (term.master().equals(myNodeId)) {
+ // only set the new term if I am the master
+ clockProviderService.setMastershipTerm(did, term);
+ }
applyRole(did, MastershipRole.MASTER);
} else {
applyRole(did, MastershipRole.STANDBY);
diff --git a/core/net/src/main/java/org/onlab/onos/net/flow/impl/FlowRuleManager.java b/core/net/src/main/java/org/onlab/onos/net/flow/impl/FlowRuleManager.java
index 00619b3..a9eddd8 100644
--- a/core/net/src/main/java/org/onlab/onos/net/flow/impl/FlowRuleManager.java
+++ b/core/net/src/main/java/org/onlab/onos/net/flow/impl/FlowRuleManager.java
@@ -5,9 +5,10 @@
import java.util.Iterator;
import java.util.List;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
@@ -21,7 +22,11 @@
import org.onlab.onos.net.Device;
import org.onlab.onos.net.DeviceId;
import org.onlab.onos.net.device.DeviceService;
+import org.onlab.onos.net.flow.CompletedBatchOperation;
+import org.onlab.onos.net.flow.FlowEntry;
import org.onlab.onos.net.flow.FlowRule;
+import org.onlab.onos.net.flow.FlowRuleBatchEntry;
+import org.onlab.onos.net.flow.FlowRuleBatchOperation;
import org.onlab.onos.net.flow.FlowRuleEvent;
import org.onlab.onos.net.flow.FlowRuleListener;
import org.onlab.onos.net.flow.FlowRuleProvider;
@@ -34,7 +39,9 @@
import org.onlab.onos.net.provider.AbstractProviderService;
import org.slf4j.Logger;
+import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Lists;
+import com.google.common.collect.Multimap;
/**
* Provides implementation of the flow NB & SB APIs.
@@ -42,14 +49,14 @@
@Component(immediate = true)
@Service
public class FlowRuleManager
-extends AbstractProviderRegistry<FlowRuleProvider, FlowRuleProviderService>
-implements FlowRuleService, FlowRuleProviderRegistry {
+ extends AbstractProviderRegistry<FlowRuleProvider, FlowRuleProviderService>
+ implements FlowRuleService, FlowRuleProviderRegistry {
public static final String FLOW_RULE_NULL = "FlowRule cannot be null";
private final Logger log = getLogger(getClass());
private final AbstractListenerRegistry<FlowRuleEvent, FlowRuleListener>
- listenerRegistry = new AbstractListenerRegistry<>();
+ listenerRegistry = new AbstractListenerRegistry<>();
private final FlowRuleStoreDelegate delegate = new InternalStoreDelegate();
@@ -62,8 +69,6 @@
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected DeviceService deviceService;
- private final Map<FlowRule, AtomicInteger> deadRounds = new ConcurrentHashMap<>();
-
@Activate
public void activate() {
store.setDelegate(delegate);
@@ -79,7 +84,12 @@
}
@Override
- public Iterable<FlowRule> getFlowEntries(DeviceId deviceId) {
+ public int getFlowRuleCount() {
+ return store.getFlowRuleCount();
+ }
+
+ @Override
+ public Iterable<FlowEntry> getFlowEntries(DeviceId deviceId) {
return store.getFlowEntries(deviceId);
}
@@ -89,7 +99,6 @@
FlowRule f = flowRules[i];
final Device device = deviceService.getDevice(f.deviceId());
final FlowRuleProvider frp = getProvider(device.providerId());
- deadRounds.put(f, new AtomicInteger(0));
store.storeFlowRule(f);
frp.applyFlowRule(f);
}
@@ -103,16 +112,17 @@
for (int i = 0; i < flowRules.length; i++) {
f = flowRules[i];
device = deviceService.getDevice(f.deviceId());
- frp = getProvider(device.providerId());
- deadRounds.remove(f);
store.deleteFlowRule(f);
- frp.removeFlowRule(f);
+ if (device != null) {
+ frp = getProvider(device.providerId());
+ frp.removeFlowRule(f);
+ }
}
}
@Override
public void removeFlowRulesById(ApplicationId id) {
- Iterable<FlowRule> rules = getFlowRulesById(id);
+ Iterable<FlowRule> rules = getFlowRulesById(id);
FlowRuleProvider frp;
Device device;
@@ -126,7 +136,39 @@
@Override
public Iterable<FlowRule> getFlowRulesById(ApplicationId id) {
- return store.getFlowEntriesByAppId(id);
+ return store.getFlowRulesByAppId(id);
+ }
+
+ @Override
+ public Future<CompletedBatchOperation> applyBatch(
+ FlowRuleBatchOperation batch) {
+ Multimap<FlowRuleProvider, FlowRuleBatchEntry> batches =
+ ArrayListMultimap.create();
+ List<Future<Void>> futures = Lists.newArrayList();
+ for (FlowRuleBatchEntry fbe : batch.getOperations()) {
+ final FlowRule f = fbe.getTarget();
+ final Device device = deviceService.getDevice(f.deviceId());
+ final FlowRuleProvider frp = getProvider(device.providerId());
+ batches.put(frp, fbe);
+ switch (fbe.getOperator()) {
+ case ADD:
+ store.storeFlowRule(f);
+ break;
+ case REMOVE:
+ store.deleteFlowRule(f);
+ break;
+ case MODIFY:
+ default:
+ log.error("Batch operation type {} unsupported.", fbe.getOperator());
+ }
+ }
+ for (FlowRuleProvider provider : batches.keySet()) {
+ FlowRuleBatchOperation b =
+ new FlowRuleBatchOperation(batches.get(provider));
+ Future<Void> future = provider.executeBatch(b);
+ futures.add(future);
+ }
+ return new FlowRuleBatchFuture(futures);
}
@Override
@@ -146,63 +188,63 @@
}
private class InternalFlowRuleProviderService
- extends AbstractProviderService<FlowRuleProvider>
- implements FlowRuleProviderService {
+ extends AbstractProviderService<FlowRuleProvider>
+ implements FlowRuleProviderService {
protected InternalFlowRuleProviderService(FlowRuleProvider provider) {
super(provider);
}
@Override
- public void flowRemoved(FlowRule flowRule) {
- checkNotNull(flowRule, FLOW_RULE_NULL);
+ public void flowRemoved(FlowEntry flowEntry) {
+ checkNotNull(flowEntry, FLOW_RULE_NULL);
checkValidity();
- FlowRule stored = store.getFlowRule(flowRule);
+ FlowEntry stored = store.getFlowEntry(flowEntry);
if (stored == null) {
- log.debug("Rule already evicted from store: {}", flowRule);
+ log.info("Rule already evicted from store: {}", flowEntry);
return;
}
- Device device = deviceService.getDevice(flowRule.deviceId());
+ Device device = deviceService.getDevice(flowEntry.deviceId());
FlowRuleProvider frp = getProvider(device.providerId());
FlowRuleEvent event = null;
switch (stored.state()) {
- case ADDED:
- case PENDING_ADD:
+ case ADDED:
+ case PENDING_ADD:
frp.applyFlowRule(stored);
- break;
- case PENDING_REMOVE:
- case REMOVED:
- event = store.removeFlowRule(flowRule);
- break;
- default:
- break;
+ break;
+ case PENDING_REMOVE:
+ case REMOVED:
+ event = store.removeFlowRule(stored);
+ break;
+ default:
+ break;
}
if (event != null) {
- log.debug("Flow {} removed", flowRule);
+ log.debug("Flow {} removed", flowEntry);
post(event);
}
}
- private void flowMissing(FlowRule flowRule) {
+ private void flowMissing(FlowEntry flowRule) {
checkNotNull(flowRule, FLOW_RULE_NULL);
checkValidity();
Device device = deviceService.getDevice(flowRule.deviceId());
FlowRuleProvider frp = getProvider(device.providerId());
FlowRuleEvent event = null;
switch (flowRule.state()) {
- case PENDING_REMOVE:
- case REMOVED:
- event = store.removeFlowRule(flowRule);
- frp.removeFlowRule(flowRule);
- break;
- case ADDED:
- case PENDING_ADD:
- frp.applyFlowRule(flowRule);
- break;
- default:
- log.debug("Flow {} has not been installed.", flowRule);
+ case PENDING_REMOVE:
+ case REMOVED:
+ event = store.removeFlowRule(flowRule);
+ frp.removeFlowRule(flowRule);
+ break;
+ case ADDED:
+ case PENDING_ADD:
+ frp.applyFlowRule(flowRule);
+ break;
+ default:
+ log.debug("Flow {} has not been installed.", flowRule);
}
if (event != null) {
@@ -221,36 +263,40 @@
}
- private void flowAdded(FlowRule flowRule) {
- checkNotNull(flowRule, FLOW_RULE_NULL);
+ private void flowAdded(FlowEntry flowEntry) {
+ checkNotNull(flowEntry, FLOW_RULE_NULL);
checkValidity();
- if (deadRounds.containsKey(flowRule) &&
- checkRuleLiveness(flowRule, store.getFlowRule(flowRule))) {
+ if (checkRuleLiveness(flowEntry, store.getFlowEntry(flowEntry))) {
- FlowRuleEvent event = store.addOrUpdateFlowRule(flowRule);
+ FlowRuleEvent event = store.addOrUpdateFlowRule(flowEntry);
if (event == null) {
log.debug("No flow store event generated.");
} else {
- log.debug("Flow {} {}", flowRule, event.type());
+ log.debug("Flow {} {}", flowEntry, event.type());
post(event);
}
} else {
- removeFlowRules(flowRule);
+ removeFlowRules(flowEntry);
}
}
- private boolean checkRuleLiveness(FlowRule swRule, FlowRule storedRule) {
- int timeout = storedRule.timeout();
+ private boolean checkRuleLiveness(FlowEntry swRule, FlowEntry storedRule) {
+ if (storedRule == null) {
+ return false;
+ }
+ long timeout = storedRule.timeout() * 1000;
+ Long currentTime = System.currentTimeMillis();
if (storedRule.packets() != swRule.packets()) {
- deadRounds.get(swRule).set(0);
+ storedRule.setLastSeen();
return true;
}
- return (deadRounds.get(swRule).getAndIncrement() *
- FlowRuleProvider.POLL_INTERVAL) <= timeout;
-
+ if ((currentTime - storedRule.lastSeen()) <= timeout) {
+ return true;
+ }
+ return false;
}
// Posts the specified event to the local event dispatcher.
@@ -261,13 +307,13 @@
}
@Override
- public void pushFlowMetrics(DeviceId deviceId, Iterable<FlowRule> flowEntries) {
- List<FlowRule> storedRules = Lists.newLinkedList(store.getFlowEntries(deviceId));
+ public void pushFlowMetrics(DeviceId deviceId, Iterable<FlowEntry> flowEntries) {
+ List<FlowEntry> storedRules = Lists.newLinkedList(store.getFlowEntries(deviceId));
- Iterator<FlowRule> switchRulesIterator = flowEntries.iterator();
+ Iterator<FlowEntry> switchRulesIterator = flowEntries.iterator();
while (switchRulesIterator.hasNext()) {
- FlowRule rule = switchRulesIterator.next();
+ FlowEntry rule = switchRulesIterator.next();
if (storedRules.remove(rule)) {
// we both have the rule, let's update some info then.
flowAdded(rule);
@@ -276,7 +322,7 @@
extraneousFlow(rule);
}
}
- for (FlowRule rule : storedRules) {
+ for (FlowEntry rule : storedRules) {
// there are rules in the store that aren't on the switch
flowMissing(rule);
@@ -291,4 +337,63 @@
eventDispatcher.post(event);
}
}
+
+ private class FlowRuleBatchFuture
+ implements Future<CompletedBatchOperation> {
+
+ private final List<Future<Void>> futures;
+
+ public FlowRuleBatchFuture(List<Future<Void>> futures) {
+ this.futures = futures;
+ }
+
+ @Override
+ public boolean cancel(boolean mayInterruptIfRunning) {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+ @Override
+ public boolean isCancelled() {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+ @Override
+ public boolean isDone() {
+ boolean isDone = true;
+ for (Future<Void> future : futures) {
+ isDone &= future.isDone();
+ }
+ return isDone;
+ }
+
+ @Override
+ public CompletedBatchOperation get() throws InterruptedException,
+ ExecutionException {
+ // TODO Auto-generated method stub
+ for (Future<Void> future : futures) {
+ future.get();
+ }
+ return new CompletedBatchOperation();
+ }
+
+ @Override
+ public CompletedBatchOperation get(long timeout, TimeUnit unit)
+ throws InterruptedException, ExecutionException,
+ TimeoutException {
+ // TODO we should decrement the timeout
+ long start = System.nanoTime();
+ long end = start + unit.toNanos(timeout);
+ for (Future<Void> future : futures) {
+ long now = System.nanoTime();
+ long thisTimeout = end - now;
+ future.get(thisTimeout, TimeUnit.NANOSECONDS);
+ }
+ return new CompletedBatchOperation();
+ }
+
+ }
+
+
}
diff --git a/core/net/src/main/java/org/onlab/onos/net/host/impl/HostManager.java b/core/net/src/main/java/org/onlab/onos/net/host/impl/HostManager.java
index 88b6923..29a0f18 100644
--- a/core/net/src/main/java/org/onlab/onos/net/host/impl/HostManager.java
+++ b/core/net/src/main/java/org/onlab/onos/net/host/impl/HostManager.java
@@ -76,7 +76,7 @@
eventDispatcher.addSink(HostEvent.class, listenerRegistry);
monitor = new HostMonitor(deviceService, packetService, this);
-
+ monitor.start();
}
@Deactivate
diff --git a/core/net/src/main/java/org/onlab/onos/net/host/impl/HostMonitor.java b/core/net/src/main/java/org/onlab/onos/net/host/impl/HostMonitor.java
index 9f8dd48..e6e348f 100644
--- a/core/net/src/main/java/org/onlab/onos/net/host/impl/HostMonitor.java
+++ b/core/net/src/main/java/org/onlab/onos/net/host/impl/HostMonitor.java
@@ -2,11 +2,11 @@
import java.nio.ByteBuffer;
import java.util.ArrayList;
-import java.util.HashSet;
+import java.util.Collections;
import java.util.List;
-import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeUnit;
import org.jboss.netty.util.Timeout;
@@ -33,8 +33,6 @@
import org.onlab.packet.IpPrefix;
import org.onlab.packet.MacAddress;
import org.onlab.util.Timer;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
/**
* Monitors hosts on the dataplane to detect changes in host data.
@@ -44,70 +42,91 @@
* probe for hosts that have not yet been detected (specified by IP address).
*/
public class HostMonitor implements TimerTask {
- private static final Logger log = LoggerFactory.getLogger(HostMonitor.class);
-
- private static final byte[] ZERO_MAC_ADDRESS =
- MacAddress.valueOf("00:00:00:00:00:00").getAddress();
-
- // TODO put on Ethernet
- private static final byte[] BROADCAST_MAC =
- MacAddress.valueOf("ff:ff:ff:ff:ff:ff").getAddress();
-
private DeviceService deviceService;
private PacketService packetService;
private HostManager hostManager;
private final Set<IpAddress> monitoredAddresses;
- private final Map<ProviderId, HostProvider> hostProviders;
+ private final ConcurrentMap<ProviderId, HostProvider> hostProviders;
- private final long probeRate;
+ private static final long DEFAULT_PROBE_RATE = 30000; // milliseconds
+ private long probeRate = DEFAULT_PROBE_RATE;
- private final Timeout timeout;
+ private Timeout timeout;
- public HostMonitor(
- DeviceService deviceService,
- PacketService packetService,
- HostManager hostService) {
+ /**
+ * Creates a new host monitor.
+ *
+ * @param deviceService device service used to find edge ports
+ * @param packetService packet service used to send packets on the data plane
+ * @param hostManager host manager used to look up host information and
+ * probe existing hosts
+ */
+ public HostMonitor(DeviceService deviceService, PacketService packetService,
+ HostManager hostManager) {
this.deviceService = deviceService;
this.packetService = packetService;
- this.hostManager = hostService;
+ this.hostManager = hostManager;
- monitoredAddresses = new HashSet<>();
+ monitoredAddresses = Collections.newSetFromMap(
+ new ConcurrentHashMap<IpAddress, Boolean>());
hostProviders = new ConcurrentHashMap<>();
- probeRate = 30000; // milliseconds
-
timeout = Timer.getTimer().newTimeout(this, 0, TimeUnit.MILLISECONDS);
-
- addDefaultAddresses();
}
- private void addDefaultAddresses() {
- //monitoredAddresses.add(IpAddress.valueOf("10.0.0.1"));
- }
-
+ /**
+ * Adds an IP address to be monitored by the host monitor. The monitor will
+ * periodically probe the host to detect changes.
+ *
+ * @param ip IP address of the host to monitor
+ */
void addMonitoringFor(IpAddress ip) {
monitoredAddresses.add(ip);
}
+ /**
+ * Stops monitoring the given IP address.
+ *
+ * @param ip IP address to stop monitoring on
+ */
void stopMonitoring(IpAddress ip) {
monitoredAddresses.remove(ip);
}
- void shutdown() {
- timeout.cancel();
+ /**
+ * Starts the host monitor. Does nothing if the monitor is already running.
+ */
+ void start() {
+ synchronized (this) {
+ if (timeout == null) {
+ timeout = Timer.getTimer().newTimeout(this, 0, TimeUnit.MILLISECONDS);
+ }
+ }
}
+ /**
+ * Stops the host monitor.
+ */
+ void shutdown() {
+ synchronized (this) {
+ timeout.cancel();
+ timeout = null;
+ }
+ }
+
+ /**
+ * Registers a host provider with the host monitor. The monitor can use the
+ * provider to probe hosts.
+ *
+ * @param provider the host provider to register
+ */
void registerHostProvider(HostProvider provider) {
hostProviders.put(provider.id(), provider);
}
- void unregisterHostProvider(HostProvider provider) {
- // TODO find out how to call this
- }
-
@Override
public void run(Timeout timeout) throws Exception {
for (IpAddress ip : monitoredAddresses) {
@@ -121,14 +140,16 @@
} else {
for (Host host : hosts) {
HostProvider provider = hostProviders.get(host.providerId());
- if (provider != null) {
+ if (provider == null) {
+ hostProviders.remove(host.providerId(), null);
+ } else {
provider.triggerProbe(host);
}
}
}
}
- timeout = Timer.getTimer().newTimeout(this, probeRate, TimeUnit.MILLISECONDS);
+ this.timeout = Timer.getTimer().newTimeout(this, probeRate, TimeUnit.MILLISECONDS);
}
/**
@@ -161,7 +182,7 @@
List<Instruction> instructions = new ArrayList<>();
instructions.add(Instructions.createOutput(port.number()));
- TrafficTreatment treatment = new DefaultTrafficTreatment.Builder()
+ TrafficTreatment treatment = DefaultTrafficTreatment.builder()
.setOutput(port.number())
.build();
@@ -184,12 +205,12 @@
arp.setSenderHardwareAddress(sourceMac.getAddress())
.setSenderProtocolAddress(sourceIp.toOctets())
- .setTargetHardwareAddress(ZERO_MAC_ADDRESS)
+ .setTargetHardwareAddress(MacAddress.ZERO_MAC_ADDRESS)
.setTargetProtocolAddress(targetIp.toOctets());
Ethernet ethernet = new Ethernet();
ethernet.setEtherType(Ethernet.TYPE_ARP)
- .setDestinationMACAddress(BROADCAST_MAC)
+ .setDestinationMACAddress(MacAddress.BROADCAST_MAC)
.setSourceMACAddress(sourceMac.getAddress())
.setPayload(arp);
diff --git a/core/net/src/main/java/org/onlab/onos/net/intent/impl/AbstractBlockAllocatorBasedIdGenerator.java b/core/net/src/main/java/org/onlab/onos/net/intent/impl/AbstractBlockAllocatorBasedIdGenerator.java
new file mode 100644
index 0000000..00b64da
--- /dev/null
+++ b/core/net/src/main/java/org/onlab/onos/net/intent/impl/AbstractBlockAllocatorBasedIdGenerator.java
@@ -0,0 +1,42 @@
+package org.onlab.onos.net.intent.impl;
+
+import org.onlab.onos.net.intent.IdGenerator;
+
+/**
+ * Base class of {@link IdGenerator} implementations which use {@link IdBlockAllocator} as
+ * backend.
+ *
+ * @param <T> the type of ID
+ */
+public abstract class AbstractBlockAllocatorBasedIdGenerator<T> implements IdGenerator<T> {
+ protected final IdBlockAllocator allocator;
+ protected IdBlock idBlock;
+
+ /**
+ * Constructs an ID generator which use {@link IdBlockAllocator} as backend.
+ *
+ * @param allocator
+ */
+ protected AbstractBlockAllocatorBasedIdGenerator(IdBlockAllocator allocator) {
+ this.allocator = allocator;
+ this.idBlock = allocator.allocateUniqueIdBlock();
+ }
+
+ @Override
+ public synchronized T getNewId() {
+ try {
+ return convertFrom(idBlock.getNextId());
+ } catch (UnavailableIdException e) {
+ idBlock = allocator.allocateUniqueIdBlock();
+ return convertFrom(idBlock.getNextId());
+ }
+ }
+
+ /**
+ * Returns an ID instance of {@code T} type from the long value.
+ *
+ * @param value original long value
+ * @return ID instance
+ */
+ protected abstract T convertFrom(long value);
+}
diff --git a/core/net/src/main/java/org/onlab/onos/net/intent/impl/DummyIdBlockAllocator.java b/core/net/src/main/java/org/onlab/onos/net/intent/impl/DummyIdBlockAllocator.java
new file mode 100644
index 0000000..f331aa2
--- /dev/null
+++ b/core/net/src/main/java/org/onlab/onos/net/intent/impl/DummyIdBlockAllocator.java
@@ -0,0 +1,31 @@
+package org.onlab.onos.net.intent.impl;
+
+public class DummyIdBlockAllocator implements IdBlockAllocator {
+ private long blockTop;
+ private static final long BLOCK_SIZE = 0x1000000L;
+
+ /**
+ * Returns a block of IDs which are unique and unused.
+ * Range of IDs is fixed size and is assigned incrementally as this method
+ * called.
+ *
+ * @return an IdBlock containing a set of unique IDs
+ */
+ @Override
+ public IdBlock allocateUniqueIdBlock() {
+ synchronized (this) {
+ long blockHead = blockTop;
+ long blockTail = blockTop + BLOCK_SIZE;
+
+ IdBlock block = new IdBlock(blockHead, BLOCK_SIZE);
+ blockTop = blockTail;
+
+ return block;
+ }
+ }
+
+ @Override
+ public IdBlock allocateUniqueIdBlock(long range) {
+ throw new UnsupportedOperationException("Not supported yet");
+ }
+}
diff --git a/core/net/src/main/java/org/onlab/onos/net/intent/impl/HostToHostIntentCompiler.java b/core/net/src/main/java/org/onlab/onos/net/intent/impl/HostToHostIntentCompiler.java
new file mode 100644
index 0000000..de61e8e
--- /dev/null
+++ b/core/net/src/main/java/org/onlab/onos/net/intent/impl/HostToHostIntentCompiler.java
@@ -0,0 +1,90 @@
+package org.onlab.onos.net.intent.impl;
+
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.onlab.onos.net.Host;
+import org.onlab.onos.net.HostId;
+import org.onlab.onos.net.Path;
+import org.onlab.onos.net.flow.TrafficSelector;
+import org.onlab.onos.net.host.HostService;
+import org.onlab.onos.net.intent.HostToHostIntent;
+import org.onlab.onos.net.intent.IdGenerator;
+import org.onlab.onos.net.intent.Intent;
+import org.onlab.onos.net.intent.IntentCompiler;
+import org.onlab.onos.net.intent.IntentExtensionService;
+import org.onlab.onos.net.intent.IntentId;
+import org.onlab.onos.net.intent.PathIntent;
+import org.onlab.onos.net.topology.PathService;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Set;
+
+import static org.onlab.onos.net.flow.DefaultTrafficSelector.builder;
+
+/**
+ * A intent compiler for {@link HostToHostIntent}.
+ */
+@Component(immediate = true)
+public class HostToHostIntentCompiler
+ implements IntentCompiler<HostToHostIntent> {
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected IntentExtensionService intentManager;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected PathService pathService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected HostService hostService;
+
+ private IdGenerator<IntentId> intentIdGenerator;
+
+ @Activate
+ public void activate() {
+ IdBlockAllocator idBlockAllocator = new DummyIdBlockAllocator();
+ intentIdGenerator = new IdBlockAllocatorBasedIntentIdGenerator(idBlockAllocator);
+ intentManager.registerCompiler(HostToHostIntent.class, this);
+ }
+
+ @Deactivate
+ public void deactivate() {
+ intentManager.unregisterCompiler(HostToHostIntent.class);
+ }
+
+ @Override
+ public List<Intent> compile(HostToHostIntent intent) {
+ Path pathOne = getPath(intent.one(), intent.two());
+ Path pathTwo = getPath(intent.two(), intent.one());
+
+ Host one = hostService.getHost(intent.one());
+ Host two = hostService.getHost(intent.two());
+
+ return Arrays.asList(createPathIntent(pathOne, one, two, intent),
+ createPathIntent(pathTwo, two, one, intent));
+ }
+
+ // Creates a path intent from the specified path and original connectivity intent.
+ private Intent createPathIntent(Path path, Host src, Host dst,
+ HostToHostIntent intent) {
+
+ TrafficSelector selector = builder(intent.selector())
+ .matchEthSrc(src.mac()).matchEthDst(dst.mac()).build();
+
+ return new PathIntent(intentIdGenerator.getNewId(),
+ selector, intent.treatment(),
+ path.src(), path.dst(), path);
+ }
+
+ private Path getPath(HostId one, HostId two) {
+ Set<Path> paths = pathService.getPaths(one, two);
+ if (paths.isEmpty()) {
+ throw new PathNotFoundException("No path from host " + one + " to " + two);
+ }
+ // TODO: let's be more intelligent about this eventually
+ return paths.iterator().next();
+ }
+}
diff --git a/core/net/src/main/java/org/onlab/onos/net/intent/impl/IdBlock.java b/core/net/src/main/java/org/onlab/onos/net/intent/impl/IdBlock.java
new file mode 100644
index 0000000..ce418ea
--- /dev/null
+++ b/core/net/src/main/java/org/onlab/onos/net/intent/impl/IdBlock.java
@@ -0,0 +1,111 @@
+package org.onlab.onos.net.intent.impl;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+import java.util.Objects;
+import java.util.concurrent.atomic.AtomicLong;
+
+import com.google.common.base.MoreObjects;
+
+/**
+ * A class representing an ID space.
+ */
+public final class IdBlock {
+ private final long start;
+ private final long size;
+
+ private final AtomicLong currentId;
+
+ /**
+ * Constructs a new ID block with the specified size and initial value.
+ *
+ * @param start initial value of the block
+ * @param size size of the block
+ * @throws IllegalArgumentException if the size is less than or equal to 0
+ */
+ public IdBlock(long start, long size) {
+ checkArgument(size > 0, "size should be more than 0, but %s", size);
+
+ this.start = start;
+ this.size = size;
+
+ this.currentId = new AtomicLong(start);
+ }
+
+ // TODO: consider if this method is needed or not
+ /**
+ * Returns the initial value.
+ *
+ * @return initial value
+ */
+ public long getStart() {
+ return start;
+ }
+
+ // TODO: consider if this method is needed or not
+ /**
+ * Returns the last value.
+ *
+ * @return last value
+ */
+ public long getEnd() {
+ return start + size - 1;
+ }
+
+ /**
+ * Returns the block size.
+ *
+ * @return block size
+ */
+ public long getSize() {
+ return size;
+ }
+
+ /**
+ * Returns the next ID in the block.
+ *
+ * @return next ID
+ * @throws UnavailableIdException if there is no available ID in the block.
+ */
+ public long getNextId() {
+ final long id = currentId.getAndIncrement();
+ if (id > getEnd()) {
+ throw new UnavailableIdException(String.format(
+ "used all IDs in allocated space (size: %d, end: %d, current: %d)",
+ size, getEnd(), id
+ ));
+ }
+
+ return id;
+ }
+
+ // TODO: Do we really need equals and hashCode? Should it contain currentId?
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ IdBlock that = (IdBlock) o;
+ return Objects.equals(this.start, that.start)
+ && Objects.equals(this.size, that.size)
+ && Objects.equals(this.currentId.get(), that.currentId.get());
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(start, size, currentId);
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(getClass())
+ .add("start", start)
+ .add("size", size)
+ .add("currentId", currentId)
+ .toString();
+ }
+}
diff --git a/core/net/src/main/java/org/onlab/onos/net/intent/impl/IdBlockAllocator.java b/core/net/src/main/java/org/onlab/onos/net/intent/impl/IdBlockAllocator.java
new file mode 100644
index 0000000..1adac02
--- /dev/null
+++ b/core/net/src/main/java/org/onlab/onos/net/intent/impl/IdBlockAllocator.java
@@ -0,0 +1,21 @@
+package org.onlab.onos.net.intent.impl;
+
+/**
+ * An interface that gives unique ID spaces.
+ */
+public interface IdBlockAllocator {
+ /**
+ * Allocates a unique Id Block.
+ *
+ * @return Id Block.
+ */
+ IdBlock allocateUniqueIdBlock();
+
+ /**
+ * Allocates next unique id and retrieve a new range of ids if needed.
+ *
+ * @param range range to use for the identifier
+ * @return Id Block.
+ */
+ IdBlock allocateUniqueIdBlock(long range);
+}
diff --git a/core/net/src/main/java/org/onlab/onos/net/intent/impl/IdBlockAllocatorBasedIntentIdGenerator.java b/core/net/src/main/java/org/onlab/onos/net/intent/impl/IdBlockAllocatorBasedIntentIdGenerator.java
new file mode 100644
index 0000000..9620e59
--- /dev/null
+++ b/core/net/src/main/java/org/onlab/onos/net/intent/impl/IdBlockAllocatorBasedIntentIdGenerator.java
@@ -0,0 +1,25 @@
+package org.onlab.onos.net.intent.impl;
+
+import org.onlab.onos.net.intent.IntentId;
+
+/**
+ * An implementation of {@link org.onlab.onos.net.intent.IdGenerator} of intent ID,
+ * which uses {@link IdBlockAllocator}.
+ */
+public class IdBlockAllocatorBasedIntentIdGenerator extends AbstractBlockAllocatorBasedIdGenerator<IntentId> {
+
+ /**
+ * Constructs an intent ID generator, which uses the specified ID block allocator
+ * to generate a global unique intent ID.
+ *
+ * @param allocator the ID block allocator to use for generating intent IDs
+ */
+ public IdBlockAllocatorBasedIntentIdGenerator(IdBlockAllocator allocator) {
+ super(allocator);
+ }
+
+ @Override
+ protected IntentId convertFrom(long value) {
+ return new IntentId(value);
+ }
+}
diff --git a/core/net/src/main/java/org/onlab/onos/net/intent/impl/IntentCompilationException.java b/core/net/src/main/java/org/onlab/onos/net/intent/impl/IntentCompilationException.java
new file mode 100644
index 0000000..bf739df
--- /dev/null
+++ b/core/net/src/main/java/org/onlab/onos/net/intent/impl/IntentCompilationException.java
@@ -0,0 +1,22 @@
+package org.onlab.onos.net.intent.impl;
+
+import org.onlab.onos.net.intent.IntentException;
+
+/**
+ * An exception thrown when a intent compilation fails.
+ */
+public class IntentCompilationException extends IntentException {
+ private static final long serialVersionUID = 235237603018210810L;
+
+ public IntentCompilationException() {
+ super();
+ }
+
+ public IntentCompilationException(String message) {
+ super(message);
+ }
+
+ public IntentCompilationException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/core/net/src/main/java/org/onlab/onos/net/intent/impl/IntentInstallationException.java b/core/net/src/main/java/org/onlab/onos/net/intent/impl/IntentInstallationException.java
new file mode 100644
index 0000000..3b17cf1
--- /dev/null
+++ b/core/net/src/main/java/org/onlab/onos/net/intent/impl/IntentInstallationException.java
@@ -0,0 +1,22 @@
+package org.onlab.onos.net.intent.impl;
+
+import org.onlab.onos.net.intent.IntentException;
+
+/**
+ * An exception thrown when intent installation fails.
+ */
+public class IntentInstallationException extends IntentException {
+ private static final long serialVersionUID = 3720268258616014168L;
+
+ public IntentInstallationException() {
+ super();
+ }
+
+ public IntentInstallationException(String message) {
+ super(message);
+ }
+
+ public IntentInstallationException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/core/net/src/main/java/org/onlab/onos/net/intent/impl/IntentManager.java b/core/net/src/main/java/org/onlab/onos/net/intent/impl/IntentManager.java
new file mode 100644
index 0000000..16b75f2
--- /dev/null
+++ b/core/net/src/main/java/org/onlab/onos/net/intent/impl/IntentManager.java
@@ -0,0 +1,463 @@
+package org.onlab.onos.net.intent.impl;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static java.util.concurrent.Executors.newSingleThreadExecutor;
+import static org.onlab.onos.net.intent.IntentState.COMPILING;
+import static org.onlab.onos.net.intent.IntentState.FAILED;
+import static org.onlab.onos.net.intent.IntentState.INSTALLED;
+import static org.onlab.onos.net.intent.IntentState.INSTALLING;
+import static org.onlab.onos.net.intent.IntentState.RECOMPILING;
+import static org.onlab.onos.net.intent.IntentState.WITHDRAWING;
+import static org.onlab.onos.net.intent.IntentState.WITHDRAWN;
+import static org.onlab.util.Tools.namedThreads;
+import static org.slf4j.LoggerFactory.getLogger;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.ExecutorService;
+
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.apache.felix.scr.annotations.Service;
+import org.onlab.onos.event.AbstractListenerRegistry;
+import org.onlab.onos.event.EventDeliveryService;
+import org.onlab.onos.net.intent.InstallableIntent;
+import org.onlab.onos.net.intent.Intent;
+import org.onlab.onos.net.intent.IntentCompiler;
+import org.onlab.onos.net.intent.IntentEvent;
+import org.onlab.onos.net.intent.IntentException;
+import org.onlab.onos.net.intent.IntentExtensionService;
+import org.onlab.onos.net.intent.IntentId;
+import org.onlab.onos.net.intent.IntentInstaller;
+import org.onlab.onos.net.intent.IntentListener;
+import org.onlab.onos.net.intent.IntentOperations;
+import org.onlab.onos.net.intent.IntentService;
+import org.onlab.onos.net.intent.IntentState;
+import org.onlab.onos.net.intent.IntentStore;
+import org.onlab.onos.net.intent.IntentStoreDelegate;
+import org.slf4j.Logger;
+
+import com.google.common.collect.ImmutableMap;
+
+/**
+ * An implementation of Intent Manager.
+ */
+@Component(immediate = true)
+@Service
+public class IntentManager
+ implements IntentService, IntentExtensionService {
+ private final Logger log = getLogger(getClass());
+
+ public static final String INTENT_NULL = "Intent cannot be null";
+ public static final String INTENT_ID_NULL = "Intent ID cannot be null";
+
+ // Collections for compiler, installer, and listener are ONOS instance local
+ private final ConcurrentMap<Class<? extends Intent>,
+ IntentCompiler<? extends Intent>> compilers = new ConcurrentHashMap<>();
+ private final ConcurrentMap<Class<? extends InstallableIntent>,
+ IntentInstaller<? extends InstallableIntent>> installers = new ConcurrentHashMap<>();
+
+ private final AbstractListenerRegistry<IntentEvent, IntentListener>
+ listenerRegistry = new AbstractListenerRegistry<>();
+
+ private final ExecutorService executor = newSingleThreadExecutor(namedThreads("onos-intents"));
+
+ private final IntentStoreDelegate delegate = new InternalStoreDelegate();
+ private final TopologyChangeDelegate topoDelegate = new InternalTopoChangeDelegate();
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected IntentStore store;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected ObjectiveTrackerService trackerService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected EventDeliveryService eventDispatcher;
+
+ @Activate
+ public void activate() {
+ store.setDelegate(delegate);
+ trackerService.setDelegate(topoDelegate);
+ eventDispatcher.addSink(IntentEvent.class, listenerRegistry);
+ log.info("Started");
+ }
+
+ @Deactivate
+ public void deactivate() {
+ store.unsetDelegate(delegate);
+ trackerService.unsetDelegate(topoDelegate);
+ eventDispatcher.removeSink(IntentEvent.class);
+ log.info("Stopped");
+ }
+
+ @Override
+ public void submit(Intent intent) {
+ checkNotNull(intent, INTENT_NULL);
+ registerSubclassCompilerIfNeeded(intent);
+ IntentEvent event = store.createIntent(intent);
+ if (event != null) {
+ eventDispatcher.post(event);
+ executor.execute(new IntentTask(COMPILING, intent));
+ }
+ }
+
+ @Override
+ public void withdraw(Intent intent) {
+ checkNotNull(intent, INTENT_NULL);
+ executor.execute(new IntentTask(WITHDRAWING, intent));
+ }
+
+ // FIXME: implement this method
+ @Override
+ public void execute(IntentOperations operations) {
+ throw new UnsupportedOperationException("execute() is not implemented yet");
+ }
+
+ @Override
+ public Iterable<Intent> getIntents() {
+ return store.getIntents();
+ }
+
+ @Override
+ public long getIntentCount() {
+ return store.getIntentCount();
+ }
+
+ @Override
+ public Intent getIntent(IntentId id) {
+ checkNotNull(id, INTENT_ID_NULL);
+ return store.getIntent(id);
+ }
+
+ @Override
+ public IntentState getIntentState(IntentId id) {
+ checkNotNull(id, INTENT_ID_NULL);
+ return store.getIntentState(id);
+ }
+
+ @Override
+ public void addListener(IntentListener listener) {
+ listenerRegistry.addListener(listener);
+ }
+
+ @Override
+ public void removeListener(IntentListener listener) {
+ listenerRegistry.removeListener(listener);
+ }
+
+ @Override
+ public <T extends Intent> void registerCompiler(Class<T> cls, IntentCompiler<T> compiler) {
+ compilers.put(cls, compiler);
+ }
+
+ @Override
+ public <T extends Intent> void unregisterCompiler(Class<T> cls) {
+ compilers.remove(cls);
+ }
+
+ @Override
+ public Map<Class<? extends Intent>, IntentCompiler<? extends Intent>> getCompilers() {
+ return ImmutableMap.copyOf(compilers);
+ }
+
+ @Override
+ public <T extends InstallableIntent> void registerInstaller(Class<T> cls, IntentInstaller<T> installer) {
+ installers.put(cls, installer);
+ }
+
+ @Override
+ public <T extends InstallableIntent> void unregisterInstaller(Class<T> cls) {
+ installers.remove(cls);
+ }
+
+ @Override
+ public Map<Class<? extends InstallableIntent>, IntentInstaller<? extends InstallableIntent>> getInstallers() {
+ return ImmutableMap.copyOf(installers);
+ }
+
+ /**
+ * Returns the corresponding intent compiler to the specified intent.
+ *
+ * @param intent intent
+ * @param <T> the type of intent
+ * @return intent compiler corresponding to the specified intent
+ */
+ private <T extends Intent> IntentCompiler<T> getCompiler(T intent) {
+ @SuppressWarnings("unchecked")
+ IntentCompiler<T> compiler = (IntentCompiler<T>) compilers.get(intent.getClass());
+ if (compiler == null) {
+ throw new IntentException("no compiler for class " + intent.getClass());
+ }
+ return compiler;
+ }
+
+ /**
+ * Returns the corresponding intent installer to the specified installable intent.
+ *
+ * @param intent intent
+ * @param <T> the type of installable intent
+ * @return intent installer corresponding to the specified installable intent
+ */
+ private <T extends InstallableIntent> IntentInstaller<T> getInstaller(T intent) {
+ @SuppressWarnings("unchecked")
+ IntentInstaller<T> installer = (IntentInstaller<T>) installers.get(intent.getClass());
+ if (installer == null) {
+ throw new IntentException("no installer for class " + intent.getClass());
+ }
+ return installer;
+ }
+
+ /**
+ * Compiles the specified intent.
+ *
+ * @param intent intent to be compiled
+ */
+ private void executeCompilingPhase(Intent intent) {
+ // Indicate that the intent is entering the compiling phase.
+ store.setState(intent, COMPILING);
+
+ try {
+ // Compile the intent into installable derivatives.
+ List<InstallableIntent> installable = compileIntent(intent);
+
+ // If all went well, associate the resulting list of installable
+ // intents with the top-level intent and proceed to install.
+ store.addInstallableIntents(intent.id(), installable);
+ executeInstallingPhase(intent);
+
+ } catch (Exception e) {
+ log.warn("Unable to compile intent {} due to: {}", intent.id(), e);
+
+ // If compilation failed, mark the intent as failed.
+ store.setState(intent, FAILED);
+ }
+ }
+
+ // FIXME: To make SDN-IP workable ASAP, only single level compilation is implemented
+ // TODO: implement compilation traversing tree structure
+ private List<InstallableIntent> compileIntent(Intent intent) {
+ List<InstallableIntent> installable = new ArrayList<>();
+ for (Intent compiled : getCompiler(intent).compile(intent)) {
+ InstallableIntent installableIntent = (InstallableIntent) compiled;
+ installable.add(installableIntent);
+ }
+ return installable;
+ }
+
+ /**
+ * Installs all installable intents associated with the specified top-level
+ * intent.
+ *
+ * @param intent intent to be installed
+ */
+ private void executeInstallingPhase(Intent intent) {
+ // Indicate that the intent is entering the installing phase.
+ store.setState(intent, INSTALLING);
+
+ try {
+ List<InstallableIntent> installables = store.getInstallableIntents(intent.id());
+ if (installables != null) {
+ for (InstallableIntent installable : installables) {
+ registerSubclassInstallerIfNeeded(installable);
+ trackerService.addTrackedResources(intent.id(),
+ installable.requiredLinks());
+ getInstaller(installable).install(installable);
+ }
+ }
+ eventDispatcher.post(store.setState(intent, INSTALLED));
+
+ } catch (Exception e) {
+ log.warn("Unable to install intent {} due to: {}", intent.id(), e);
+ uninstallIntent(intent);
+
+ // If compilation failed, kick off the recompiling phase.
+ executeRecompilingPhase(intent);
+ }
+ }
+
+ /**
+ * Recompiles the specified intent.
+ *
+ * @param intent intent to be recompiled
+ */
+ private void executeRecompilingPhase(Intent intent) {
+ // Indicate that the intent is entering the recompiling phase.
+ store.setState(intent, RECOMPILING);
+
+ try {
+ // Compile the intent into installable derivatives.
+ List<InstallableIntent> installable = compileIntent(intent);
+
+ // If all went well, compare the existing list of installable
+ // intents with the newly compiled list. If they are the same,
+ // bail, out since the previous approach was determined not to
+ // be viable.
+ List<InstallableIntent> originalInstallable =
+ store.getInstallableIntents(intent.id());
+
+ if (Objects.equals(originalInstallable, installable)) {
+ eventDispatcher.post(store.setState(intent, FAILED));
+ } else {
+ // Otherwise, re-associate the newly compiled installable intents
+ // with the top-level intent and kick off installing phase.
+ store.addInstallableIntents(intent.id(), installable);
+ executeInstallingPhase(intent);
+ }
+ } catch (Exception e) {
+ log.warn("Unable to recompile intent {} due to: {}", intent.id(), e);
+
+ // If compilation failed, mark the intent as failed.
+ eventDispatcher.post(store.setState(intent, FAILED));
+ }
+ }
+
+ /**
+ * Uninstalls the specified intent by uninstalling all of its associated
+ * installable derivatives.
+ *
+ * @param intent intent to be installed
+ */
+ private void executeWithdrawingPhase(Intent intent) {
+ // Indicate that the intent is being withdrawn.
+ store.setState(intent, WITHDRAWING);
+ uninstallIntent(intent);
+
+ // If all went well, disassociate the top-level intent with its
+ // installable derivatives and mark it as withdrawn.
+ store.removeInstalledIntents(intent.id());
+ eventDispatcher.post(store.setState(intent, WITHDRAWN));
+ }
+
+ /**
+ * Uninstalls all installable intents associated with the given intent.
+ *
+ * @param intent intent to be uninstalled
+ */
+ private void uninstallIntent(Intent intent) {
+ try {
+ List<InstallableIntent> installables = store.getInstallableIntents(intent.id());
+ if (installables != null) {
+ for (InstallableIntent installable : installables) {
+ getInstaller(installable).uninstall(installable);
+ }
+ }
+ } catch (IntentException e) {
+ log.warn("Unable to uninstall intent {} due to: {}", intent.id(), e);
+ }
+ }
+
+ /**
+ * Registers an intent compiler of the specified intent if an intent compiler
+ * for the intent is not registered. This method traverses the class hierarchy of
+ * the intent. Once an intent compiler for a parent type is found, this method
+ * registers the found intent compiler.
+ *
+ * @param intent intent
+ */
+ private void registerSubclassCompilerIfNeeded(Intent intent) {
+ if (!compilers.containsKey(intent.getClass())) {
+ Class<?> cls = intent.getClass();
+ while (cls != Object.class) {
+ // As long as we're within the Intent class descendants
+ if (Intent.class.isAssignableFrom(cls)) {
+ IntentCompiler<?> compiler = compilers.get(cls);
+ if (compiler != null) {
+ compilers.put(intent.getClass(), compiler);
+ return;
+ }
+ }
+ cls = cls.getSuperclass();
+ }
+ }
+ }
+
+ /**
+ * Registers an intent installer of the specified intent if an intent installer
+ * for the intent is not registered. This method traverses the class hierarchy of
+ * the intent. Once an intent installer for a parent type is found, this method
+ * registers the found intent installer.
+ *
+ * @param intent intent
+ */
+ private void registerSubclassInstallerIfNeeded(InstallableIntent intent) {
+ if (!installers.containsKey(intent.getClass())) {
+ Class<?> cls = intent.getClass();
+ while (cls != Object.class) {
+ // As long as we're within the InstallableIntent class descendants
+ if (InstallableIntent.class.isAssignableFrom(cls)) {
+ IntentInstaller<?> installer = installers.get(cls);
+ if (installer != null) {
+ installers.put(intent.getClass(), installer);
+ return;
+ }
+ }
+ cls = cls.getSuperclass();
+ }
+ }
+ }
+
+ // Store delegate to re-post events emitted from the store.
+ private class InternalStoreDelegate implements IntentStoreDelegate {
+ @Override
+ public void notify(IntentEvent event) {
+ eventDispatcher.post(event);
+ if (event.type() == IntentEvent.Type.SUBMITTED) {
+ executor.execute(new IntentTask(COMPILING, event.subject()));
+ }
+ }
+ }
+
+ // Topology change delegate
+ private class InternalTopoChangeDelegate implements TopologyChangeDelegate {
+ @Override
+ public void triggerCompile(Iterable<IntentId> intentIds,
+ boolean compileAllFailed) {
+ // Attempt recompilation of the specified intents first.
+ for (IntentId intentId : intentIds) {
+ Intent intent = getIntent(intentId);
+ uninstallIntent(intent);
+
+ executeRecompilingPhase(intent);
+ }
+
+ if (compileAllFailed) {
+ // If required, compile all currently failed intents.
+ for (Intent intent : getIntents()) {
+ if (getIntentState(intent.id()) == FAILED) {
+ executeCompilingPhase(intent);
+ }
+ }
+ }
+ }
+ }
+
+ // Auxiliary runnable to perform asynchronous tasks.
+ private class IntentTask implements Runnable {
+ private final IntentState state;
+ private final Intent intent;
+
+ public IntentTask(IntentState state, Intent intent) {
+ this.state = state;
+ this.intent = intent;
+ }
+
+ @Override
+ public void run() {
+ if (state == COMPILING) {
+ executeCompilingPhase(intent);
+ } else if (state == RECOMPILING) {
+ executeRecompilingPhase(intent);
+ } else if (state == WITHDRAWING) {
+ executeWithdrawingPhase(intent);
+ }
+ }
+ }
+
+}
diff --git a/core/net/src/main/java/org/onlab/onos/net/intent/impl/IntentRemovalException.java b/core/net/src/main/java/org/onlab/onos/net/intent/impl/IntentRemovalException.java
new file mode 100644
index 0000000..5ee4ee4
--- /dev/null
+++ b/core/net/src/main/java/org/onlab/onos/net/intent/impl/IntentRemovalException.java
@@ -0,0 +1,22 @@
+package org.onlab.onos.net.intent.impl;
+
+import org.onlab.onos.net.intent.IntentException;
+
+/**
+ * An exception thrown when intent removal failed.
+ */
+public class IntentRemovalException extends IntentException {
+ private static final long serialVersionUID = -5259226322037891951L;
+
+ public IntentRemovalException() {
+ super();
+ }
+
+ public IntentRemovalException(String message) {
+ super(message);
+ }
+
+ public IntentRemovalException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/core/net/src/main/java/org/onlab/onos/net/intent/impl/ObjectiveTracker.java b/core/net/src/main/java/org/onlab/onos/net/intent/impl/ObjectiveTracker.java
new file mode 100644
index 0000000..d84c367
--- /dev/null
+++ b/core/net/src/main/java/org/onlab/onos/net/intent/impl/ObjectiveTracker.java
@@ -0,0 +1,140 @@
+package org.onlab.onos.net.intent.impl;
+
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.SetMultimap;
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.apache.felix.scr.annotations.Service;
+import org.onlab.onos.event.Event;
+import org.onlab.onos.net.Link;
+import org.onlab.onos.net.LinkKey;
+import org.onlab.onos.net.intent.IntentId;
+import org.onlab.onos.net.link.LinkEvent;
+import org.onlab.onos.net.topology.TopologyEvent;
+import org.onlab.onos.net.topology.TopologyListener;
+import org.onlab.onos.net.topology.TopologyService;
+import org.slf4j.Logger;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.ExecutorService;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.collect.Multimaps.synchronizedSetMultimap;
+import static java.util.concurrent.Executors.newSingleThreadExecutor;
+import static org.onlab.onos.net.link.LinkEvent.Type.LINK_REMOVED;
+import static org.onlab.util.Tools.namedThreads;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Entity responsible for tracking installed flows and for monitoring topology
+ * events to determine what flows are affected by topology changes.
+ */
+@Component
+@Service
+public class ObjectiveTracker implements ObjectiveTrackerService {
+
+ private final Logger log = getLogger(getClass());
+
+ private final SetMultimap<LinkKey, IntentId> intentsByLink =
+ synchronizedSetMultimap(HashMultimap.<LinkKey, IntentId>create());
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected TopologyService topologyService;
+
+ private ExecutorService executorService =
+ newSingleThreadExecutor(namedThreads("onos-flowtracker"));
+
+ private TopologyListener listener = new InternalTopologyListener();
+ private TopologyChangeDelegate delegate;
+
+ @Activate
+ public void activate() {
+ topologyService.addListener(listener);
+ log.info("Started");
+ }
+
+ @Deactivate
+ public void deactivate() {
+ topologyService.removeListener(listener);
+ log.info("Stopped");
+ }
+
+ @Override
+ public void setDelegate(TopologyChangeDelegate delegate) {
+ checkNotNull(delegate, "Delegate cannot be null");
+ checkArgument(this.delegate == null || this.delegate == delegate,
+ "Another delegate already set");
+ this.delegate = delegate;
+ }
+
+ @Override
+ public void unsetDelegate(TopologyChangeDelegate delegate) {
+ checkArgument(this.delegate == delegate, "Not the current delegate");
+ this.delegate = null;
+ }
+
+ @Override
+ public void addTrackedResources(IntentId intentId, Collection<Link> resources) {
+ for (Link link : resources) {
+ intentsByLink.put(new LinkKey(link), intentId);
+ }
+ }
+
+ @Override
+ public void removeTrackedResources(IntentId intentId, Collection<Link> resources) {
+ for (Link link : resources) {
+ intentsByLink.remove(new LinkKey(link), intentId);
+ }
+ }
+
+ // Internal re-actor to topology change events.
+ private class InternalTopologyListener implements TopologyListener {
+ @Override
+ public void event(TopologyEvent event) {
+ executorService.execute(new TopologyChangeHandler(event));
+ }
+ }
+
+ // Re-dispatcher of topology change events.
+ private class TopologyChangeHandler implements Runnable {
+
+ private final TopologyEvent event;
+
+ TopologyChangeHandler(TopologyEvent event) {
+ this.event = event;
+ }
+
+ @Override
+ public void run() {
+ if (event.reasons() == null) {
+ delegate.triggerCompile(new HashSet<IntentId>(), true);
+
+ } else {
+ Set<IntentId> toBeRecompiled = new HashSet<>();
+ boolean recompileOnly = true;
+
+ // Scan through the list of reasons and keep accruing all
+ // intents that need to be recompiled.
+ for (Event reason : event.reasons()) {
+ if (reason instanceof LinkEvent) {
+ LinkEvent linkEvent = (LinkEvent) reason;
+ if (linkEvent.type() == LINK_REMOVED) {
+ Set<IntentId> intentIds = intentsByLink.get(new LinkKey(linkEvent.subject()));
+ toBeRecompiled.addAll(intentIds);
+ }
+ recompileOnly = recompileOnly && linkEvent.type() == LINK_REMOVED;
+ }
+ }
+
+ delegate.triggerCompile(toBeRecompiled, !recompileOnly);
+ }
+ }
+ }
+
+}
diff --git a/core/net/src/main/java/org/onlab/onos/net/intent/impl/ObjectiveTrackerService.java b/core/net/src/main/java/org/onlab/onos/net/intent/impl/ObjectiveTrackerService.java
new file mode 100644
index 0000000..15496ff
--- /dev/null
+++ b/core/net/src/main/java/org/onlab/onos/net/intent/impl/ObjectiveTrackerService.java
@@ -0,0 +1,44 @@
+package org.onlab.onos.net.intent.impl;
+
+import org.onlab.onos.net.Link;
+import org.onlab.onos.net.intent.IntentId;
+
+import java.util.Collection;
+
+/**
+ * Auxiliary service for tracking intent path flows and for notifying the
+ * intent service of environment changes via topology change delegate.
+ */
+public interface ObjectiveTrackerService {
+
+ /**
+ * Sets a topology change delegate.
+ *
+ * @param delegate topology change delegate
+ */
+ void setDelegate(TopologyChangeDelegate delegate);
+
+ /**
+ * Unsets topology change delegate.
+ *
+ * @param delegate topology change delegate
+ */
+ void unsetDelegate(TopologyChangeDelegate delegate);
+
+ /**
+ * Adds a path flow to be tracked.
+ *
+ * @param intentId intent identity on whose behalf the path is being tracked
+ * @param resources resources to track
+ */
+ public void addTrackedResources(IntentId intentId, Collection<Link> resources);
+
+ /**
+ * Removes a path flow to be tracked.
+ *
+ * @param intentId intent identity on whose behalf the path is being tracked
+ * @param resources resources to stop tracking
+ */
+ public void removeTrackedResources(IntentId intentId, Collection<Link> resources);
+
+}
diff --git a/core/net/src/main/java/org/onlab/onos/net/intent/impl/PathIntentInstaller.java b/core/net/src/main/java/org/onlab/onos/net/intent/impl/PathIntentInstaller.java
new file mode 100644
index 0000000..0ca75c2
--- /dev/null
+++ b/core/net/src/main/java/org/onlab/onos/net/intent/impl/PathIntentInstaller.java
@@ -0,0 +1,117 @@
+package org.onlab.onos.net.intent.impl;
+
+import static org.onlab.onos.net.flow.DefaultTrafficTreatment.builder;
+import static org.slf4j.LoggerFactory.getLogger;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.onlab.onos.ApplicationId;
+import org.onlab.onos.net.ConnectPoint;
+import org.onlab.onos.net.Link;
+import org.onlab.onos.net.flow.DefaultFlowRule;
+import org.onlab.onos.net.flow.DefaultTrafficSelector;
+import org.onlab.onos.net.flow.FlowRule;
+import org.onlab.onos.net.flow.FlowRuleBatchEntry;
+import org.onlab.onos.net.flow.FlowRuleBatchEntry.FlowRuleOperation;
+import org.onlab.onos.net.flow.FlowRuleBatchOperation;
+import org.onlab.onos.net.flow.FlowRuleService;
+import org.onlab.onos.net.flow.TrafficSelector;
+import org.onlab.onos.net.flow.TrafficTreatment;
+import org.onlab.onos.net.intent.IntentExtensionService;
+import org.onlab.onos.net.intent.IntentInstaller;
+import org.onlab.onos.net.intent.PathIntent;
+import org.slf4j.Logger;
+
+import com.google.common.collect.Lists;
+
+/**
+ * Installer for {@link PathIntent path connectivity intents}.
+ */
+@Component(immediate = true)
+public class PathIntentInstaller implements IntentInstaller<PathIntent> {
+
+ private final Logger log = getLogger(getClass());
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected IntentExtensionService intentManager;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected FlowRuleService flowRuleService;
+
+ private final ApplicationId appId = ApplicationId.getAppId();
+
+ @Activate
+ public void activate() {
+ intentManager.registerInstaller(PathIntent.class, this);
+ }
+
+ @Deactivate
+ public void deactivate() {
+ intentManager.unregisterInstaller(PathIntent.class);
+ }
+
+ @Override
+ public void install(PathIntent intent) {
+ TrafficSelector.Builder builder =
+ DefaultTrafficSelector.builder(intent.selector());
+ Iterator<Link> links = intent.path().links().iterator();
+ ConnectPoint prev = links.next().dst();
+ List<FlowRuleBatchEntry> rules = Lists.newLinkedList();
+ while (links.hasNext()) {
+ builder.matchInport(prev.port());
+ Link link = links.next();
+ TrafficTreatment treatment = builder()
+ .setOutput(link.src().port()).build();
+
+ FlowRule rule = new DefaultFlowRule(link.src().deviceId(),
+ builder.build(), treatment,
+ 123, appId, 600);
+ rules.add(new FlowRuleBatchEntry(FlowRuleOperation.ADD, rule));
+ //flowRuleService.applyFlowRules(rule);
+ prev = link.dst();
+ }
+ FlowRuleBatchOperation batch = new FlowRuleBatchOperation(rules);
+ try {
+ flowRuleService.applyBatch(batch).get();
+ } catch (InterruptedException | ExecutionException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+
+ @Override
+ public void uninstall(PathIntent intent) {
+ TrafficSelector.Builder builder =
+ DefaultTrafficSelector.builder(intent.selector());
+ Iterator<Link> links = intent.path().links().iterator();
+ ConnectPoint prev = links.next().dst();
+ List<FlowRuleBatchEntry> rules = Lists.newLinkedList();
+
+ while (links.hasNext()) {
+ builder.matchInport(prev.port());
+ Link link = links.next();
+ TrafficTreatment treatment = builder()
+ .setOutput(link.src().port()).build();
+ FlowRule rule = new DefaultFlowRule(link.src().deviceId(),
+ builder.build(), treatment,
+ 123, appId, 600);
+ rules.add(new FlowRuleBatchEntry(FlowRuleOperation.REMOVE, rule));
+ //flowRuleService.removeFlowRules(rule);
+ prev = link.dst();
+ }
+ FlowRuleBatchOperation batch = new FlowRuleBatchOperation(rules);
+ try {
+ flowRuleService.applyBatch(batch).get();
+ } catch (InterruptedException | ExecutionException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/core/net/src/main/java/org/onlab/onos/net/intent/impl/PathNotFoundException.java b/core/net/src/main/java/org/onlab/onos/net/intent/impl/PathNotFoundException.java
new file mode 100644
index 0000000..a1fd63a
--- /dev/null
+++ b/core/net/src/main/java/org/onlab/onos/net/intent/impl/PathNotFoundException.java
@@ -0,0 +1,22 @@
+package org.onlab.onos.net.intent.impl;
+
+import org.onlab.onos.net.intent.IntentException;
+
+/**
+ * An exception thrown when a path is not found.
+ */
+public class PathNotFoundException extends IntentException {
+ private static final long serialVersionUID = -2087045731049914733L;
+
+ public PathNotFoundException() {
+ super();
+ }
+
+ public PathNotFoundException(String message) {
+ super(message);
+ }
+
+ public PathNotFoundException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/core/net/src/main/java/org/onlab/onos/net/intent/impl/PointToPointIntentCompiler.java b/core/net/src/main/java/org/onlab/onos/net/intent/impl/PointToPointIntentCompiler.java
new file mode 100644
index 0000000..0bd1703
--- /dev/null
+++ b/core/net/src/main/java/org/onlab/onos/net/intent/impl/PointToPointIntentCompiler.java
@@ -0,0 +1,105 @@
+package org.onlab.onos.net.intent.impl;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.onlab.onos.net.ConnectPoint;
+import org.onlab.onos.net.DefaultEdgeLink;
+import org.onlab.onos.net.DefaultPath;
+import org.onlab.onos.net.Link;
+import org.onlab.onos.net.Path;
+import org.onlab.onos.net.host.HostService;
+import org.onlab.onos.net.intent.IdGenerator;
+import org.onlab.onos.net.intent.Intent;
+import org.onlab.onos.net.intent.IntentCompiler;
+import org.onlab.onos.net.intent.IntentExtensionService;
+import org.onlab.onos.net.intent.IntentId;
+import org.onlab.onos.net.intent.PathIntent;
+import org.onlab.onos.net.intent.PointToPointIntent;
+import org.onlab.onos.net.provider.ProviderId;
+import org.onlab.onos.net.topology.PathService;
+
+/**
+ * A intent compiler for {@link org.onlab.onos.net.intent.HostToHostIntent}.
+ */
+@Component(immediate = true)
+public class PointToPointIntentCompiler
+ implements IntentCompiler<PointToPointIntent> {
+
+ private static final ProviderId PID = new ProviderId("core", "org.onlab.onos.core", true);
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected IntentExtensionService intentManager;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected PathService pathService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected HostService hostService;
+
+ private IdGenerator<IntentId> intentIdGenerator;
+
+ @Activate
+ public void activate() {
+ IdBlockAllocator idBlockAllocator = new DummyIdBlockAllocator();
+ intentIdGenerator = new IdBlockAllocatorBasedIntentIdGenerator(idBlockAllocator);
+ intentManager.registerCompiler(PointToPointIntent.class, this);
+ }
+
+ @Deactivate
+ public void deactivate() {
+ intentManager.unregisterCompiler(PointToPointIntent.class);
+ }
+
+ @Override
+ public List<Intent> compile(PointToPointIntent intent) {
+ Path path = getPath(intent.ingressPoint(), intent.egressPoint());
+
+ List<Link> links = new ArrayList<>();
+ links.add(DefaultEdgeLink.createEdgeLink(intent.ingressPoint(), true));
+ links.addAll(path.links());
+ links.add(DefaultEdgeLink.createEdgeLink(intent.egressPoint(), false));
+
+ return Arrays.asList(createPathIntent(new DefaultPath(PID, links, path.cost() + 2,
+ path.annotations()),
+ intent));
+ }
+
+ /**
+ * Creates a path intent from the specified path and original
+ * connectivity intent.
+ *
+ * @param path path to create an intent for
+ * @param intent original intent
+ */
+ private Intent createPathIntent(Path path,
+ PointToPointIntent intent) {
+
+ return new PathIntent(intentIdGenerator.getNewId(),
+ intent.selector(), intent.treatment(),
+ path.src(), path.dst(), path);
+ }
+
+ /**
+ * Computes a path between two ConnectPoints.
+ *
+ * @param one start of the path
+ * @param two end of the path
+ * @return Path between the two
+ * @throws PathNotFoundException if a path cannot be found
+ */
+ private Path getPath(ConnectPoint one, ConnectPoint two) {
+ Set<Path> paths = pathService.getPaths(one.deviceId(), two.deviceId());
+ if (paths.isEmpty()) {
+ throw new PathNotFoundException("No path from " + one + " to " + two);
+ }
+ // TODO: let's be more intelligent about this eventually
+ return paths.iterator().next();
+ }
+}
diff --git a/core/net/src/main/java/org/onlab/onos/net/intent/impl/TopologyChangeDelegate.java b/core/net/src/main/java/org/onlab/onos/net/intent/impl/TopologyChangeDelegate.java
new file mode 100644
index 0000000..30e6899
--- /dev/null
+++ b/core/net/src/main/java/org/onlab/onos/net/intent/impl/TopologyChangeDelegate.java
@@ -0,0 +1,22 @@
+package org.onlab.onos.net.intent.impl;
+
+import org.onlab.onos.net.intent.IntentId;
+
+/**
+ * Auxiliary delegate for integration of intent manager and flow trackerService.
+ */
+public interface TopologyChangeDelegate {
+
+ /**
+ * Notifies that topology has changed in such a way that the specified
+ * intents should be recompiled. If the {@code compileAllFailed} parameter
+ * is true, then all intents in {@link org.onlab.onos.net.intent.IntentState#FAILED}
+ * state should be compiled as well.
+ *
+ * @param intentIds intents that should be recompiled
+ * @param compileAllFailed true implies full compile of all failed intents
+ * is required; false for selective recompile only
+ */
+ void triggerCompile(Iterable<IntentId> intentIds, boolean compileAllFailed);
+
+}
diff --git a/core/net/src/main/java/org/onlab/onos/net/intent/impl/UnavailableIdException.java b/core/net/src/main/java/org/onlab/onos/net/intent/impl/UnavailableIdException.java
new file mode 100644
index 0000000..fd4f122
--- /dev/null
+++ b/core/net/src/main/java/org/onlab/onos/net/intent/impl/UnavailableIdException.java
@@ -0,0 +1,34 @@
+package org.onlab.onos.net.intent.impl;
+
+/**
+ * Represents that there is no available IDs.
+ */
+public class UnavailableIdException extends RuntimeException {
+
+ private static final long serialVersionUID = -2287403908433720122L;
+
+ /**
+ * Constructs an exception with no message and no underlying cause.
+ */
+ public UnavailableIdException() {
+ }
+
+ /**
+ * Constructs an exception with the specified message.
+ *
+ * @param message the message describing the specific nature of the error
+ */
+ public UnavailableIdException(String message) {
+ super(message);
+ }
+
+ /**
+ * Constructs an exception with the specified message and the underlying cause.
+ *
+ * @param message the message describing the specific nature of the error
+ * @param cause the underlying cause of this error
+ */
+ public UnavailableIdException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/core/net/src/main/java/org/onlab/onos/net/intent/impl/package-info.java b/core/net/src/main/java/org/onlab/onos/net/intent/impl/package-info.java
new file mode 100644
index 0000000..3f00271
--- /dev/null
+++ b/core/net/src/main/java/org/onlab/onos/net/intent/impl/package-info.java
@@ -0,0 +1,5 @@
+/**
+ * Core subsystem for tracking high-level intents for treatment of selected
+ * network traffic.
+ */
+package org.onlab.onos.net.intent.impl;
\ No newline at end of file
diff --git a/core/net/src/main/java/org/onlab/onos/net/proxyarp/impl/ProxyArpManager.java b/core/net/src/main/java/org/onlab/onos/net/proxyarp/impl/ProxyArpManager.java
index 6e07c3e..4933322 100644
--- a/core/net/src/main/java/org/onlab/onos/net/proxyarp/impl/ProxyArpManager.java
+++ b/core/net/src/main/java/org/onlab/onos/net/proxyarp/impl/ProxyArpManager.java
@@ -31,6 +31,8 @@
import org.onlab.onos.net.link.LinkListener;
import org.onlab.onos.net.link.LinkService;
import org.onlab.onos.net.packet.DefaultOutboundPacket;
+import org.onlab.onos.net.packet.InboundPacket;
+import org.onlab.onos.net.packet.PacketContext;
import org.onlab.onos.net.packet.PacketService;
import org.onlab.onos.net.proxyarp.ProxyArpService;
import org.onlab.packet.ARP;
@@ -43,7 +45,6 @@
import com.google.common.collect.Lists;
import com.google.common.collect.Multimap;
-
@Component(immediate = true)
@Service
public class ProxyArpManager implements ProxyArpService {
@@ -93,14 +94,14 @@
@Override
public boolean known(IpPrefix addr) {
- checkNotNull(MAC_ADDR_NULL, addr);
+ checkNotNull(addr, MAC_ADDR_NULL);
Set<Host> hosts = hostService.getHostsByIp(addr);
return !hosts.isEmpty();
}
@Override
public void reply(Ethernet eth) {
- checkNotNull(REQUEST_NULL, eth);
+ checkNotNull(eth, REQUEST_NULL);
checkArgument(eth.getEtherType() == Ethernet.TYPE_ARP,
REQUEST_NOT_ARP);
ARP arp = (ARP) eth.getPayload();
@@ -128,7 +129,7 @@
Ethernet arpReply = buildArpReply(dst, eth);
// TODO: check send status with host service.
- TrafficTreatment.Builder builder = new DefaultTrafficTreatment.Builder();
+ TrafficTreatment.Builder builder = DefaultTrafficTreatment.builder();
builder.setOutput(src.location().port());
packetService.emit(new DefaultOutboundPacket(src.location().deviceId(),
builder.build(), ByteBuffer.wrap(arpReply.serialize())));
@@ -136,7 +137,7 @@
@Override
public void forward(Ethernet eth) {
- checkNotNull(REQUEST_NULL, eth);
+ checkNotNull(eth, REQUEST_NULL);
checkArgument(eth.getEtherType() == Ethernet.TYPE_ARP,
REQUEST_NOT_ARP);
ARP arp = (ARP) eth.getPayload();
@@ -148,7 +149,7 @@
if (h == null) {
flood(eth);
} else {
- TrafficTreatment.Builder builder = new DefaultTrafficTreatment.Builder();
+ TrafficTreatment.Builder builder = DefaultTrafficTreatment.builder();
builder.setOutput(h.location().port());
packetService.emit(new DefaultOutboundPacket(h.location().deviceId(),
builder.build(), ByteBuffer.wrap(eth.serialize())));
@@ -156,6 +157,23 @@
}
+ @Override
+ public boolean handleArp(PacketContext context) {
+ InboundPacket pkt = context.inPacket();
+ Ethernet ethPkt = pkt.parsed();
+ if (ethPkt.getEtherType() == Ethernet.TYPE_ARP) {
+ ARP arp = (ARP) ethPkt.getPayload();
+ if (arp.getOpCode() == ARP.OP_REPLY) {
+ forward(ethPkt);
+ } else if (arp.getOpCode() == ARP.OP_REQUEST) {
+ reply(ethPkt);
+ }
+ context.block();
+ return true;
+ }
+ return false;
+ }
+
/**
* Flood the arp request at all edges in the network.
* @param request the arp request.
@@ -166,7 +184,7 @@
synchronized (externalPorts) {
for (Entry<Device, PortNumber> entry : externalPorts.entries()) {
- builder = new DefaultTrafficTreatment.Builder();
+ builder = DefaultTrafficTreatment.builder();
builder.setOutput(entry.getValue());
packetService.emit(new DefaultOutboundPacket(entry.getKey().id(),
builder.build(), buf));
@@ -188,12 +206,12 @@
for (Link l : links) {
// for each link, mark the concerned ports as internal
// and the remaining ports are therefore external.
- if (l.src().deviceId().equals(d)
+ if (l.src().deviceId().equals(d.id())
&& ports.contains(l.src().port())) {
ports.remove(l.src().port());
internalPorts.put(d, l.src().port());
}
- if (l.dst().deviceId().equals(d)
+ if (l.dst().deviceId().equals(d.id())
&& ports.contains(l.dst().port())) {
ports.remove(l.dst().port());
internalPorts.put(d, l.dst().port());
@@ -322,7 +340,6 @@
}
-}
-
+ }
}
diff --git a/core/net/src/test/java/org/onlab/onos/net/device/impl/DeviceManagerTest.java b/core/net/src/test/java/org/onlab/onos/net/device/impl/DeviceManagerTest.java
index ea47b0c..e833b4a 100644
--- a/core/net/src/test/java/org/onlab/onos/net/device/impl/DeviceManagerTest.java
+++ b/core/net/src/test/java/org/onlab/onos/net/device/impl/DeviceManagerTest.java
@@ -6,8 +6,15 @@
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
+import org.onlab.onos.cluster.ClusterEventListener;
+import org.onlab.onos.cluster.ClusterService;
+import org.onlab.onos.cluster.ControllerNode;
+import org.onlab.onos.cluster.DefaultControllerNode;
import org.onlab.onos.cluster.MastershipServiceAdapter;
+import org.onlab.onos.cluster.MastershipTerm;
+import org.onlab.onos.cluster.MastershipTermService;
import org.onlab.onos.cluster.NodeId;
+import org.onlab.onos.cluster.ControllerNode.State;
import org.onlab.onos.event.Event;
import org.onlab.onos.event.impl.TestEventDispatcher;
import org.onlab.onos.net.Device;
@@ -28,7 +35,9 @@
import org.onlab.onos.net.device.PortDescription;
import org.onlab.onos.net.provider.AbstractProvider;
import org.onlab.onos.net.provider.ProviderId;
+import org.onlab.onos.store.ClockProviderService;
import org.onlab.onos.store.trivial.impl.SimpleDeviceStore;
+import org.onlab.packet.IpPrefix;
import java.util.ArrayList;
import java.util.Iterator;
@@ -57,6 +66,8 @@
private static final PortNumber P1 = PortNumber.portNumber(1);
private static final PortNumber P2 = PortNumber.portNumber(2);
private static final PortNumber P3 = PortNumber.portNumber(3);
+ private static final NodeId NID_LOCAL = new NodeId("local");
+ private static final IpPrefix LOCALHOST = IpPrefix.valueOf("127.0.0.1");
private DeviceManager mgr;
@@ -76,6 +87,8 @@
mgr.store = new SimpleDeviceStore();
mgr.eventDispatcher = new TestEventDispatcher();
mgr.mastershipService = new TestMastershipService();
+ mgr.clusterService = new TestClusterService();
+ mgr.clockProviderService = new TestClockProviderService();
mgr.activate();
service.addListener(listener);
@@ -275,6 +288,59 @@
public MastershipRole requestRoleFor(DeviceId deviceId) {
return MastershipRole.MASTER;
}
+
+ @Override
+ public MastershipTermService requestTermService() {
+ return new MastershipTermService() {
+ @Override
+ public MastershipTerm getMastershipTerm(DeviceId deviceId) {
+ // FIXME: just returning something not null
+ return MastershipTerm.of(NID_LOCAL, 1);
+ }
+ };
+ }
}
+ // code clone
+ private final class TestClusterService implements ClusterService {
+
+ ControllerNode local = new DefaultControllerNode(NID_LOCAL, LOCALHOST);
+
+ @Override
+ public ControllerNode getLocalNode() {
+ return local;
+ }
+
+ @Override
+ public Set<ControllerNode> getNodes() {
+ return null;
+ }
+
+ @Override
+ public ControllerNode getNode(NodeId nodeId) {
+ return null;
+ }
+
+ @Override
+ public State getState(NodeId nodeId) {
+ return null;
+ }
+
+ @Override
+ public void addListener(ClusterEventListener listener) {
+ }
+
+ @Override
+ public void removeListener(ClusterEventListener listener) {
+ }
+ }
+
+ private final class TestClockProviderService implements
+ ClockProviderService {
+
+ @Override
+ public void setMastershipTerm(DeviceId deviceId, MastershipTerm term) {
+ // TODO Auto-generated method stub
+ }
+ }
}
diff --git a/core/net/src/test/java/org/onlab/onos/net/device/impl/DistributedDeviceManagerTest.java b/core/net/src/test/java/org/onlab/onos/net/device/impl/DistributedDeviceManagerTest.java
deleted file mode 100644
index 10e9b39..0000000
--- a/core/net/src/test/java/org/onlab/onos/net/device/impl/DistributedDeviceManagerTest.java
+++ /dev/null
@@ -1,355 +0,0 @@
-package org.onlab.onos.net.device.impl;
-
-import com.google.common.collect.Iterables;
-import com.google.common.collect.Sets;
-import com.hazelcast.config.Config;
-import com.hazelcast.core.Hazelcast;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.onlab.onos.cluster.DefaultControllerNode;
-import org.onlab.onos.cluster.MastershipServiceAdapter;
-import org.onlab.onos.cluster.NodeId;
-import org.onlab.onos.event.Event;
-import org.onlab.onos.event.EventDeliveryService;
-import org.onlab.onos.event.impl.TestEventDispatcher;
-import org.onlab.onos.net.Device;
-import org.onlab.onos.net.DeviceId;
-import org.onlab.onos.net.MastershipRole;
-import org.onlab.onos.net.Port;
-import org.onlab.onos.net.PortNumber;
-import org.onlab.onos.net.device.DefaultDeviceDescription;
-import org.onlab.onos.net.device.DefaultPortDescription;
-import org.onlab.onos.net.device.DeviceAdminService;
-import org.onlab.onos.net.device.DeviceDescription;
-import org.onlab.onos.net.device.DeviceEvent;
-import org.onlab.onos.net.device.DeviceListener;
-import org.onlab.onos.net.device.DeviceProvider;
-import org.onlab.onos.net.device.DeviceProviderRegistry;
-import org.onlab.onos.net.device.DeviceProviderService;
-import org.onlab.onos.net.device.DeviceService;
-import org.onlab.onos.net.device.PortDescription;
-import org.onlab.onos.net.provider.AbstractProvider;
-import org.onlab.onos.net.provider.ProviderId;
-import org.onlab.onos.store.common.StoreManager;
-import org.onlab.onos.store.common.StoreService;
-import org.onlab.onos.store.common.TestStoreManager;
-import org.onlab.onos.store.device.impl.DistributedDeviceStore;
-import org.onlab.onos.store.serializers.KryoSerializationManager;
-import org.onlab.onos.store.serializers.KryoSerializationService;
-import org.onlab.packet.IpPrefix;
-
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map.Entry;
-import java.util.Set;
-import java.util.concurrent.BlockingQueue;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
-import java.util.concurrent.LinkedBlockingQueue;
-import java.util.concurrent.TimeUnit;
-
-import static org.junit.Assert.*;
-import static org.onlab.onos.net.Device.Type.SWITCH;
-import static org.onlab.onos.net.DeviceId.deviceId;
-import static org.onlab.onos.net.device.DeviceEvent.Type.*;
-
-// FIXME This test is slow starting up Hazelcast on each test cases.
-// FIXME DistributedDeviceStore should have it's own test cases.
-
-/**
- * Test codifying the device service & device provider service contracts.
- */
-public class DistributedDeviceManagerTest {
-
- private static final ProviderId PID = new ProviderId("of", "foo");
- private static final DeviceId DID1 = deviceId("of:foo");
- private static final DeviceId DID2 = deviceId("of:bar");
- private static final String MFR = "whitebox";
- private static final String HW = "1.1.x";
- private static final String SW1 = "3.8.1";
- private static final String SW2 = "3.9.5";
- private static final String SN = "43311-12345";
-
- private static final PortNumber P1 = PortNumber.portNumber(1);
- private static final PortNumber P2 = PortNumber.portNumber(2);
- private static final PortNumber P3 = PortNumber.portNumber(3);
-
- private static final DefaultControllerNode SELF
- = new DefaultControllerNode(new NodeId("foobar"),
- IpPrefix.valueOf("127.0.0.1"));
-
-
- private DeviceManager mgr;
-
- protected StoreManager storeManager;
- protected DeviceService service;
- protected DeviceAdminService admin;
- protected DeviceProviderRegistry registry;
- protected DeviceProviderService providerService;
- protected TestProvider provider;
- protected TestListener listener = new TestListener();
- private DistributedDeviceStore dstore;
- private TestMastershipManager masterManager;
- private EventDeliveryService eventService;
- private KryoSerializationManager serializationMgr;
-
- @Before
- public void setUp() {
- mgr = new DeviceManager();
- service = mgr;
- admin = mgr;
- registry = mgr;
- // TODO should find a way to clean Hazelcast instance without shutdown.
- Config config = TestStoreManager.getTestConfig();
-
- masterManager = new TestMastershipManager();
-
- storeManager = new TestStoreManager(Hazelcast.newHazelcastInstance(config));
- storeManager.activate();
-
- serializationMgr = new KryoSerializationManager();
- serializationMgr.activate();
-
- dstore = new TestDistributedDeviceStore(storeManager, serializationMgr);
- dstore.activate();
-
- mgr.store = dstore;
- eventService = new TestEventDispatcher();
- mgr.eventDispatcher = eventService;
- mgr.mastershipService = masterManager;
- mgr.activate();
-
- service.addListener(listener);
-
- provider = new TestProvider();
- providerService = registry.register(provider);
- assertTrue("provider should be registered",
- registry.getProviders().contains(provider.id()));
- }
-
- @After
- public void tearDown() {
- registry.unregister(provider);
- assertFalse("provider should not be registered",
- registry.getProviders().contains(provider.id()));
- service.removeListener(listener);
- mgr.deactivate();
-
- dstore.deactivate();
- serializationMgr.deactivate();
- storeManager.deactivate();
- }
-
- private void connectDevice(DeviceId deviceId, String swVersion) {
- DeviceDescription description =
- new DefaultDeviceDescription(deviceId.uri(), SWITCH, MFR,
- HW, swVersion, SN);
- providerService.deviceConnected(deviceId, description);
- assertNotNull("device should be found", service.getDevice(DID1));
- }
-
- @Test
- public void deviceConnected() {
- assertNull("device should not be found", service.getDevice(DID1));
- connectDevice(DID1, SW1);
- validateEvents(DEVICE_ADDED);
-
- assertEquals("only one device expected", 1, Iterables.size(service.getDevices()));
- Iterator<Device> it = service.getDevices().iterator();
- assertNotNull("one device expected", it.next());
- assertFalse("only one device expected", it.hasNext());
-
- assertEquals("incorrect device count", 1, service.getDeviceCount());
- assertTrue("device should be available", service.isAvailable(DID1));
- }
-
- @Test
- public void deviceDisconnected() {
- connectDevice(DID1, SW1);
- connectDevice(DID2, SW1);
- validateEvents(DEVICE_ADDED, DEVICE_ADDED);
- assertTrue("device should be available", service.isAvailable(DID1));
-
- // Disconnect
- providerService.deviceDisconnected(DID1);
- assertNotNull("device should not be found", service.getDevice(DID1));
- assertFalse("device should not be available", service.isAvailable(DID1));
- validateEvents(DEVICE_AVAILABILITY_CHANGED);
-
- // Reconnect
- connectDevice(DID1, SW1);
- validateEvents(DEVICE_AVAILABILITY_CHANGED);
-
- assertEquals("incorrect device count", 2, service.getDeviceCount());
- }
-
- @Test
- public void deviceUpdated() {
- connectDevice(DID1, SW1);
- validateEvents(DEVICE_ADDED);
-
- connectDevice(DID1, SW2);
- validateEvents(DEVICE_UPDATED);
- }
-
- @Test
- public void getRole() {
- connectDevice(DID1, SW1);
- assertEquals("incorrect role", MastershipRole.MASTER, service.getRole(DID1));
- }
-
- @Test
- public void updatePorts() {
- connectDevice(DID1, SW1);
- List<PortDescription> pds = new ArrayList<>();
- pds.add(new DefaultPortDescription(P1, true));
- pds.add(new DefaultPortDescription(P2, true));
- pds.add(new DefaultPortDescription(P3, true));
- providerService.updatePorts(DID1, pds);
- validateEvents(DEVICE_ADDED, PORT_ADDED, PORT_ADDED, PORT_ADDED);
- pds.clear();
-
- pds.add(new DefaultPortDescription(P1, false));
- pds.add(new DefaultPortDescription(P3, true));
- providerService.updatePorts(DID1, pds);
- validateEvents(PORT_UPDATED, PORT_REMOVED);
- }
-
- @Test
- public void updatePortStatus() {
- connectDevice(DID1, SW1);
- List<PortDescription> pds = new ArrayList<>();
- pds.add(new DefaultPortDescription(P1, true));
- pds.add(new DefaultPortDescription(P2, true));
- providerService.updatePorts(DID1, pds);
- validateEvents(DEVICE_ADDED, PORT_ADDED, PORT_ADDED);
-
- providerService.portStatusChanged(DID1, new DefaultPortDescription(P1, false));
- validateEvents(PORT_UPDATED);
- providerService.portStatusChanged(DID1, new DefaultPortDescription(P1, false));
- assertTrue("no events expected", listener.events.isEmpty());
- }
-
- @Test
- public void getPorts() {
- connectDevice(DID1, SW1);
- List<PortDescription> pds = new ArrayList<>();
- pds.add(new DefaultPortDescription(P1, true));
- pds.add(new DefaultPortDescription(P2, true));
- providerService.updatePorts(DID1, pds);
- validateEvents(DEVICE_ADDED, PORT_ADDED, PORT_ADDED);
- assertEquals("wrong port count", 2, service.getPorts(DID1).size());
-
- Port port = service.getPort(DID1, P1);
- assertEquals("incorrect port", P1, service.getPort(DID1, P1).number());
- assertEquals("incorrect state", true, service.getPort(DID1, P1).isEnabled());
- }
-
- @Test
- public void removeDevice() {
- connectDevice(DID1, SW1);
- connectDevice(DID2, SW2);
- assertEquals("incorrect device count", 2, service.getDeviceCount());
- admin.removeDevice(DID1);
- validateEvents(DEVICE_ADDED, DEVICE_ADDED, DEVICE_REMOVED);
- assertNull("device should not be found", service.getDevice(DID1));
- assertNotNull("device should be found", service.getDevice(DID2));
- assertEquals("incorrect device count", 1, service.getDeviceCount());
- }
-
- protected void validateEvents(Enum... types) {
- for (Enum type : types) {
- try {
- Event event = listener.events.poll(1, TimeUnit.SECONDS);
- assertNotNull("Timed out waiting for " + event, event);
- assertEquals("incorrect event type", type, event.type());
- } catch (InterruptedException e) {
- fail("Unexpected interrupt");
- }
- }
- assertTrue("Unexpected events left", listener.events.isEmpty());
- listener.events.clear();
- }
-
-
- private class TestProvider extends AbstractProvider implements DeviceProvider {
- private Device deviceReceived;
- private MastershipRole roleReceived;
-
- public TestProvider() {
- super(PID);
- }
-
- @Override
- public void triggerProbe(Device device) {
- }
-
- @Override
- public void roleChanged(Device device, MastershipRole newRole) {
- deviceReceived = device;
- roleReceived = newRole;
- }
- }
-
- private static class TestListener implements DeviceListener {
- final BlockingQueue<DeviceEvent> events = new LinkedBlockingQueue<>();
-
- @Override
- public void event(DeviceEvent event) {
- events.add(event);
- }
- }
-
- private class TestDistributedDeviceStore extends DistributedDeviceStore {
-
- public TestDistributedDeviceStore(StoreService storeService,
- KryoSerializationService kryoSerializationService) {
- this.storeService = storeService;
- this.kryoSerializationService = kryoSerializationService;
- }
- }
-
- private static class TestMastershipManager extends MastershipServiceAdapter {
-
- private ConcurrentMap<DeviceId, NodeId> masters = new ConcurrentHashMap<>();
-
- public TestMastershipManager() {
- // SELF master of all initially
- masters.put(DID1, SELF.id());
- masters.put(DID1, SELF.id());
- }
- @Override
- public MastershipRole getLocalRole(DeviceId deviceId) {
- return MastershipRole.MASTER;
- }
-
- @Override
- public Set<DeviceId> getDevicesOf(NodeId nodeId) {
- HashSet<DeviceId> set = Sets.newHashSet();
- for (Entry<DeviceId, NodeId> e : masters.entrySet()) {
- if (e.getValue().equals(nodeId)) {
- set.add(e.getKey());
- }
- }
- return set;
- }
-
- @Override
- public MastershipRole requestRoleFor(DeviceId deviceId) {
- if (SELF.id().equals(masters.get(deviceId))) {
- return MastershipRole.MASTER;
- } else {
- return MastershipRole.STANDBY;
- }
- }
-
- @Override
- public void relinquishMastership(DeviceId deviceId) {
- masters.remove(deviceId, SELF.id());
- }
- }
-}
diff --git a/core/net/src/test/java/org/onlab/onos/net/flow/impl/FlowRuleManagerTest.java b/core/net/src/test/java/org/onlab/onos/net/flow/impl/FlowRuleManagerTest.java
index 0b451c0..86f3ddc 100644
--- a/core/net/src/test/java/org/onlab/onos/net/flow/impl/FlowRuleManagerTest.java
+++ b/core/net/src/test/java/org/onlab/onos/net/flow/impl/FlowRuleManagerTest.java
@@ -12,6 +12,7 @@
import java.util.Collections;
import java.util.List;
import java.util.Set;
+import java.util.concurrent.Future;
import org.junit.After;
import org.junit.Before;
@@ -27,9 +28,12 @@
import org.onlab.onos.net.PortNumber;
import org.onlab.onos.net.device.DeviceListener;
import org.onlab.onos.net.device.DeviceService;
+import org.onlab.onos.net.flow.DefaultFlowEntry;
import org.onlab.onos.net.flow.DefaultFlowRule;
+import org.onlab.onos.net.flow.FlowEntry;
+import org.onlab.onos.net.flow.FlowEntry.FlowEntryState;
import org.onlab.onos.net.flow.FlowRule;
-import org.onlab.onos.net.flow.FlowRule.FlowRuleState;
+import org.onlab.onos.net.flow.FlowRuleBatchEntry;
import org.onlab.onos.net.flow.FlowRuleEvent;
import org.onlab.onos.net.flow.FlowRuleListener;
import org.onlab.onos.net.flow.FlowRuleProvider;
@@ -40,6 +44,7 @@
import org.onlab.onos.net.flow.TrafficTreatment;
import org.onlab.onos.net.flow.criteria.Criterion;
import org.onlab.onos.net.flow.instructions.Instruction;
+import org.onlab.onos.net.intent.BatchOperation;
import org.onlab.onos.net.provider.AbstractProvider;
import org.onlab.onos.net.provider.ProviderId;
import org.onlab.onos.store.trivial.impl.SimpleFlowRuleStore;
@@ -100,12 +105,9 @@
private FlowRule flowRule(int tsval, int trval) {
TestSelector ts = new TestSelector(tsval);
TestTreatment tr = new TestTreatment(trval);
- return new DefaultFlowRule(DID, ts, tr, 0, appId, TIMEOUT);
+ return new DefaultFlowRule(DID, ts, tr, 10, appId, TIMEOUT);
}
- private FlowRule flowRule(FlowRule rule, FlowRuleState state) {
- return new DefaultFlowRule(rule, state);
- }
private FlowRule addFlowRule(int hval) {
FlowRule rule = flowRule(hval, hval);
@@ -143,24 +145,26 @@
FlowRule f1 = addFlowRule(1);
FlowRule f2 = addFlowRule(2);
+ FlowEntry fe1 = new DefaultFlowEntry(f1);
+ FlowEntry fe2 = new DefaultFlowEntry(f2);
assertEquals("2 rules should exist", 2, flowCount());
- providerService.pushFlowMetrics(DID, ImmutableList.of(f1, f2));
+ providerService.pushFlowMetrics(DID, ImmutableList.of(fe1, fe2));
validateEvents(RULE_ADDED, RULE_ADDED);
addFlowRule(1);
assertEquals("should still be 2 rules", 2, flowCount());
- providerService.pushFlowMetrics(DID, ImmutableList.of(f1));
+ providerService.pushFlowMetrics(DID, ImmutableList.of(fe1));
validateEvents(RULE_UPDATED);
}
//backing store is sensitive to the order of additions/removals
- private boolean validateState(FlowRuleState... state) {
- Iterable<FlowRule> rules = service.getFlowEntries(DID);
+ private boolean validateState(FlowEntryState... state) {
+ Iterable<FlowEntry> rules = service.getFlowEntries(DID);
int i = 0;
- for (FlowRule f : rules) {
+ for (FlowEntry f : rules) {
if (f.state() != state[i]) {
return false;
}
@@ -181,8 +185,8 @@
mgr.applyFlowRules(r1, r2, r3);
assertEquals("3 rules should exist", 3, flowCount());
assertTrue("Entries should be pending add.",
- validateState(FlowRuleState.PENDING_ADD, FlowRuleState.PENDING_ADD,
- FlowRuleState.PENDING_ADD));
+ validateState(FlowEntryState.PENDING_ADD, FlowEntryState.PENDING_ADD,
+ FlowEntryState.PENDING_ADD));
}
@Test
@@ -192,20 +196,21 @@
FlowRule f3 = addFlowRule(3);
assertEquals("3 rules should exist", 3, flowCount());
- providerService.pushFlowMetrics(DID, ImmutableList.of(f1, f2, f3));
+ FlowEntry fe1 = new DefaultFlowEntry(f1);
+ FlowEntry fe2 = new DefaultFlowEntry(f2);
+ FlowEntry fe3 = new DefaultFlowEntry(f3);
+ providerService.pushFlowMetrics(DID, ImmutableList.of(fe1, fe2, fe3));
validateEvents(RULE_ADDED, RULE_ADDED, RULE_ADDED);
- FlowRule rem1 = flowRule(f1, FlowRuleState.REMOVED);
- FlowRule rem2 = flowRule(f2, FlowRuleState.REMOVED);
- mgr.removeFlowRules(rem1, rem2);
+ mgr.removeFlowRules(f1, f2);
//removing from north, so no events generated
validateEvents();
assertEquals("3 rule should exist", 3, flowCount());
assertTrue("Entries should be pending remove.",
- validateState(FlowRuleState.CREATED, FlowRuleState.PENDING_REMOVE,
- FlowRuleState.PENDING_REMOVE));
+ validateState(FlowEntryState.PENDING_REMOVE, FlowEntryState.PENDING_REMOVE,
+ FlowEntryState.ADDED));
- mgr.removeFlowRules(rem1);
+ mgr.removeFlowRules(f1);
assertEquals("3 rule should still exist", 3, flowCount());
}
@@ -213,21 +218,24 @@
public void flowRemoved() {
FlowRule f1 = addFlowRule(1);
FlowRule f2 = addFlowRule(2);
- providerService.pushFlowMetrics(f1.deviceId(), ImmutableList.of(f1, f2));
+ FlowEntry fe1 = new DefaultFlowEntry(f1);
+ FlowEntry fe2 = new DefaultFlowEntry(f2);
+ providerService.pushFlowMetrics(DID, ImmutableList.of(fe1, fe2));
service.removeFlowRules(f1);
- FlowRule rem1 = flowRule(f1, FlowRuleState.REMOVED);
- providerService.flowRemoved(rem1);
+ fe1.setState(FlowEntryState.REMOVED);
+ providerService.flowRemoved(fe1);
validateEvents(RULE_ADDED, RULE_ADDED, RULE_REMOVED);
- providerService.flowRemoved(rem1);
+ providerService.flowRemoved(fe1);
validateEvents();
FlowRule f3 = flowRule(3, 3);
+ FlowEntry fe3 = new DefaultFlowEntry(f3);
service.applyFlowRules(f3);
- providerService.pushFlowMetrics(f3.deviceId(), Collections.singletonList(f3));
+ providerService.pushFlowMetrics(DID, Collections.singletonList(fe3));
validateEvents(RULE_ADDED);
- providerService.flowRemoved(f3);
+ providerService.flowRemoved(fe3);
validateEvents();
}
@@ -237,17 +245,20 @@
FlowRule f2 = flowRule(2, 2);
FlowRule f3 = flowRule(3, 3);
-
-
mgr.applyFlowRules(f1, f2, f3);
- FlowRule updatedF1 = flowRule(f1, FlowRuleState.ADDED);
- FlowRule updatedF2 = flowRule(f2, FlowRuleState.ADDED);
- providerService.pushFlowMetrics(DID, Lists.newArrayList(updatedF1, updatedF2));
+ FlowEntry fe1 = new DefaultFlowEntry(f1);
+ FlowEntry fe2 = new DefaultFlowEntry(f2);
+
+
+ //FlowRule updatedF1 = flowRule(f1, FlowRuleState.ADDED);
+ //FlowRule updatedF2 = flowRule(f2, FlowRuleState.ADDED);
+
+ providerService.pushFlowMetrics(DID, Lists.newArrayList(fe1, fe2));
assertTrue("Entries should be added.",
- validateState(FlowRuleState.PENDING_ADD, FlowRuleState.ADDED,
- FlowRuleState.ADDED));
+ validateState(FlowEntryState.ADDED, FlowEntryState.ADDED,
+ FlowEntryState.PENDING_ADD));
validateEvents(RULE_ADDED, RULE_ADDED);
}
@@ -259,11 +270,15 @@
FlowRule f3 = flowRule(3, 3);
mgr.applyFlowRules(f1, f2);
- FlowRule updatedF1 = flowRule(f1, FlowRuleState.ADDED);
- FlowRule updatedF2 = flowRule(f2, FlowRuleState.ADDED);
- FlowRule updatedF3 = flowRule(f3, FlowRuleState.ADDED);
+// FlowRule updatedF1 = flowRule(f1, FlowRuleState.ADDED);
+// FlowRule updatedF2 = flowRule(f2, FlowRuleState.ADDED);
+// FlowRule updatedF3 = flowRule(f3, FlowRuleState.ADDED);
+ FlowEntry fe1 = new DefaultFlowEntry(f1);
+ FlowEntry fe2 = new DefaultFlowEntry(f2);
+ FlowEntry fe3 = new DefaultFlowEntry(f3);
- providerService.pushFlowMetrics(DID, Lists.newArrayList(updatedF1, updatedF2, updatedF3));
+
+ providerService.pushFlowMetrics(DID, Lists.newArrayList(fe1, fe2, fe3));
validateEvents(RULE_ADDED, RULE_ADDED);
@@ -279,13 +294,16 @@
FlowRule f2 = flowRule(2, 2);
FlowRule f3 = flowRule(3, 3);
- FlowRule updatedF1 = flowRule(f1, FlowRuleState.ADDED);
- FlowRule updatedF2 = flowRule(f2, FlowRuleState.ADDED);
+// FlowRule updatedF1 = flowRule(f1, FlowRuleState.ADDED);
+// FlowRule updatedF2 = flowRule(f2, FlowRuleState.ADDED);
+
+ FlowEntry fe1 = new DefaultFlowEntry(f1);
+ FlowEntry fe2 = new DefaultFlowEntry(f2);
mgr.applyFlowRules(f1, f2, f3);
mgr.removeFlowRules(f3);
- providerService.pushFlowMetrics(DID, Lists.newArrayList(updatedF1, updatedF2));
+ providerService.pushFlowMetrics(DID, Lists.newArrayList(fe1, fe2));
validateEvents(RULE_ADDED, RULE_ADDED, RULE_REMOVED);
@@ -312,7 +330,7 @@
//only check that we are in pending remove. Events and actual remove state will
// be set by flowRemoved call.
- validateState(FlowRuleState.PENDING_REMOVE, FlowRuleState.PENDING_REMOVE);
+ validateState(FlowEntryState.PENDING_REMOVE, FlowEntryState.PENDING_REMOVE);
}
private static class TestListener implements FlowRuleListener {
@@ -389,6 +407,13 @@
public void removeRulesById(ApplicationId id, FlowRule... flowRules) {
}
+ @Override
+ public Future<Void> executeBatch(
+ BatchOperation<FlowRuleBatchEntry> batch) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
}
diff --git a/core/net/src/test/java/org/onlab/onos/net/host/impl/HostMonitorTest.java b/core/net/src/test/java/org/onlab/onos/net/host/impl/HostMonitorTest.java
new file mode 100644
index 0000000..d8ddfbe
--- /dev/null
+++ b/core/net/src/test/java/org/onlab/onos/net/host/impl/HostMonitorTest.java
@@ -0,0 +1,223 @@
+package org.onlab.onos.net.host.impl;
+
+import static org.easymock.EasyMock.createMock;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.expectLastCall;
+import static org.easymock.EasyMock.replay;
+import static org.easymock.EasyMock.verify;
+import static org.junit.Assert.assertTrue;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+
+import org.junit.Ignore;
+import org.junit.Test;
+import org.onlab.onos.net.ConnectPoint;
+import org.onlab.onos.net.Device;
+import org.onlab.onos.net.DeviceId;
+import org.onlab.onos.net.Host;
+import org.onlab.onos.net.MastershipRole;
+import org.onlab.onos.net.Port;
+import org.onlab.onos.net.PortNumber;
+import org.onlab.onos.net.device.DeviceListener;
+import org.onlab.onos.net.device.DeviceService;
+import org.onlab.onos.net.flow.instructions.Instruction;
+import org.onlab.onos.net.flow.instructions.Instructions.OutputInstruction;
+import org.onlab.onos.net.host.HostProvider;
+import org.onlab.onos.net.host.PortAddresses;
+import org.onlab.onos.net.packet.OutboundPacket;
+import org.onlab.onos.net.packet.PacketProcessor;
+import org.onlab.onos.net.packet.PacketService;
+import org.onlab.onos.net.provider.ProviderId;
+import org.onlab.packet.ARP;
+import org.onlab.packet.Ethernet;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.IpPrefix;
+import org.onlab.packet.MacAddress;
+
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Multimap;
+
+public class HostMonitorTest {
+
+ private IpAddress targetIpAddress = IpAddress.valueOf("10.0.0.1");
+ private IpPrefix targetIpPrefix = IpPrefix.valueOf(targetIpAddress.toOctets());
+
+ private IpPrefix sourcePrefix = IpPrefix.valueOf("10.0.0.99/24");
+ private MacAddress sourceMac = MacAddress.valueOf(1L);
+
+ private HostMonitor hostMonitor;
+
+ @Test
+ @Ignore
+ public void testMonitorHostExists() throws Exception {
+ ProviderId id = new ProviderId("fake://", "id");
+
+ Host host = createMock(Host.class);
+ expect(host.providerId()).andReturn(id);
+ replay(host);
+
+ HostManager hostManager = createMock(HostManager.class);
+ expect(hostManager.getHostsByIp(targetIpPrefix))
+ .andReturn(Collections.singleton(host));
+ replay(hostManager);
+
+ HostProvider hostProvider = createMock(HostProvider.class);
+ expect(hostProvider.id()).andReturn(id).anyTimes();
+ hostProvider.triggerProbe(host);
+ expectLastCall().once();
+ replay(hostProvider);
+
+ hostMonitor = new HostMonitor(null, null, hostManager);
+
+ hostMonitor.registerHostProvider(hostProvider);
+ hostMonitor.addMonitoringFor(targetIpAddress);
+
+ hostMonitor.run(null);
+
+ verify(hostProvider);
+ }
+
+ @Test
+ @Ignore
+ public void testMonitorHostDoesNotExist() throws Exception {
+ HostManager hostManager = createMock(HostManager.class);
+
+ DeviceId devId = DeviceId.deviceId("fake");
+
+ Device device = createMock(Device.class);
+ expect(device.id()).andReturn(devId).anyTimes();
+ replay(device);
+
+ PortNumber portNum = PortNumber.portNumber(1L);
+
+ Port port = createMock(Port.class);
+ expect(port.number()).andReturn(portNum).anyTimes();
+ replay(port);
+
+ TestDeviceService deviceService = new TestDeviceService();
+ deviceService.addDevice(device, Collections.singleton(port));
+
+ ConnectPoint cp = new ConnectPoint(devId, portNum);
+ PortAddresses pa = new PortAddresses(cp, Collections.singleton(sourcePrefix),
+ sourceMac);
+
+ expect(hostManager.getHostsByIp(targetIpPrefix))
+ .andReturn(Collections.<Host>emptySet()).anyTimes();
+ expect(hostManager.getAddressBindingsForPort(cp))
+ .andReturn(pa).anyTimes();
+ replay(hostManager);
+
+ TestPacketService packetService = new TestPacketService();
+
+
+ // Run the test
+ hostMonitor = new HostMonitor(deviceService, packetService, hostManager);
+
+ hostMonitor.addMonitoringFor(targetIpAddress);
+ hostMonitor.run(null);
+
+
+ // Check that a packet was sent to our PacketService and that it has
+ // the properties we expect
+ assertTrue(packetService.packets.size() == 1);
+ OutboundPacket packet = packetService.packets.get(0);
+
+ // Check the output port is correct
+ assertTrue(packet.treatment().instructions().size() == 1);
+ Instruction instruction = packet.treatment().instructions().get(0);
+ assertTrue(instruction instanceof OutputInstruction);
+ OutputInstruction oi = (OutputInstruction) instruction;
+ assertTrue(oi.port().equals(portNum));
+
+ // Check the output packet is correct (well the important bits anyway)
+ Ethernet eth = new Ethernet();
+ eth.deserialize(packet.data().array(), 0, packet.data().array().length);
+ ARP arp = (ARP) eth.getPayload();
+ assertTrue(Arrays.equals(arp.getSenderProtocolAddress(), sourcePrefix.toOctets()));
+ assertTrue(Arrays.equals(arp.getSenderHardwareAddress(), sourceMac.toBytes()));
+ assertTrue(Arrays.equals(arp.getTargetProtocolAddress(), targetIpPrefix.toOctets()));
+ }
+
+ class TestPacketService implements PacketService {
+
+ List<OutboundPacket> packets = new ArrayList<>();
+
+ @Override
+ public void addProcessor(PacketProcessor processor, int priority) {
+ }
+
+ @Override
+ public void removeProcessor(PacketProcessor processor) {
+ }
+
+ @Override
+ public void emit(OutboundPacket packet) {
+ packets.add(packet);
+ }
+ }
+
+ class TestDeviceService implements DeviceService {
+
+ List<Device> devices = Lists.newArrayList();
+ Multimap<DeviceId, Port> devicePorts = HashMultimap.create();
+
+ void addDevice(Device device, Set<Port> ports) {
+ devices.add(device);
+ for (Port p : ports) {
+ devicePorts.put(device.id(), p);
+ }
+ }
+
+ @Override
+ public int getDeviceCount() {
+ return 0;
+ }
+
+ @Override
+ public Iterable<Device> getDevices() {
+ return devices;
+ }
+
+ @Override
+ public Device getDevice(DeviceId deviceId) {
+ return null;
+ }
+
+ @Override
+ public MastershipRole getRole(DeviceId deviceId) {
+ return null;
+ }
+
+ @Override
+ public List<Port> getPorts(DeviceId deviceId) {
+ List<Port> ports = Lists.newArrayList();
+ for (Port p : devicePorts.get(deviceId)) {
+ ports.add(p);
+ }
+ return ports;
+ }
+
+ @Override
+ public Port getPort(DeviceId deviceId, PortNumber portNumber) {
+ return null;
+ }
+
+ @Override
+ public boolean isAvailable(DeviceId deviceId) {
+ return false;
+ }
+
+ @Override
+ public void addListener(DeviceListener listener) {
+ }
+
+ @Override
+ public void removeListener(DeviceListener listener) {
+ }
+ }
+}
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/cluster/impl/ClusterManagementMessageSubjects.java b/core/store/dist/src/main/java/org/onlab/onos/store/cluster/impl/ClusterManagementMessageSubjects.java
new file mode 100644
index 0000000..74c22f1
--- /dev/null
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/cluster/impl/ClusterManagementMessageSubjects.java
@@ -0,0 +1,10 @@
+package org.onlab.onos.store.cluster.impl;
+
+import org.onlab.onos.store.cluster.messaging.MessageSubject;
+
+public final class ClusterManagementMessageSubjects {
+ // avoid instantiation
+ private ClusterManagementMessageSubjects() {}
+
+ public static final MessageSubject CLUSTER_MEMBERSHIP_EVENT = new MessageSubject("CLUSTER_MEMBERSHIP_EVENT");
+}
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/cluster/messaging/impl/ClusterMembershipEvent.java b/core/store/dist/src/main/java/org/onlab/onos/store/cluster/impl/ClusterMembershipEvent.java
similarity index 91%
rename from core/store/dist/src/main/java/org/onlab/onos/store/cluster/messaging/impl/ClusterMembershipEvent.java
rename to core/store/dist/src/main/java/org/onlab/onos/store/cluster/impl/ClusterMembershipEvent.java
index 961ed4f..30b847f 100644
--- a/core/store/dist/src/main/java/org/onlab/onos/store/cluster/messaging/impl/ClusterMembershipEvent.java
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/cluster/impl/ClusterMembershipEvent.java
@@ -1,4 +1,4 @@
-package org.onlab.onos.store.cluster.messaging.impl;
+package org.onlab.onos.store.cluster.impl;
import org.onlab.onos.cluster.ControllerNode;
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/cluster/messaging/impl/ClusterMembershipEventType.java b/core/store/dist/src/main/java/org/onlab/onos/store/cluster/impl/ClusterMembershipEventType.java
similarity index 69%
rename from core/store/dist/src/main/java/org/onlab/onos/store/cluster/messaging/impl/ClusterMembershipEventType.java
rename to core/store/dist/src/main/java/org/onlab/onos/store/cluster/impl/ClusterMembershipEventType.java
index 1f5fd3f..cdfd145 100644
--- a/core/store/dist/src/main/java/org/onlab/onos/store/cluster/messaging/impl/ClusterMembershipEventType.java
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/cluster/impl/ClusterMembershipEventType.java
@@ -1,4 +1,4 @@
-package org.onlab.onos.store.cluster.messaging.impl;
+package org.onlab.onos.store.cluster.impl;
public enum ClusterMembershipEventType {
NEW_MEMBER,
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/cluster/impl/DistributedClusterStore.java b/core/store/dist/src/main/java/org/onlab/onos/store/cluster/impl/DistributedClusterStore.java
index 9408cc9..5e64a39 100644
--- a/core/store/dist/src/main/java/org/onlab/onos/store/cluster/impl/DistributedClusterStore.java
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/cluster/impl/DistributedClusterStore.java
@@ -7,11 +7,9 @@
import com.google.common.collect.ImmutableSet;
import org.apache.felix.scr.annotations.Activate;
-import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Deactivate;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.ReferenceCardinality;
-import org.apache.felix.scr.annotations.Service;
import org.onlab.onos.cluster.ClusterEvent;
import org.onlab.onos.cluster.ClusterStore;
import org.onlab.onos.cluster.ClusterStoreDelegate;
@@ -37,8 +35,8 @@
/**
* Distributed implementation of the cluster nodes store.
*/
-@Component(immediate = true)
-@Service
+//@Component(immediate = true)
+//@Service
public class DistributedClusterStore
extends AbstractStore<ClusterEvent, ClusterStoreDelegate>
implements ClusterStore {
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/cluster/impl/MessageSerializer.java b/core/store/dist/src/main/java/org/onlab/onos/store/cluster/impl/MessageSerializer.java
deleted file mode 100644
index 98e80f7..0000000
--- a/core/store/dist/src/main/java/org/onlab/onos/store/cluster/impl/MessageSerializer.java
+++ /dev/null
@@ -1,117 +0,0 @@
-package org.onlab.onos.store.cluster.impl;
-
-import de.javakaffee.kryoserializers.URISerializer;
-import org.apache.felix.scr.annotations.Activate;
-import org.apache.felix.scr.annotations.Component;
-import org.apache.felix.scr.annotations.Deactivate;
-import org.apache.felix.scr.annotations.Service;
-import org.onlab.onos.cluster.ControllerNode;
-import org.onlab.onos.cluster.DefaultControllerNode;
-import org.onlab.onos.cluster.NodeId;
-import org.onlab.onos.net.ConnectPoint;
-import org.onlab.onos.net.DefaultDevice;
-import org.onlab.onos.net.DefaultLink;
-import org.onlab.onos.net.DefaultPort;
-import org.onlab.onos.net.Device;
-import org.onlab.onos.net.DeviceId;
-import org.onlab.onos.net.Element;
-import org.onlab.onos.net.Link;
-import org.onlab.onos.net.LinkKey;
-import org.onlab.onos.net.MastershipRole;
-import org.onlab.onos.net.Port;
-import org.onlab.onos.net.PortNumber;
-import org.onlab.onos.net.provider.ProviderId;
-import org.onlab.onos.store.cluster.messaging.MessageSubject;
-import org.onlab.onos.store.cluster.messaging.SerializationService;
-import org.onlab.onos.store.serializers.ConnectPointSerializer;
-import org.onlab.onos.store.serializers.DefaultLinkSerializer;
-import org.onlab.onos.store.serializers.DefaultPortSerializer;
-import org.onlab.onos.store.serializers.DeviceIdSerializer;
-import org.onlab.onos.store.serializers.IpPrefixSerializer;
-import org.onlab.onos.store.serializers.LinkKeySerializer;
-import org.onlab.onos.store.serializers.NodeIdSerializer;
-import org.onlab.onos.store.serializers.PortNumberSerializer;
-import org.onlab.onos.store.serializers.ProviderIdSerializer;
-import org.onlab.packet.IpPrefix;
-import org.onlab.util.KryoPool;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.net.URI;
-import java.util.ArrayList;
-import java.util.HashMap;
-
-/**
- * Factory for parsing messages sent between cluster members.
- */
-@Component(immediate = true)
-@Service
-public class MessageSerializer implements SerializationService {
-
- private final Logger log = LoggerFactory.getLogger(getClass());
-
- private static final int METADATA_LENGTH = 12; // 8 + 4
- private static final int LENGTH_OFFSET = 8;
-
- private static final long MARKER = 0xfeedcafebeaddeadL;
-
- private KryoPool serializerPool;
-
- @Activate
- public void activate() {
- setupKryoPool();
- log.info("Started");
- }
-
- @Deactivate
- public void deactivate() {
- log.info("Stopped");
- }
-
- /**
- * Sets up the common serialzers pool.
- */
- protected void setupKryoPool() {
- // FIXME Slice out types used in common to separate pool/namespace.
- serializerPool = KryoPool.newBuilder()
- .register(ArrayList.class,
- HashMap.class,
-
- ControllerNode.State.class,
- Device.Type.class,
-
- DefaultControllerNode.class,
- DefaultDevice.class,
- MastershipRole.class,
- Port.class,
- Element.class,
-
- Link.Type.class,
-
- MessageSubject.class
- )
- .register(IpPrefix.class, new IpPrefixSerializer())
- .register(URI.class, new URISerializer())
- .register(NodeId.class, new NodeIdSerializer())
- .register(ProviderId.class, new ProviderIdSerializer())
- .register(DeviceId.class, new DeviceIdSerializer())
- .register(PortNumber.class, new PortNumberSerializer())
- .register(DefaultPort.class, new DefaultPortSerializer())
- .register(LinkKey.class, new LinkKeySerializer())
- .register(ConnectPoint.class, new ConnectPointSerializer())
- .register(DefaultLink.class, new DefaultLinkSerializer())
- .build()
- .populate(1);
- }
-
-
- @Override
- public Object decode(byte[] data) {
- return serializerPool.deserialize(data);
- }
-
- @Override
- public byte[] encode(Object payload) {
- return serializerPool.serialize(payload);
- }
-}
\ No newline at end of file
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/cluster/messaging/ClusterCommunicationAdminService.java b/core/store/dist/src/main/java/org/onlab/onos/store/cluster/messaging/ClusterCommunicationAdminService.java
index 5966f12..0bc31fa 100644
--- a/core/store/dist/src/main/java/org/onlab/onos/store/cluster/messaging/ClusterCommunicationAdminService.java
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/cluster/messaging/ClusterCommunicationAdminService.java
@@ -3,6 +3,8 @@
import org.onlab.onos.cluster.ControllerNode;
import org.onlab.onos.store.cluster.impl.ClusterNodesDelegate;
+// TODO: This service interface can be removed, once we properly start
+// using ClusterService
/**
* Service for administering communications manager.
*/
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/cluster/messaging/ClusterMessage.java b/core/store/dist/src/main/java/org/onlab/onos/store/cluster/messaging/ClusterMessage.java
index ee558dd..b74f887 100644
--- a/core/store/dist/src/main/java/org/onlab/onos/store/cluster/messaging/ClusterMessage.java
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/cluster/messaging/ClusterMessage.java
@@ -2,6 +2,7 @@
import org.onlab.onos.cluster.NodeId;
+// TODO: Should payload type be ByteBuffer?
/**
* Base message for cluster-wide communications.
*/
@@ -9,14 +10,14 @@
private final NodeId sender;
private final MessageSubject subject;
- private final Object payload;
+ private final byte[] payload;
/**
* Creates a cluster message.
*
* @param subject message subject
*/
- public ClusterMessage(NodeId sender, MessageSubject subject, Object payload) {
+ public ClusterMessage(NodeId sender, MessageSubject subject, byte[] payload) {
this.sender = sender;
this.subject = subject;
this.payload = payload;
@@ -45,7 +46,7 @@
*
* @return message payload.
*/
- public Object payload() {
+ public byte[] payload() {
return payload;
}
}
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/cluster/messaging/ClusterMessageHandler.java b/core/store/dist/src/main/java/org/onlab/onos/store/cluster/messaging/ClusterMessageHandler.java
index 7ec27ec..4dd7bc2 100644
--- a/core/store/dist/src/main/java/org/onlab/onos/store/cluster/messaging/ClusterMessageHandler.java
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/cluster/messaging/ClusterMessageHandler.java
@@ -10,4 +10,4 @@
* @param message cluster message.
*/
public void handle(ClusterMessage message);
-}
\ No newline at end of file
+}
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/cluster/messaging/MessageSubject.java b/core/store/dist/src/main/java/org/onlab/onos/store/cluster/messaging/MessageSubject.java
index ee8d9c1..43df15f 100644
--- a/core/store/dist/src/main/java/org/onlab/onos/store/cluster/messaging/MessageSubject.java
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/cluster/messaging/MessageSubject.java
@@ -1,5 +1,9 @@
package org.onlab.onos.store.cluster.messaging;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.util.Objects;
+
/**
* Representation of a message subject.
* Cluster messages have associated subjects that dictate how they get handled
@@ -10,7 +14,7 @@
private final String value;
public MessageSubject(String value) {
- this.value = value;
+ this.value = checkNotNull(value);
}
public String value() {
@@ -21,4 +25,29 @@
public String toString() {
return value;
}
+
+ @Override
+ public int hashCode() {
+ return value.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ MessageSubject that = (MessageSubject) obj;
+ return Objects.equals(this.value, that.value);
+ }
+
+ // for serializer
+ protected MessageSubject() {
+ this.value = "";
+ }
}
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/cluster/messaging/SerializationService.java b/core/store/dist/src/main/java/org/onlab/onos/store/cluster/messaging/SerializationService.java
index d85f488..4d76ce3 100644
--- a/core/store/dist/src/main/java/org/onlab/onos/store/cluster/messaging/SerializationService.java
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/cluster/messaging/SerializationService.java
@@ -1,7 +1,7 @@
package org.onlab.onos.store.cluster.messaging;
/**
- * Service for encoding & decoding intra-cluster messages.
+ * Service for encoding & decoding intra-cluster message payload.
*/
public interface SerializationService {
@@ -11,7 +11,7 @@
* @param buffer byte buffer with message(s)
* @return parsed message
*/
- Object decode(byte[] data);
+ <T> T decode(byte[] data);
/**
* Encodes the specified message into the given byte buffer.
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/cluster/messaging/impl/ClusterCommunicationManager.java b/core/store/dist/src/main/java/org/onlab/onos/store/cluster/messaging/impl/ClusterCommunicationManager.java
index d4fd9c0..1b11873 100644
--- a/core/store/dist/src/main/java/org/onlab/onos/store/cluster/messaging/impl/ClusterCommunicationManager.java
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/cluster/messaging/impl/ClusterCommunicationManager.java
@@ -3,30 +3,36 @@
import static com.google.common.base.Preconditions.checkArgument;
import java.io.IOException;
-import java.util.HashMap;
-import java.util.Map;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;
-
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Deactivate;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.apache.felix.scr.annotations.Service;
+import org.onlab.onos.cluster.ClusterService;
import org.onlab.onos.cluster.ControllerNode;
import org.onlab.onos.cluster.NodeId;
+import org.onlab.onos.store.cluster.impl.ClusterMembershipEvent;
+import org.onlab.onos.store.cluster.impl.ClusterMembershipEventType;
import org.onlab.onos.store.cluster.impl.ClusterNodesDelegate;
import org.onlab.onos.store.cluster.messaging.ClusterCommunicationAdminService;
import org.onlab.onos.store.cluster.messaging.ClusterCommunicationService;
import org.onlab.onos.store.cluster.messaging.ClusterMessage;
import org.onlab.onos.store.cluster.messaging.ClusterMessageHandler;
import org.onlab.onos.store.cluster.messaging.MessageSubject;
+import org.onlab.onos.store.serializers.ClusterMessageSerializer;
+import org.onlab.onos.store.serializers.KryoPoolUtil;
+import org.onlab.onos.store.serializers.KryoSerializer;
+import org.onlab.onos.store.serializers.MessageSubjectSerializer;
+import org.onlab.util.KryoPool;
import org.onlab.netty.Endpoint;
import org.onlab.netty.Message;
import org.onlab.netty.MessageHandler;
import org.onlab.netty.MessagingService;
+import org.onlab.netty.NettyMessagingService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -38,28 +44,57 @@
private final Logger log = LoggerFactory.getLogger(getClass());
private ControllerNode localNode;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ private ClusterService clusterService;
+
private ClusterNodesDelegate nodesDelegate;
- private Map<NodeId, ControllerNode> members = new HashMap<>();
private final Timer timer = new Timer("onos-controller-heatbeats");
public static final long HEART_BEAT_INTERVAL_MILLIS = 1000L;
- @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ // TODO: This probably should not be a OSGi service.
private MessagingService messagingService;
+ private static final KryoSerializer SERIALIZER = new KryoSerializer() {
+ @Override
+ protected void setupKryoPool() {
+ serializerPool = KryoPool.newBuilder()
+ .register(KryoPoolUtil.API)
+ .register(ClusterMessage.class, new ClusterMessageSerializer())
+ .register(ClusterMembershipEvent.class)
+ .register(byte[].class)
+ .register(MessageSubject.class, new MessageSubjectSerializer())
+ .build()
+ .populate(1);
+ }
+
+ };
+
@Activate
public void activate() {
+ localNode = clusterService.getLocalNode();
+ NettyMessagingService netty = new NettyMessagingService(localNode.tcpPort());
+ // FIXME: workaround until it becomes a service.
+ try {
+ netty.activate();
+ } catch (Exception e) {
+ // TODO Auto-generated catch block
+ log.error("NettyMessagingService#activate", e);
+ }
+ messagingService = netty;
log.info("Started");
}
@Deactivate
public void deactivate() {
+ // TODO: cleanup messageingService if needed.
log.info("Stopped");
}
@Override
public boolean broadcast(ClusterMessage message) {
boolean ok = true;
- for (ControllerNode node : members.values()) {
+ for (ControllerNode node : clusterService.getNodes()) {
if (!node.equals(localNode)) {
ok = unicast(message, node.id()) && ok;
}
@@ -80,11 +115,12 @@
@Override
public boolean unicast(ClusterMessage message, NodeId toNodeId) {
- ControllerNode node = members.get(toNodeId);
+ ControllerNode node = clusterService.getNode(toNodeId);
checkArgument(node != null, "Unknown nodeId: %s", toNodeId);
Endpoint nodeEp = new Endpoint(node.ip().toString(), node.tcpPort());
try {
- messagingService.sendAsync(nodeEp, message.subject().value(), message);
+ messagingService.sendAsync(nodeEp,
+ message.subject().value(), SERIALIZER.encode(message));
return true;
} catch (IOException e) {
log.error("Failed to send cluster message to nodeId: " + toNodeId, e);
@@ -110,7 +146,7 @@
@Override
public void addNode(ControllerNode node) {
- members.put(node.id(), node);
+ //members.put(node.id(), node);
}
@Override
@@ -118,8 +154,8 @@
broadcast(new ClusterMessage(
localNode.id(),
new MessageSubject("CLUSTER_MEMBERSHIP_EVENT"),
- new ClusterMembershipEvent(ClusterMembershipEventType.LEAVING_MEMBER, node)));
- members.remove(node.id());
+ SERIALIZER.encode(new ClusterMembershipEvent(ClusterMembershipEventType.LEAVING_MEMBER, node))));
+ //members.remove(node.id());
}
// Sends a heart beat to all peers.
@@ -130,7 +166,7 @@
broadcast(new ClusterMessage(
localNode.id(),
new MessageSubject("CLUSTER_MEMBERSHIP_EVENT"),
- new ClusterMembershipEvent(ClusterMembershipEventType.HEART_BEAT, localNode)));
+ SERIALIZER.encode(new ClusterMembershipEvent(ClusterMembershipEventType.HEART_BEAT, localNode))));
}
}
@@ -139,7 +175,7 @@
@Override
public void handle(ClusterMessage message) {
- ClusterMembershipEvent event = (ClusterMembershipEvent) message.payload();
+ ClusterMembershipEvent event = SERIALIZER.decode(message.payload());
ControllerNode node = event.node();
if (event.type() == ClusterMembershipEventType.HEART_BEAT) {
log.info("Node {} sent a hearbeat", node.id());
@@ -154,7 +190,7 @@
}
}
- private static class InternalClusterMessageHandler implements MessageHandler {
+ private final class InternalClusterMessageHandler implements MessageHandler {
private final ClusterMessageHandler handler;
@@ -164,7 +200,13 @@
@Override
public void handle(Message message) {
- handler.handle((ClusterMessage) message.payload());
+ try {
+ ClusterMessage clusterMessage = SERIALIZER.decode(message.payload());
+ handler.handle(clusterMessage);
+ } catch (Exception e) {
+ log.error("Exception caught during ClusterMessageHandler", e);
+ throw e;
+ }
}
}
}
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/cluster/messaging/impl/ClusterMessageSubjects.java b/core/store/dist/src/main/java/org/onlab/onos/store/cluster/messaging/impl/ClusterMessageSubjects.java
deleted file mode 100644
index d1f75ae..0000000
--- a/core/store/dist/src/main/java/org/onlab/onos/store/cluster/messaging/impl/ClusterMessageSubjects.java
+++ /dev/null
@@ -1,8 +0,0 @@
-package org.onlab.onos.store.cluster.messaging.impl;
-
-import org.onlab.onos.store.cluster.messaging.MessageSubject;
-
-public final class ClusterMessageSubjects {
- private ClusterMessageSubjects() {}
- public static final MessageSubject CLUSTER_MEMBERSHIP_EVENT = new MessageSubject("CLUSTER_MEMBERSHIP_EVENT");
-}
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/cluster/messaging/impl/MessageSerializer.java b/core/store/dist/src/main/java/org/onlab/onos/store/cluster/messaging/impl/MessageSerializer.java
new file mode 100644
index 0000000..bf47f49
--- /dev/null
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/cluster/messaging/impl/MessageSerializer.java
@@ -0,0 +1,63 @@
+package org.onlab.onos.store.cluster.messaging.impl;
+
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Service;
+import org.onlab.onos.store.cluster.messaging.MessageSubject;
+import org.onlab.onos.store.cluster.messaging.SerializationService;
+import org.onlab.onos.store.serializers.KryoPoolUtil;
+import org.onlab.util.KryoPool;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Factory for parsing messages sent between cluster members.
+ */
+@Component(immediate = true)
+@Service
+public class MessageSerializer implements SerializationService {
+
+ private final Logger log = LoggerFactory.getLogger(getClass());
+
+ private static final int METADATA_LENGTH = 12; // 8 + 4
+ private static final int LENGTH_OFFSET = 8;
+
+ private static final long MARKER = 0xfeedcafebeaddeadL;
+
+ private KryoPool serializerPool;
+
+ @Activate
+ public void activate() {
+ setupKryoPool();
+ log.info("Started");
+ }
+
+ @Deactivate
+ public void deactivate() {
+ log.info("Stopped");
+ }
+
+ /**
+ * Sets up the common serialzers pool.
+ */
+ protected void setupKryoPool() {
+ serializerPool = KryoPool.newBuilder()
+ .register(KryoPoolUtil.API)
+ // TODO: Should MessageSubject be in API bundle?
+ .register(MessageSubject.class)
+ .build()
+ .populate(1);
+ }
+
+
+ @Override
+ public <T> T decode(byte[] data) {
+ return serializerPool.deserialize(data);
+ }
+
+ @Override
+ public byte[] encode(Object payload) {
+ return serializerPool.serialize(payload);
+ }
+}
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/cluster/messaging/impl/package-info.java b/core/store/dist/src/main/java/org/onlab/onos/store/cluster/messaging/impl/package-info.java
new file mode 100644
index 0000000..6c1e71b
--- /dev/null
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/cluster/messaging/impl/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * Implementation of the cluster messaging mechanism.
+ */
+package org.onlab.onos.store.cluster.messaging.impl;
\ No newline at end of file
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/cluster/messaging/AntiEntropyAdvertisement.java b/core/store/dist/src/main/java/org/onlab/onos/store/common/impl/AntiEntropyAdvertisement.java
similarity index 95%
rename from core/store/dist/src/main/java/org/onlab/onos/store/cluster/messaging/AntiEntropyAdvertisement.java
rename to core/store/dist/src/main/java/org/onlab/onos/store/common/impl/AntiEntropyAdvertisement.java
index b70da73..132f27a 100644
--- a/core/store/dist/src/main/java/org/onlab/onos/store/cluster/messaging/AntiEntropyAdvertisement.java
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/common/impl/AntiEntropyAdvertisement.java
@@ -1,4 +1,4 @@
-package org.onlab.onos.store.cluster.messaging;
+package org.onlab.onos.store.common.impl;
import java.util.Map;
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/cluster/messaging/AntiEntropyReply.java b/core/store/dist/src/main/java/org/onlab/onos/store/common/impl/AntiEntropyReply.java
similarity index 97%
rename from core/store/dist/src/main/java/org/onlab/onos/store/cluster/messaging/AntiEntropyReply.java
rename to core/store/dist/src/main/java/org/onlab/onos/store/common/impl/AntiEntropyReply.java
index 095752b..033a1de 100644
--- a/core/store/dist/src/main/java/org/onlab/onos/store/cluster/messaging/AntiEntropyReply.java
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/common/impl/AntiEntropyReply.java
@@ -1,4 +1,4 @@
-package org.onlab.onos.store.cluster.messaging;
+package org.onlab.onos.store.common.impl;
import java.util.Map;
import java.util.Set;
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/impl/OnosTimestamp.java b/core/store/dist/src/main/java/org/onlab/onos/store/common/impl/MastershipBasedTimestamp.java
similarity index 73%
rename from core/store/dist/src/main/java/org/onlab/onos/store/impl/OnosTimestamp.java
rename to core/store/dist/src/main/java/org/onlab/onos/store/common/impl/MastershipBasedTimestamp.java
index 2005582..0f4f894 100644
--- a/core/store/dist/src/main/java/org/onlab/onos/store/impl/OnosTimestamp.java
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/common/impl/MastershipBasedTimestamp.java
@@ -1,4 +1,4 @@
-package org.onlab.onos.store.impl;
+package org.onlab.onos.store.common.impl;
import static com.google.common.base.Preconditions.checkArgument;
@@ -9,12 +9,11 @@
import com.google.common.base.MoreObjects;
import com.google.common.collect.ComparisonChain;
-// If it is store specific, implement serializable interfaces?
/**
* Default implementation of Timestamp.
* TODO: Better documentation.
*/
-public final class OnosTimestamp implements Timestamp {
+public final class MastershipBasedTimestamp implements Timestamp {
private final int termNumber;
private final int sequenceNumber;
@@ -25,15 +24,16 @@
* @param termNumber the mastership termNumber
* @param sequenceNumber the sequenceNumber number within the termNumber
*/
- public OnosTimestamp(int termNumber, int sequenceNumber) {
+ public MastershipBasedTimestamp(int termNumber, int sequenceNumber) {
this.termNumber = termNumber;
this.sequenceNumber = sequenceNumber;
}
@Override
public int compareTo(Timestamp o) {
- checkArgument(o instanceof OnosTimestamp, "Must be OnosTimestamp", o);
- OnosTimestamp that = (OnosTimestamp) o;
+ checkArgument(o instanceof MastershipBasedTimestamp,
+ "Must be MastershipBasedTimestamp", o);
+ MastershipBasedTimestamp that = (MastershipBasedTimestamp) o;
return ComparisonChain.start()
.compare(this.termNumber, that.termNumber)
@@ -51,10 +51,10 @@
if (this == obj) {
return true;
}
- if (!(obj instanceof OnosTimestamp)) {
+ if (!(obj instanceof MastershipBasedTimestamp)) {
return false;
}
- OnosTimestamp that = (OnosTimestamp) obj;
+ MastershipBasedTimestamp that = (MastershipBasedTimestamp) obj;
return Objects.equals(this.termNumber, that.termNumber) &&
Objects.equals(this.sequenceNumber, that.sequenceNumber);
}
@@ -84,4 +84,11 @@
public int sequenceNumber() {
return sequenceNumber;
}
+
+ // Default constructor for serialization
+ @Deprecated
+ protected MastershipBasedTimestamp() {
+ this.termNumber = -1;
+ this.sequenceNumber = -1;
+ }
}
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/common/impl/Timestamped.java b/core/store/dist/src/main/java/org/onlab/onos/store/common/impl/Timestamped.java
new file mode 100644
index 0000000..77b0a87
--- /dev/null
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/common/impl/Timestamped.java
@@ -0,0 +1,89 @@
+package org.onlab.onos.store.common.impl;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.util.Objects;
+
+import org.onlab.onos.store.Timestamp;
+
+import com.google.common.base.MoreObjects;
+
+/**
+ * Wrapper class to store Timestamped value.
+ * @param <T>
+ */
+public final class Timestamped<T> {
+
+ private final Timestamp timestamp;
+ private final T value;
+
+ /**
+ * Creates a time stamped value.
+ *
+ * @param value to be timestamp
+ * @param timestamp the timestamp
+ */
+ public Timestamped(T value, Timestamp timestamp) {
+ this.value = checkNotNull(value);
+ this.timestamp = checkNotNull(timestamp);
+ }
+
+ /**
+ * Returns the value.
+ * @return value
+ */
+ public T value() {
+ return value;
+ }
+
+ /**
+ * Returns the time stamp.
+ * @return time stamp
+ */
+ public Timestamp timestamp() {
+ return timestamp;
+ }
+
+ /**
+ * Tests if this timestamped value is newer than the other.
+ *
+ * @param other timestamped value
+ * @return true if this instance is newer.
+ */
+ public boolean isNewer(Timestamped<T> other) {
+ return this.timestamp.compareTo(checkNotNull(other).timestamp()) > 0;
+ }
+
+ @Override
+ public int hashCode() {
+ return timestamp.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (!(obj instanceof Timestamped)) {
+ return false;
+ }
+ @SuppressWarnings("unchecked")
+ Timestamped<T> that = (Timestamped<T>) obj;
+ return Objects.equals(this.timestamp, that.timestamp);
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(getClass())
+ .add("timestamp", timestamp)
+ .add("value", value)
+ .toString();
+ }
+
+ // Default constructor for serialization
+ @Deprecated
+ private Timestamped() {
+ this.value = null;
+ this.timestamp = null;
+ }
+}
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/common/impl/package-info.java b/core/store/dist/src/main/java/org/onlab/onos/store/common/impl/package-info.java
new file mode 100644
index 0000000..992fd49
--- /dev/null
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/common/impl/package-info.java
@@ -0,0 +1,5 @@
+/**
+ * Common abstractions and facilities for implementing distributed store
+ * using gossip protocol.
+ */
+package org.onlab.onos.store.common.impl;
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/DeviceAntiEntropyAdvertisement.java b/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/DeviceAntiEntropyAdvertisement.java
index 301884c..d05659b 100644
--- a/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/DeviceAntiEntropyAdvertisement.java
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/DeviceAntiEntropyAdvertisement.java
@@ -8,7 +8,7 @@
import org.onlab.onos.net.Device;
import org.onlab.onos.net.DeviceId;
import org.onlab.onos.store.Timestamp;
-import org.onlab.onos.store.cluster.messaging.AntiEntropyAdvertisement;
+import org.onlab.onos.store.common.impl.AntiEntropyAdvertisement;
// TODO DeviceID needs to be changed to something like (ProviderID, DeviceID)
// TODO: Handle Port as part of these messages, or separate messages for Ports?
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/DeviceAntiEntropyReply.java b/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/DeviceAntiEntropyReply.java
index 011713e..e7a4d0a 100644
--- a/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/DeviceAntiEntropyReply.java
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/DeviceAntiEntropyReply.java
@@ -10,7 +10,7 @@
import org.onlab.onos.net.Device;
import org.onlab.onos.net.DeviceId;
import org.onlab.onos.store.Timestamp;
-import org.onlab.onos.store.cluster.messaging.AntiEntropyReply;
+import org.onlab.onos.store.common.impl.AntiEntropyReply;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/OnosClockService.java b/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/DeviceClockManager.java
similarity index 81%
rename from core/store/dist/src/main/java/org/onlab/onos/store/device/impl/OnosClockService.java
rename to core/store/dist/src/main/java/org/onlab/onos/store/device/impl/DeviceClockManager.java
index a99482f..e1e3692 100644
--- a/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/OnosClockService.java
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/DeviceClockManager.java
@@ -12,14 +12,18 @@
import org.apache.felix.scr.annotations.Service;
import org.onlab.onos.cluster.MastershipTerm;
import org.onlab.onos.net.DeviceId;
+import org.onlab.onos.store.ClockProviderService;
import org.onlab.onos.store.ClockService;
import org.onlab.onos.store.Timestamp;
-import org.onlab.onos.store.impl.OnosTimestamp;
+import org.onlab.onos.store.common.impl.MastershipBasedTimestamp;
import org.slf4j.Logger;
+/**
+ * Clock service to issue Timestamp based on Device Mastership.
+ */
@Component(immediate = true)
@Service
-public class OnosClockService implements ClockService {
+public class DeviceClockManager implements ClockService, ClockProviderService {
private final Logger log = getLogger(getClass());
@@ -43,7 +47,7 @@
if (term == null) {
throw new IllegalStateException("Requesting timestamp for a deviceId without mastership");
}
- return new OnosTimestamp(term.termNumber(), ticker.incrementAndGet());
+ return new MastershipBasedTimestamp(term.termNumber(), ticker.incrementAndGet());
}
@Override
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/GossipDeviceStore.java b/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/GossipDeviceStore.java
new file mode 100644
index 0000000..f39413b
--- /dev/null
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/GossipDeviceStore.java
@@ -0,0 +1,945 @@
+package org.onlab.onos.store.device.impl;
+
+import com.google.common.collect.FluentIterable;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+
+import org.apache.commons.lang3.concurrent.ConcurrentException;
+import org.apache.commons.lang3.concurrent.ConcurrentInitializer;
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.apache.felix.scr.annotations.Service;
+import org.onlab.onos.cluster.ClusterService;
+import org.onlab.onos.net.AnnotationsUtil;
+import org.onlab.onos.net.DefaultAnnotations;
+import org.onlab.onos.net.DefaultDevice;
+import org.onlab.onos.net.DefaultPort;
+import org.onlab.onos.net.Device;
+import org.onlab.onos.net.Device.Type;
+import org.onlab.onos.net.DeviceId;
+import org.onlab.onos.net.Port;
+import org.onlab.onos.net.PortNumber;
+import org.onlab.onos.net.SparseAnnotations;
+import org.onlab.onos.net.device.DefaultDeviceDescription;
+import org.onlab.onos.net.device.DefaultPortDescription;
+import org.onlab.onos.net.device.DeviceDescription;
+import org.onlab.onos.net.device.DeviceEvent;
+import org.onlab.onos.net.device.DeviceStore;
+import org.onlab.onos.net.device.DeviceStoreDelegate;
+import org.onlab.onos.net.device.PortDescription;
+import org.onlab.onos.net.provider.ProviderId;
+import org.onlab.onos.store.AbstractStore;
+import org.onlab.onos.store.ClockService;
+import org.onlab.onos.store.Timestamp;
+import org.onlab.onos.store.cluster.messaging.ClusterCommunicationService;
+import org.onlab.onos.store.cluster.messaging.ClusterMessage;
+import org.onlab.onos.store.cluster.messaging.ClusterMessageHandler;
+import org.onlab.onos.store.common.impl.MastershipBasedTimestamp;
+import org.onlab.onos.store.common.impl.Timestamped;
+import org.onlab.onos.store.serializers.KryoPoolUtil;
+import org.onlab.onos.store.serializers.KryoSerializer;
+import org.onlab.onos.store.serializers.MastershipBasedTimestampSerializer;
+import org.onlab.util.KryoPool;
+import org.onlab.util.NewConcurrentHashMap;
+import org.slf4j.Logger;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.atomic.AtomicReference;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Predicates.notNull;
+import static org.onlab.onos.net.device.DeviceEvent.Type.*;
+import static org.slf4j.LoggerFactory.getLogger;
+import static org.apache.commons.lang3.concurrent.ConcurrentUtils.createIfAbsentUnchecked;
+import static org.onlab.onos.net.DefaultAnnotations.merge;
+import static org.onlab.onos.net.DefaultAnnotations.union;
+import static com.google.common.base.Verify.verify;
+
+// TODO: give me a better name
+/**
+ * Manages inventory of infrastructure devices using gossip protocol to distribute
+ * information.
+ */
+@Component(immediate = true)
+@Service
+public class GossipDeviceStore
+ extends AbstractStore<DeviceEvent, DeviceStoreDelegate>
+ implements DeviceStore {
+
+ private final Logger log = getLogger(getClass());
+
+ public static final String DEVICE_NOT_FOUND = "Device with ID %s not found";
+
+ // TODO: Check if inner Map can be replaced with plain Map
+ // innerMap is used to lock a Device, thus instance should never be replaced.
+ // collection of Description given from various providers
+ private final ConcurrentMap<DeviceId,
+ ConcurrentMap<ProviderId, DeviceDescriptions>>
+ deviceDescs = Maps.newConcurrentMap();
+
+ // cache of Device and Ports generated by compositing descriptions from providers
+ private final ConcurrentMap<DeviceId, Device> devices = Maps.newConcurrentMap();
+ private final ConcurrentMap<DeviceId, ConcurrentMap<PortNumber, Port>> devicePorts = Maps.newConcurrentMap();
+
+ // to be updated under Device lock
+ private final Map<DeviceId, Timestamp> offline = Maps.newHashMap();
+ private final Map<DeviceId, Timestamp> removalRequest = Maps.newHashMap();
+
+ // available(=UP) devices
+ private final Set<DeviceId> availableDevices = Sets.newConcurrentHashSet();
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected ClockService clockService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected ClusterCommunicationService clusterCommunicator;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected ClusterService clusterService;
+
+ private static final KryoSerializer SERIALIZER = new KryoSerializer() {
+ @Override
+ protected void setupKryoPool() {
+ serializerPool = KryoPool.newBuilder()
+ .register(KryoPoolUtil.API)
+ .register(InternalDeviceEvent.class, new InternalDeviceEventSerializer())
+ .register(InternalDeviceOfflineEvent.class, new InternalDeviceOfflineEventSerializer())
+ .register(InternalDeviceRemovedEvent.class)
+ .register(InternalPortEvent.class, new InternalPortEventSerializer())
+ .register(InternalPortStatusEvent.class, new InternalPortStatusEventSerializer())
+ .register(Timestamp.class)
+ .register(Timestamped.class)
+ .register(MastershipBasedTimestamp.class, new MastershipBasedTimestampSerializer())
+ .build()
+ .populate(1);
+ }
+
+ };
+
+ @Activate
+ public void activate() {
+ clusterCommunicator.addSubscriber(
+ GossipDeviceStoreMessageSubjects.DEVICE_UPDATE, new InternalDeviceEventListener());
+ clusterCommunicator.addSubscriber(
+ GossipDeviceStoreMessageSubjects.DEVICE_OFFLINE, new InternalDeviceOfflineEventListener());
+ clusterCommunicator.addSubscriber(
+ GossipDeviceStoreMessageSubjects.DEVICE_REMOVED, new InternalDeviceRemovedEventListener());
+ clusterCommunicator.addSubscriber(
+ GossipDeviceStoreMessageSubjects.PORT_UPDATE, new InternalPortEventListener());
+ clusterCommunicator.addSubscriber(
+ GossipDeviceStoreMessageSubjects.PORT_STATUS_UPDATE, new InternalPortStatusEventListener());
+ log.info("Started");
+ }
+
+ @Deactivate
+ public void deactivate() {
+ deviceDescs.clear();
+ devices.clear();
+ devicePorts.clear();
+ availableDevices.clear();
+ log.info("Stopped");
+ }
+
+ @Override
+ public int getDeviceCount() {
+ return devices.size();
+ }
+
+ @Override
+ public Iterable<Device> getDevices() {
+ return Collections.unmodifiableCollection(devices.values());
+ }
+
+ @Override
+ public Device getDevice(DeviceId deviceId) {
+ return devices.get(deviceId);
+ }
+
+ @Override
+ public synchronized DeviceEvent createOrUpdateDevice(ProviderId providerId,
+ DeviceId deviceId,
+ DeviceDescription deviceDescription) {
+ Timestamp newTimestamp = clockService.getTimestamp(deviceId);
+ final Timestamped<DeviceDescription> deltaDesc = new Timestamped<>(deviceDescription, newTimestamp);
+ DeviceEvent event = createOrUpdateDeviceInternal(providerId, deviceId, deltaDesc);
+ if (event != null) {
+ log.info("Notifying peers of a device update topology event for providerId: {} and deviceId: {}",
+ providerId, deviceId);
+ try {
+ notifyPeers(new InternalDeviceEvent(providerId, deviceId, deltaDesc));
+ } catch (IOException e) {
+ log.error("Failed to notify peers of a device update topology event for providerId: "
+ + providerId + " and deviceId: " + deviceId, e);
+ }
+ }
+ return event;
+ }
+
+ private DeviceEvent createOrUpdateDeviceInternal(ProviderId providerId,
+ DeviceId deviceId,
+ Timestamped<DeviceDescription> deltaDesc) {
+
+ // Collection of DeviceDescriptions for a Device
+ ConcurrentMap<ProviderId, DeviceDescriptions> providerDescs
+ = getDeviceDescriptions(deviceId);
+
+ synchronized (providerDescs) {
+ // locking per device
+
+ if (isDeviceRemoved(deviceId, deltaDesc.timestamp())) {
+ log.debug("Ignoring outdated event: {}", deltaDesc);
+ return null;
+ }
+
+ DeviceDescriptions descs
+ = createIfAbsentUnchecked(providerDescs, providerId,
+ new InitDeviceDescs(deltaDesc));
+
+ final Device oldDevice = devices.get(deviceId);
+ final Device newDevice;
+
+ if (deltaDesc == descs.getDeviceDesc() ||
+ deltaDesc.isNewer(descs.getDeviceDesc())) {
+ // on new device or valid update
+ descs.putDeviceDesc(deltaDesc);
+ newDevice = composeDevice(deviceId, providerDescs);
+ } else {
+ // outdated event, ignored.
+ return null;
+ }
+ if (oldDevice == null) {
+ // ADD
+ return createDevice(providerId, newDevice, deltaDesc.timestamp());
+ } else {
+ // UPDATE or ignore (no change or stale)
+ return updateDevice(providerId, oldDevice, newDevice, deltaDesc.timestamp());
+ }
+ }
+ }
+
+ // Creates the device and returns the appropriate event if necessary.
+ // Guarded by deviceDescs value (=Device lock)
+ private DeviceEvent createDevice(ProviderId providerId,
+ Device newDevice, Timestamp timestamp) {
+
+ // update composed device cache
+ Device oldDevice = devices.putIfAbsent(newDevice.id(), newDevice);
+ verify(oldDevice == null,
+ "Unexpected Device in cache. PID:%s [old=%s, new=%s]",
+ providerId, oldDevice, newDevice);
+
+ if (!providerId.isAncillary()) {
+ markOnline(newDevice.id(), timestamp);
+ }
+
+ return new DeviceEvent(DeviceEvent.Type.DEVICE_ADDED, newDevice, null);
+ }
+
+ // Updates the device and returns the appropriate event if necessary.
+ // Guarded by deviceDescs value (=Device lock)
+ private DeviceEvent updateDevice(ProviderId providerId,
+ Device oldDevice,
+ Device newDevice, Timestamp newTimestamp) {
+
+ // We allow only certain attributes to trigger update
+ if (!Objects.equals(oldDevice.hwVersion(), newDevice.hwVersion()) ||
+ !Objects.equals(oldDevice.swVersion(), newDevice.swVersion()) ||
+ !AnnotationsUtil.isEqual(oldDevice.annotations(), newDevice.annotations())) {
+
+ boolean replaced = devices.replace(newDevice.id(), oldDevice, newDevice);
+ if (!replaced) {
+ verify(replaced,
+ "Replacing devices cache failed. PID:%s [expected:%s, found:%s, new=%s]",
+ providerId, oldDevice, devices.get(newDevice.id())
+ , newDevice);
+ }
+ if (!providerId.isAncillary()) {
+ markOnline(newDevice.id(), newTimestamp);
+ }
+ return new DeviceEvent(DeviceEvent.Type.DEVICE_UPDATED, newDevice, null);
+ }
+
+ // Otherwise merely attempt to change availability if primary provider
+ if (!providerId.isAncillary()) {
+ boolean added = markOnline(newDevice.id(), newTimestamp);
+ return !added ? null :
+ new DeviceEvent(DEVICE_AVAILABILITY_CHANGED, newDevice, null);
+ }
+ return null;
+ }
+
+ @Override
+ public DeviceEvent markOffline(DeviceId deviceId) {
+ Timestamp timestamp = clockService.getTimestamp(deviceId);
+ DeviceEvent event = markOfflineInternal(deviceId, timestamp);
+ if (event != null) {
+ log.info("Notifying peers of a device offline topology event for deviceId: {}",
+ deviceId);
+ try {
+ notifyPeers(new InternalDeviceOfflineEvent(deviceId, timestamp));
+ } catch (IOException e) {
+ log.error("Failed to notify peers of a device offline topology event for deviceId: {}",
+ deviceId);
+ }
+ }
+ return event;
+ }
+
+ private DeviceEvent markOfflineInternal(DeviceId deviceId, Timestamp timestamp) {
+
+ Map<ProviderId, DeviceDescriptions> providerDescs
+ = getDeviceDescriptions(deviceId);
+
+ // locking device
+ synchronized (providerDescs) {
+
+ // accept off-line if given timestamp is newer than
+ // the latest Timestamp from Primary provider
+ DeviceDescriptions primDescs = getPrimaryDescriptions(providerDescs);
+ Timestamp lastTimestamp = primDescs.getLatestTimestamp();
+ if (timestamp.compareTo(lastTimestamp) <= 0) {
+ // outdated event ignore
+ return null;
+ }
+
+ offline.put(deviceId, timestamp);
+
+ Device device = devices.get(deviceId);
+ if (device == null) {
+ return null;
+ }
+ boolean removed = availableDevices.remove(deviceId);
+ if (removed) {
+ // TODO: broadcast ... DOWN only?
+ return new DeviceEvent(DEVICE_AVAILABILITY_CHANGED, device, null);
+ }
+ return null;
+ }
+ }
+
+ /**
+ * Marks the device as available if the given timestamp is not outdated,
+ * compared to the time the device has been marked offline.
+ *
+ * @param deviceId identifier of the device
+ * @param timestamp of the event triggering this change.
+ * @return true if availability change request was accepted and changed the state
+ */
+ // Guarded by deviceDescs value (=Device lock)
+ private boolean markOnline(DeviceId deviceId, Timestamp timestamp) {
+ // accept on-line if given timestamp is newer than
+ // the latest offline request Timestamp
+ Timestamp offlineTimestamp = offline.get(deviceId);
+ if (offlineTimestamp == null ||
+ offlineTimestamp.compareTo(timestamp) < 0) {
+
+ offline.remove(deviceId);
+ return availableDevices.add(deviceId);
+ }
+ return false;
+ }
+
+ @Override
+ public synchronized List<DeviceEvent> updatePorts(ProviderId providerId,
+ DeviceId deviceId,
+ List<PortDescription> portDescriptions) {
+ Timestamp newTimestamp = clockService.getTimestamp(deviceId);
+
+ Timestamped<List<PortDescription>> timestampedPortDescriptions =
+ new Timestamped<>(portDescriptions, newTimestamp);
+
+ List<DeviceEvent> events = updatePortsInternal(providerId, deviceId, timestampedPortDescriptions);
+ if (!events.isEmpty()) {
+ log.info("Notifying peers of a port update topology event for providerId: {} and deviceId: {}",
+ providerId, deviceId);
+ try {
+ notifyPeers(new InternalPortEvent(providerId, deviceId, timestampedPortDescriptions));
+ } catch (IOException e) {
+ log.error("Failed to notify peers of a port update topology event or providerId: "
+ + providerId + " and deviceId: " + deviceId, e);
+ }
+ }
+ return events;
+ }
+
+ private List<DeviceEvent> updatePortsInternal(ProviderId providerId,
+ DeviceId deviceId,
+ Timestamped<List<PortDescription>> portDescriptions) {
+
+ Device device = devices.get(deviceId);
+ checkArgument(device != null, DEVICE_NOT_FOUND, deviceId);
+
+ ConcurrentMap<ProviderId, DeviceDescriptions> descsMap = deviceDescs.get(deviceId);
+ checkArgument(descsMap != null, DEVICE_NOT_FOUND, deviceId);
+
+ List<DeviceEvent> events = new ArrayList<>();
+ synchronized (descsMap) {
+
+ if (isDeviceRemoved(deviceId, portDescriptions.timestamp())) {
+ log.debug("Ignoring outdated events: {}", portDescriptions);
+ return null;
+ }
+
+ DeviceDescriptions descs = descsMap.get(providerId);
+ // every provider must provide DeviceDescription.
+ checkArgument(descs != null,
+ "Device description for Device ID %s from Provider %s was not found",
+ deviceId, providerId);
+
+ Map<PortNumber, Port> ports = getPortMap(deviceId);
+
+ final Timestamp newTimestamp = portDescriptions.timestamp();
+
+ // Add new ports
+ Set<PortNumber> processed = new HashSet<>();
+ for (PortDescription portDescription : portDescriptions.value()) {
+ final PortNumber number = portDescription.portNumber();
+ processed.add(number);
+
+ final Port oldPort = ports.get(number);
+ final Port newPort;
+
+
+ final Timestamped<PortDescription> existingPortDesc = descs.getPortDesc(number);
+ if (existingPortDesc == null ||
+ newTimestamp.compareTo(existingPortDesc.timestamp()) >= 0) {
+ // on new port or valid update
+ // update description
+ descs.putPortDesc(new Timestamped<>(portDescription,
+ portDescriptions.timestamp()));
+ newPort = composePort(device, number, descsMap);
+ } else {
+ // outdated event, ignored.
+ continue;
+ }
+
+ events.add(oldPort == null ?
+ createPort(device, newPort, ports) :
+ updatePort(device, oldPort, newPort, ports));
+ }
+
+ events.addAll(pruneOldPorts(device, ports, processed));
+ }
+ return FluentIterable.from(events).filter(notNull()).toList();
+ }
+
+ // Creates a new port based on the port description adds it to the map and
+ // Returns corresponding event.
+ // Guarded by deviceDescs value (=Device lock)
+ private DeviceEvent createPort(Device device, Port newPort,
+ Map<PortNumber, Port> ports) {
+ ports.put(newPort.number(), newPort);
+ return new DeviceEvent(PORT_ADDED, device, newPort);
+ }
+
+ // Checks if the specified port requires update and if so, it replaces the
+ // existing entry in the map and returns corresponding event.
+ // Guarded by deviceDescs value (=Device lock)
+ private DeviceEvent updatePort(Device device, Port oldPort,
+ Port newPort,
+ Map<PortNumber, Port> ports) {
+ if (oldPort.isEnabled() != newPort.isEnabled() ||
+ !AnnotationsUtil.isEqual(oldPort.annotations(), newPort.annotations())) {
+
+ ports.put(oldPort.number(), newPort);
+ return new DeviceEvent(PORT_UPDATED, device, newPort);
+ }
+ return null;
+ }
+
+ // Prunes the specified list of ports based on which ports are in the
+ // processed list and returns list of corresponding events.
+ // Guarded by deviceDescs value (=Device lock)
+ private List<DeviceEvent> pruneOldPorts(Device device,
+ Map<PortNumber, Port> ports,
+ Set<PortNumber> processed) {
+ List<DeviceEvent> events = new ArrayList<>();
+ Iterator<PortNumber> iterator = ports.keySet().iterator();
+ while (iterator.hasNext()) {
+ PortNumber portNumber = iterator.next();
+ if (!processed.contains(portNumber)) {
+ events.add(new DeviceEvent(PORT_REMOVED, device,
+ ports.get(portNumber)));
+ iterator.remove();
+ }
+ }
+ return events;
+ }
+
+ // Gets the map of ports for the specified device; if one does not already
+ // exist, it creates and registers a new one.
+ private ConcurrentMap<PortNumber, Port> getPortMap(DeviceId deviceId) {
+ return createIfAbsentUnchecked(devicePorts, deviceId,
+ NewConcurrentHashMap.<PortNumber, Port>ifNeeded());
+ }
+
+ private ConcurrentMap<ProviderId, DeviceDescriptions> getDeviceDescriptions(
+ DeviceId deviceId) {
+ return createIfAbsentUnchecked(deviceDescs, deviceId,
+ NewConcurrentHashMap.<ProviderId, DeviceDescriptions>ifNeeded());
+ }
+
+ @Override
+ public synchronized DeviceEvent updatePortStatus(ProviderId providerId, DeviceId deviceId,
+ PortDescription portDescription) {
+ Timestamp newTimestamp = clockService.getTimestamp(deviceId);
+ final Timestamped<PortDescription> deltaDesc = new Timestamped<>(portDescription, newTimestamp);
+ DeviceEvent event = updatePortStatusInternal(providerId, deviceId, deltaDesc);
+ if (event != null) {
+ log.info("Notifying peers of a port status update topology event for providerId: {} and deviceId: {}",
+ providerId, deviceId);
+ try {
+ notifyPeers(new InternalPortStatusEvent(providerId, deviceId, deltaDesc));
+ } catch (IOException e) {
+ log.error("Failed to notify peers of a port status update topology event or providerId: "
+ + providerId + " and deviceId: " + deviceId, e);
+ }
+ }
+ return event;
+ }
+
+ private DeviceEvent updatePortStatusInternal(ProviderId providerId, DeviceId deviceId,
+ Timestamped<PortDescription> deltaDesc) {
+
+ Device device = devices.get(deviceId);
+ checkArgument(device != null, DEVICE_NOT_FOUND, deviceId);
+
+ ConcurrentMap<ProviderId, DeviceDescriptions> descsMap = deviceDescs.get(deviceId);
+ checkArgument(descsMap != null, DEVICE_NOT_FOUND, deviceId);
+
+ synchronized (descsMap) {
+
+ if (isDeviceRemoved(deviceId, deltaDesc.timestamp())) {
+ log.debug("Ignoring outdated event: {}", deltaDesc);
+ return null;
+ }
+
+ DeviceDescriptions descs = descsMap.get(providerId);
+ // assuming all providers must to give DeviceDescription
+ checkArgument(descs != null,
+ "Device description for Device ID %s from Provider %s was not found",
+ deviceId, providerId);
+
+ ConcurrentMap<PortNumber, Port> ports = getPortMap(deviceId);
+ final PortNumber number = deltaDesc.value().portNumber();
+ final Port oldPort = ports.get(number);
+ final Port newPort;
+
+ final Timestamped<PortDescription> existingPortDesc = descs.getPortDesc(number);
+ if (existingPortDesc == null ||
+ deltaDesc == existingPortDesc ||
+ deltaDesc.isNewer(existingPortDesc)) {
+ // on new port or valid update
+ // update description
+ descs.putPortDesc(deltaDesc);
+ newPort = composePort(device, number, descsMap);
+ } else {
+ // outdated event, ignored.
+ return null;
+ }
+
+ if (oldPort == null) {
+ return createPort(device, newPort, ports);
+ } else {
+ return updatePort(device, oldPort, newPort, ports);
+ }
+ }
+ }
+
+ @Override
+ public List<Port> getPorts(DeviceId deviceId) {
+ Map<PortNumber, Port> ports = devicePorts.get(deviceId);
+ if (ports == null) {
+ return Collections.emptyList();
+ }
+ return ImmutableList.copyOf(ports.values());
+ }
+
+ @Override
+ public Port getPort(DeviceId deviceId, PortNumber portNumber) {
+ Map<PortNumber, Port> ports = devicePorts.get(deviceId);
+ return ports == null ? null : ports.get(portNumber);
+ }
+
+ @Override
+ public boolean isAvailable(DeviceId deviceId) {
+ return availableDevices.contains(deviceId);
+ }
+
+ @Override
+ public synchronized DeviceEvent removeDevice(DeviceId deviceId) {
+ Timestamp timestamp = clockService.getTimestamp(deviceId);
+ DeviceEvent event = removeDeviceInternal(deviceId, timestamp);
+ if (event != null) {
+ log.info("Notifying peers of a device removed topology event for deviceId: {}",
+ deviceId);
+ try {
+ notifyPeers(new InternalDeviceRemovedEvent(deviceId, timestamp));
+ } catch (IOException e) {
+ log.error("Failed to notify peers of a device removed topology event for deviceId: {}",
+ deviceId);
+ }
+ }
+ return event;
+ }
+
+ private DeviceEvent removeDeviceInternal(DeviceId deviceId,
+ Timestamp timestamp) {
+
+ Map<ProviderId, DeviceDescriptions> descs = getDeviceDescriptions(deviceId);
+ synchronized (descs) {
+ // accept removal request if given timestamp is newer than
+ // the latest Timestamp from Primary provider
+ DeviceDescriptions primDescs = getPrimaryDescriptions(descs);
+ Timestamp lastTimestamp = primDescs.getLatestTimestamp();
+ if (timestamp.compareTo(lastTimestamp) <= 0) {
+ // outdated event ignore
+ return null;
+ }
+ removalRequest.put(deviceId, timestamp);
+
+ Device device = devices.remove(deviceId);
+ // should DEVICE_REMOVED carry removed ports?
+ Map<PortNumber, Port> ports = devicePorts.get(deviceId);
+ if (ports != null) {
+ ports.clear();
+ }
+ markOfflineInternal(deviceId, timestamp);
+ descs.clear();
+ return device == null ? null :
+ new DeviceEvent(DEVICE_REMOVED, device, null);
+ }
+ }
+
+ private boolean isDeviceRemoved(DeviceId deviceId, Timestamp timestampToCheck) {
+ Timestamp removalTimestamp = removalRequest.get(deviceId);
+ if (removalTimestamp != null &&
+ removalTimestamp.compareTo(timestampToCheck) >= 0) {
+ // removalRequest is more recent
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Returns a Device, merging description given from multiple Providers.
+ *
+ * @param deviceId device identifier
+ * @param providerDescs Collection of Descriptions from multiple providers
+ * @return Device instance
+ */
+ private Device composeDevice(DeviceId deviceId,
+ ConcurrentMap<ProviderId, DeviceDescriptions> providerDescs) {
+
+ checkArgument(!providerDescs.isEmpty(), "No Device descriptions supplied");
+
+ ProviderId primary = pickPrimaryPID(providerDescs);
+
+ DeviceDescriptions desc = providerDescs.get(primary);
+
+ final DeviceDescription base = desc.getDeviceDesc().value();
+ Type type = base.type();
+ String manufacturer = base.manufacturer();
+ String hwVersion = base.hwVersion();
+ String swVersion = base.swVersion();
+ String serialNumber = base.serialNumber();
+ DefaultAnnotations annotations = DefaultAnnotations.builder().build();
+ annotations = merge(annotations, base.annotations());
+
+ for (Entry<ProviderId, DeviceDescriptions> e : providerDescs.entrySet()) {
+ if (e.getKey().equals(primary)) {
+ continue;
+ }
+ // TODO: should keep track of Description timestamp
+ // and only merge conflicting keys when timestamp is newer
+ // Currently assuming there will never be a key conflict between
+ // providers
+
+ // annotation merging. not so efficient, should revisit later
+ annotations = merge(annotations, e.getValue().getDeviceDesc().value().annotations());
+ }
+
+ return new DefaultDevice(primary, deviceId , type, manufacturer,
+ hwVersion, swVersion, serialNumber, annotations);
+ }
+
+ /**
+ * Returns a Port, merging description given from multiple Providers.
+ *
+ * @param device device the port is on
+ * @param number port number
+ * @param providerDescs Collection of Descriptions from multiple providers
+ * @return Port instance
+ */
+ private Port composePort(Device device, PortNumber number,
+ ConcurrentMap<ProviderId, DeviceDescriptions> providerDescs) {
+
+ ProviderId primary = pickPrimaryPID(providerDescs);
+ DeviceDescriptions primDescs = providerDescs.get(primary);
+ // if no primary, assume not enabled
+ // TODO: revisit this default port enabled/disabled behavior
+ boolean isEnabled = false;
+ DefaultAnnotations annotations = DefaultAnnotations.builder().build();
+
+ final Timestamped<PortDescription> portDesc = primDescs.getPortDesc(number);
+ if (portDesc != null) {
+ isEnabled = portDesc.value().isEnabled();
+ annotations = merge(annotations, portDesc.value().annotations());
+ }
+
+ for (Entry<ProviderId, DeviceDescriptions> e : providerDescs.entrySet()) {
+ if (e.getKey().equals(primary)) {
+ continue;
+ }
+ // TODO: should keep track of Description timestamp
+ // and only merge conflicting keys when timestamp is newer
+ // Currently assuming there will never be a key conflict between
+ // providers
+
+ // annotation merging. not so efficient, should revisit later
+ final Timestamped<PortDescription> otherPortDesc = e.getValue().getPortDesc(number);
+ if (otherPortDesc != null) {
+ annotations = merge(annotations, otherPortDesc.value().annotations());
+ }
+ }
+
+ return new DefaultPort(device, number, isEnabled, annotations);
+ }
+
+ /**
+ * @return primary ProviderID, or randomly chosen one if none exists
+ */
+ private ProviderId pickPrimaryPID(
+ Map<ProviderId, DeviceDescriptions> providerDescs) {
+ ProviderId fallBackPrimary = null;
+ for (Entry<ProviderId, DeviceDescriptions> e : providerDescs.entrySet()) {
+ if (!e.getKey().isAncillary()) {
+ return e.getKey();
+ } else if (fallBackPrimary == null) {
+ // pick randomly as a fallback in case there is no primary
+ fallBackPrimary = e.getKey();
+ }
+ }
+ return fallBackPrimary;
+ }
+
+ private DeviceDescriptions getPrimaryDescriptions(
+ Map<ProviderId, DeviceDescriptions> providerDescs) {
+ ProviderId pid = pickPrimaryPID(providerDescs);
+ return providerDescs.get(pid);
+ }
+
+ public static final class InitDeviceDescs
+ implements ConcurrentInitializer<DeviceDescriptions> {
+
+ private final Timestamped<DeviceDescription> deviceDesc;
+
+ public InitDeviceDescs(Timestamped<DeviceDescription> deviceDesc) {
+ this.deviceDesc = checkNotNull(deviceDesc);
+ }
+ @Override
+ public DeviceDescriptions get() throws ConcurrentException {
+ return new DeviceDescriptions(deviceDesc);
+ }
+ }
+
+
+ /**
+ * Collection of Description of a Device and it's Ports given from a Provider.
+ */
+ public static class DeviceDescriptions {
+
+ private final AtomicReference<Timestamped<DeviceDescription>> deviceDesc;
+ private final ConcurrentMap<PortNumber, Timestamped<PortDescription>> portDescs;
+
+ public DeviceDescriptions(Timestamped<DeviceDescription> desc) {
+ this.deviceDesc = new AtomicReference<>(checkNotNull(desc));
+ this.portDescs = new ConcurrentHashMap<>();
+ }
+
+ Timestamp getLatestTimestamp() {
+ Timestamp latest = deviceDesc.get().timestamp();
+ for (Timestamped<PortDescription> desc : portDescs.values()) {
+ if (desc.timestamp().compareTo(latest) > 0) {
+ latest = desc.timestamp();
+ }
+ }
+ return latest;
+ }
+
+ public Timestamped<DeviceDescription> getDeviceDesc() {
+ return deviceDesc.get();
+ }
+
+ public Timestamped<PortDescription> getPortDesc(PortNumber number) {
+ return portDescs.get(number);
+ }
+
+ /**
+ * Puts DeviceDescription, merging annotations as necessary.
+ *
+ * @param newDesc new DeviceDescription
+ * @return previous DeviceDescription
+ */
+ public synchronized Timestamped<DeviceDescription> putDeviceDesc(Timestamped<DeviceDescription> newDesc) {
+ Timestamped<DeviceDescription> oldOne = deviceDesc.get();
+ Timestamped<DeviceDescription> newOne = newDesc;
+ if (oldOne != null) {
+ SparseAnnotations merged = union(oldOne.value().annotations(),
+ newDesc.value().annotations());
+ newOne = new Timestamped<DeviceDescription>(
+ new DefaultDeviceDescription(newDesc.value(), merged),
+ newDesc.timestamp());
+ }
+ return deviceDesc.getAndSet(newOne);
+ }
+
+ /**
+ * Puts PortDescription, merging annotations as necessary.
+ *
+ * @param newDesc new PortDescription
+ * @return previous PortDescription
+ */
+ public synchronized Timestamped<PortDescription> putPortDesc(Timestamped<PortDescription> newDesc) {
+ Timestamped<PortDescription> oldOne = portDescs.get(newDesc.value().portNumber());
+ Timestamped<PortDescription> newOne = newDesc;
+ if (oldOne != null) {
+ SparseAnnotations merged = union(oldOne.value().annotations(),
+ newDesc.value().annotations());
+ newOne = new Timestamped<PortDescription>(
+ new DefaultPortDescription(newDesc.value(), merged),
+ newDesc.timestamp());
+ }
+ return portDescs.put(newOne.value().portNumber(), newOne);
+ }
+ }
+
+ private void notifyPeers(InternalDeviceEvent event) throws IOException {
+ ClusterMessage message = new ClusterMessage(
+ clusterService.getLocalNode().id(),
+ GossipDeviceStoreMessageSubjects.DEVICE_UPDATE,
+ SERIALIZER.encode(event));
+ clusterCommunicator.broadcast(message);
+ }
+
+ private void notifyPeers(InternalDeviceOfflineEvent event) throws IOException {
+ ClusterMessage message = new ClusterMessage(
+ clusterService.getLocalNode().id(),
+ GossipDeviceStoreMessageSubjects.DEVICE_OFFLINE,
+ SERIALIZER.encode(event));
+ clusterCommunicator.broadcast(message);
+ }
+
+ private void notifyPeers(InternalDeviceRemovedEvent event) throws IOException {
+ ClusterMessage message = new ClusterMessage(
+ clusterService.getLocalNode().id(),
+ GossipDeviceStoreMessageSubjects.DEVICE_REMOVED,
+ SERIALIZER.encode(event));
+ clusterCommunicator.broadcast(message);
+ }
+
+ private void notifyPeers(InternalPortEvent event) throws IOException {
+ ClusterMessage message = new ClusterMessage(
+ clusterService.getLocalNode().id(),
+ GossipDeviceStoreMessageSubjects.PORT_UPDATE,
+ SERIALIZER.encode(event));
+ clusterCommunicator.broadcast(message);
+ }
+
+ private void notifyPeers(InternalPortStatusEvent event) throws IOException {
+ ClusterMessage message = new ClusterMessage(
+ clusterService.getLocalNode().id(),
+ GossipDeviceStoreMessageSubjects.PORT_STATUS_UPDATE,
+ SERIALIZER.encode(event));
+ clusterCommunicator.broadcast(message);
+ }
+
+ private class InternalDeviceEventListener implements ClusterMessageHandler {
+ @Override
+ public void handle(ClusterMessage message) {
+
+ log.info("Received device update event from peer: {}", message.sender());
+ InternalDeviceEvent event = (InternalDeviceEvent) SERIALIZER.decode(message.payload());
+
+ ProviderId providerId = event.providerId();
+ DeviceId deviceId = event.deviceId();
+ Timestamped<DeviceDescription> deviceDescription = event.deviceDescription();
+
+ createOrUpdateDeviceInternal(providerId, deviceId, deviceDescription);
+ }
+ }
+
+ private class InternalDeviceOfflineEventListener implements ClusterMessageHandler {
+ @Override
+ public void handle(ClusterMessage message) {
+
+ log.info("Received device offline event from peer: {}", message.sender());
+ InternalDeviceOfflineEvent event = (InternalDeviceOfflineEvent) SERIALIZER.decode(message.payload());
+
+ DeviceId deviceId = event.deviceId();
+ Timestamp timestamp = event.timestamp();
+
+ markOfflineInternal(deviceId, timestamp);
+ }
+ }
+
+ private class InternalDeviceRemovedEventListener implements ClusterMessageHandler {
+ @Override
+ public void handle(ClusterMessage message) {
+
+ log.info("Received device removed event from peer: {}", message.sender());
+ InternalDeviceRemovedEvent event = (InternalDeviceRemovedEvent) SERIALIZER.decode(message.payload());
+
+ DeviceId deviceId = event.deviceId();
+ Timestamp timestamp = event.timestamp();
+
+ removeDeviceInternal(deviceId, timestamp);
+ }
+ }
+
+ private class InternalPortEventListener implements ClusterMessageHandler {
+ @Override
+ public void handle(ClusterMessage message) {
+
+ log.info("Received port update event from peer: {}", message.sender());
+ InternalPortEvent event = (InternalPortEvent) SERIALIZER.decode(message.payload());
+
+ ProviderId providerId = event.providerId();
+ DeviceId deviceId = event.deviceId();
+ Timestamped<List<PortDescription>> portDescriptions = event.portDescriptions();
+
+ updatePortsInternal(providerId, deviceId, portDescriptions);
+ }
+ }
+
+ private class InternalPortStatusEventListener implements ClusterMessageHandler {
+ @Override
+ public void handle(ClusterMessage message) {
+
+ log.info("Received port status update event from peer: {}", message.sender());
+ InternalPortStatusEvent event = (InternalPortStatusEvent) SERIALIZER.decode(message.payload());
+
+ ProviderId providerId = event.providerId();
+ DeviceId deviceId = event.deviceId();
+ Timestamped<PortDescription> portDescription = event.portDescription();
+
+ updatePortStatusInternal(providerId, deviceId, portDescription);
+ }
+ }
+}
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/GossipDeviceStoreMessageSubjects.java b/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/GossipDeviceStoreMessageSubjects.java
new file mode 100644
index 0000000..5272182
--- /dev/null
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/GossipDeviceStoreMessageSubjects.java
@@ -0,0 +1,17 @@
+package org.onlab.onos.store.device.impl;
+
+import org.onlab.onos.store.cluster.messaging.MessageSubject;
+
+/**
+ * MessageSubjects used by GossipDeviceStore peer-peer communication.
+ */
+public final class GossipDeviceStoreMessageSubjects {
+
+ private GossipDeviceStoreMessageSubjects() {}
+
+ public static final MessageSubject DEVICE_UPDATE = new MessageSubject("peer-device-update");
+ public static final MessageSubject DEVICE_OFFLINE = new MessageSubject("peer-device-offline");
+ public static final MessageSubject DEVICE_REMOVED = new MessageSubject("peer-device-removed");
+ public static final MessageSubject PORT_UPDATE = new MessageSubject("peer-port-update");
+ public static final MessageSubject PORT_STATUS_UPDATE = new MessageSubject("peer-port-status-update");
+}
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/InternalDeviceEvent.java b/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/InternalDeviceEvent.java
new file mode 100644
index 0000000..4214384
--- /dev/null
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/InternalDeviceEvent.java
@@ -0,0 +1,45 @@
+package org.onlab.onos.store.device.impl;
+
+import org.onlab.onos.net.DeviceId;
+import org.onlab.onos.net.device.DeviceDescription;
+import org.onlab.onos.net.provider.ProviderId;
+import org.onlab.onos.store.common.impl.Timestamped;
+
+/**
+ * Information published by GossipDeviceStore to notify peers of a device
+ * change event.
+ */
+public class InternalDeviceEvent {
+
+ private final ProviderId providerId;
+ private final DeviceId deviceId;
+ private final Timestamped<DeviceDescription> deviceDescription;
+
+ protected InternalDeviceEvent(
+ ProviderId providerId,
+ DeviceId deviceId,
+ Timestamped<DeviceDescription> deviceDescription) {
+ this.providerId = providerId;
+ this.deviceId = deviceId;
+ this.deviceDescription = deviceDescription;
+ }
+
+ public DeviceId deviceId() {
+ return deviceId;
+ }
+
+ public ProviderId providerId() {
+ return providerId;
+ }
+
+ public Timestamped<DeviceDescription> deviceDescription() {
+ return deviceDescription;
+ }
+
+ // for serializer
+ protected InternalDeviceEvent() {
+ this.providerId = null;
+ this.deviceId = null;
+ this.deviceDescription = null;
+ }
+}
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/InternalDeviceEventSerializer.java b/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/InternalDeviceEventSerializer.java
new file mode 100644
index 0000000..0d3d013
--- /dev/null
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/InternalDeviceEventSerializer.java
@@ -0,0 +1,43 @@
+package org.onlab.onos.store.device.impl;
+
+import org.onlab.onos.net.DeviceId;
+import org.onlab.onos.net.device.DeviceDescription;
+import org.onlab.onos.net.provider.ProviderId;
+import org.onlab.onos.store.common.impl.Timestamped;
+
+import com.esotericsoftware.kryo.Kryo;
+import com.esotericsoftware.kryo.Serializer;
+import com.esotericsoftware.kryo.io.Input;
+import com.esotericsoftware.kryo.io.Output;
+
+/**
+ * Kryo Serializer for {@link InternalDeviceEvent}.
+ */
+public class InternalDeviceEventSerializer extends Serializer<InternalDeviceEvent> {
+
+ /**
+ * Creates a serializer for {@link InternalDeviceEvent}.
+ */
+ public InternalDeviceEventSerializer() {
+ // does not accept null
+ super(false);
+ }
+
+ @Override
+ public void write(Kryo kryo, Output output, InternalDeviceEvent event) {
+ kryo.writeClassAndObject(output, event.providerId());
+ kryo.writeClassAndObject(output, event.deviceId());
+ kryo.writeClassAndObject(output, event.deviceDescription());
+ }
+
+ @Override
+ public InternalDeviceEvent read(Kryo kryo, Input input,
+ Class<InternalDeviceEvent> type) {
+ ProviderId providerId = (ProviderId) kryo.readClassAndObject(input);
+ DeviceId deviceId = (DeviceId) kryo.readClassAndObject(input);
+ Timestamped<DeviceDescription> deviceDescription
+ = (Timestamped<DeviceDescription>) kryo.readClassAndObject(input);
+
+ return new InternalDeviceEvent(providerId, deviceId, deviceDescription);
+ }
+}
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/InternalDeviceOfflineEvent.java b/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/InternalDeviceOfflineEvent.java
new file mode 100644
index 0000000..d8942d6
--- /dev/null
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/InternalDeviceOfflineEvent.java
@@ -0,0 +1,39 @@
+package org.onlab.onos.store.device.impl;
+
+import org.onlab.onos.net.DeviceId;
+import org.onlab.onos.store.Timestamp;
+
+/**
+ * Information published by GossipDeviceStore to notify peers of a device
+ * going offline.
+ */
+public class InternalDeviceOfflineEvent {
+
+ private final DeviceId deviceId;
+ private final Timestamp timestamp;
+
+ /**
+ * Creates a InternalDeviceOfflineEvent.
+ * @param deviceId identifier of device going offline.
+ * @param timestamp timestamp of when the device went offline.
+ */
+ public InternalDeviceOfflineEvent(DeviceId deviceId, Timestamp timestamp) {
+ this.deviceId = deviceId;
+ this.timestamp = timestamp;
+ }
+
+ public DeviceId deviceId() {
+ return deviceId;
+ }
+
+ public Timestamp timestamp() {
+ return timestamp;
+ }
+
+ // for serializer
+ @SuppressWarnings("unused")
+ private InternalDeviceOfflineEvent() {
+ deviceId = null;
+ timestamp = null;
+ }
+}
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/InternalDeviceOfflineEventSerializer.java b/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/InternalDeviceOfflineEventSerializer.java
new file mode 100644
index 0000000..7059636
--- /dev/null
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/InternalDeviceOfflineEventSerializer.java
@@ -0,0 +1,38 @@
+package org.onlab.onos.store.device.impl;
+
+import org.onlab.onos.net.DeviceId;
+import org.onlab.onos.store.Timestamp;
+
+import com.esotericsoftware.kryo.Kryo;
+import com.esotericsoftware.kryo.Serializer;
+import com.esotericsoftware.kryo.io.Input;
+import com.esotericsoftware.kryo.io.Output;
+
+/**
+ * Kryo Serializer for {@link InternalDeviceOfflineEvent}.
+ */
+public class InternalDeviceOfflineEventSerializer extends Serializer<InternalDeviceOfflineEvent> {
+
+ /**
+ * Creates a serializer for {@link InternalDeviceOfflineEvent}.
+ */
+ public InternalDeviceOfflineEventSerializer() {
+ // does not accept null
+ super(false);
+ }
+
+ @Override
+ public void write(Kryo kryo, Output output, InternalDeviceOfflineEvent event) {
+ kryo.writeClassAndObject(output, event.deviceId());
+ kryo.writeClassAndObject(output, event.timestamp());
+ }
+
+ @Override
+ public InternalDeviceOfflineEvent read(Kryo kryo, Input input,
+ Class<InternalDeviceOfflineEvent> type) {
+ DeviceId deviceId = (DeviceId) kryo.readClassAndObject(input);
+ Timestamp timestamp = (Timestamp) kryo.readClassAndObject(input);
+
+ return new InternalDeviceOfflineEvent(deviceId, timestamp);
+ }
+}
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/InternalDeviceRemovedEvent.java b/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/InternalDeviceRemovedEvent.java
new file mode 100644
index 0000000..6c8b905
--- /dev/null
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/InternalDeviceRemovedEvent.java
@@ -0,0 +1,39 @@
+package org.onlab.onos.store.device.impl;
+
+import org.onlab.onos.net.DeviceId;
+import org.onlab.onos.store.Timestamp;
+
+/**
+ * Information published by GossipDeviceStore to notify peers of a device
+ * being administratively removed.
+ */
+public class InternalDeviceRemovedEvent {
+
+ private final DeviceId deviceId;
+ private final Timestamp timestamp;
+
+ /**
+ * Creates a InternalDeviceRemovedEvent.
+ * @param deviceId identifier of the removed device.
+ * @param timestamp timestamp of when the device was administratively removed.
+ */
+ public InternalDeviceRemovedEvent(DeviceId deviceId, Timestamp timestamp) {
+ this.deviceId = deviceId;
+ this.timestamp = timestamp;
+ }
+
+ public DeviceId deviceId() {
+ return deviceId;
+ }
+
+ public Timestamp timestamp() {
+ return timestamp;
+ }
+
+ // for serializer
+ @SuppressWarnings("unused")
+ private InternalDeviceRemovedEvent() {
+ deviceId = null;
+ timestamp = null;
+ }
+}
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/InternalPortEvent.java b/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/InternalPortEvent.java
new file mode 100644
index 0000000..64e77ca
--- /dev/null
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/InternalPortEvent.java
@@ -0,0 +1,47 @@
+package org.onlab.onos.store.device.impl;
+
+import java.util.List;
+
+import org.onlab.onos.net.DeviceId;
+import org.onlab.onos.net.device.PortDescription;
+import org.onlab.onos.net.provider.ProviderId;
+import org.onlab.onos.store.common.impl.Timestamped;
+
+/**
+ * Information published by GossipDeviceStore to notify peers of a port
+ * change event.
+ */
+public class InternalPortEvent {
+
+ private final ProviderId providerId;
+ private final DeviceId deviceId;
+ private final Timestamped<List<PortDescription>> portDescriptions;
+
+ protected InternalPortEvent(
+ ProviderId providerId,
+ DeviceId deviceId,
+ Timestamped<List<PortDescription>> portDescriptions) {
+ this.providerId = providerId;
+ this.deviceId = deviceId;
+ this.portDescriptions = portDescriptions;
+ }
+
+ public DeviceId deviceId() {
+ return deviceId;
+ }
+
+ public ProviderId providerId() {
+ return providerId;
+ }
+
+ public Timestamped<List<PortDescription>> portDescriptions() {
+ return portDescriptions;
+ }
+
+ // for serializer
+ protected InternalPortEvent() {
+ this.providerId = null;
+ this.deviceId = null;
+ this.portDescriptions = null;
+ }
+}
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/InternalPortEventSerializer.java b/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/InternalPortEventSerializer.java
new file mode 100644
index 0000000..6fff395
--- /dev/null
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/InternalPortEventSerializer.java
@@ -0,0 +1,45 @@
+package org.onlab.onos.store.device.impl;
+
+import java.util.List;
+
+import org.onlab.onos.net.DeviceId;
+import org.onlab.onos.net.device.PortDescription;
+import org.onlab.onos.net.provider.ProviderId;
+import org.onlab.onos.store.common.impl.Timestamped;
+
+import com.esotericsoftware.kryo.Kryo;
+import com.esotericsoftware.kryo.Serializer;
+import com.esotericsoftware.kryo.io.Input;
+import com.esotericsoftware.kryo.io.Output;
+
+/**
+ * Kryo Serializer for {@link InternalPortEvent}.
+ */
+public class InternalPortEventSerializer extends Serializer<InternalPortEvent> {
+
+ /**
+ * Creates a serializer for {@link InternalPortEvent}.
+ */
+ public InternalPortEventSerializer() {
+ // does not accept null
+ super(false);
+ }
+
+ @Override
+ public void write(Kryo kryo, Output output, InternalPortEvent event) {
+ kryo.writeClassAndObject(output, event.providerId());
+ kryo.writeClassAndObject(output, event.deviceId());
+ kryo.writeClassAndObject(output, event.portDescriptions());
+ }
+
+ @Override
+ public InternalPortEvent read(Kryo kryo, Input input,
+ Class<InternalPortEvent> type) {
+ ProviderId providerId = (ProviderId) kryo.readClassAndObject(input);
+ DeviceId deviceId = (DeviceId) kryo.readClassAndObject(input);
+ Timestamped<List<PortDescription>> portDescriptions
+ = (Timestamped<List<PortDescription>>) kryo.readClassAndObject(input);
+
+ return new InternalPortEvent(providerId, deviceId, portDescriptions);
+ }
+}
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/InternalPortStatusEvent.java b/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/InternalPortStatusEvent.java
new file mode 100644
index 0000000..7d3854b
--- /dev/null
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/InternalPortStatusEvent.java
@@ -0,0 +1,45 @@
+package org.onlab.onos.store.device.impl;
+
+import org.onlab.onos.net.DeviceId;
+import org.onlab.onos.net.device.PortDescription;
+import org.onlab.onos.net.provider.ProviderId;
+import org.onlab.onos.store.common.impl.Timestamped;
+
+/**
+ * Information published by GossipDeviceStore to notify peers of a port
+ * status change event.
+ */
+public class InternalPortStatusEvent {
+
+ private final ProviderId providerId;
+ private final DeviceId deviceId;
+ private final Timestamped<PortDescription> portDescription;
+
+ protected InternalPortStatusEvent(
+ ProviderId providerId,
+ DeviceId deviceId,
+ Timestamped<PortDescription> portDescription) {
+ this.providerId = providerId;
+ this.deviceId = deviceId;
+ this.portDescription = portDescription;
+ }
+
+ public DeviceId deviceId() {
+ return deviceId;
+ }
+
+ public ProviderId providerId() {
+ return providerId;
+ }
+
+ public Timestamped<PortDescription> portDescription() {
+ return portDescription;
+ }
+
+ // for serializer
+ protected InternalPortStatusEvent() {
+ this.providerId = null;
+ this.deviceId = null;
+ this.portDescription = null;
+ }
+}
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/InternalPortStatusEventSerializer.java b/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/InternalPortStatusEventSerializer.java
new file mode 100644
index 0000000..6ec4122
--- /dev/null
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/InternalPortStatusEventSerializer.java
@@ -0,0 +1,42 @@
+package org.onlab.onos.store.device.impl;
+
+import org.onlab.onos.net.DeviceId;
+import org.onlab.onos.net.device.PortDescription;
+import org.onlab.onos.net.provider.ProviderId;
+import org.onlab.onos.store.common.impl.Timestamped;
+
+import com.esotericsoftware.kryo.Kryo;
+import com.esotericsoftware.kryo.Serializer;
+import com.esotericsoftware.kryo.io.Input;
+import com.esotericsoftware.kryo.io.Output;
+
+/**
+ * Kryo Serializer for {@link InternalPortStatusEvent}.
+ */
+public class InternalPortStatusEventSerializer extends Serializer<InternalPortStatusEvent> {
+
+ /**
+ * Creates a serializer for {@link InternalPortStatusEvent}.
+ */
+ public InternalPortStatusEventSerializer() {
+ // does not accept null
+ super(false);
+ }
+
+ @Override
+ public void write(Kryo kryo, Output output, InternalPortStatusEvent event) {
+ kryo.writeClassAndObject(output, event.providerId());
+ kryo.writeClassAndObject(output, event.deviceId());
+ kryo.writeClassAndObject(output, event.portDescription());
+ }
+
+ @Override
+ public InternalPortStatusEvent read(Kryo kryo, Input input,
+ Class<InternalPortStatusEvent> type) {
+ ProviderId providerId = (ProviderId) kryo.readClassAndObject(input);
+ DeviceId deviceId = (DeviceId) kryo.readClassAndObject(input);
+ Timestamped<PortDescription> portDescription = (Timestamped<PortDescription>) kryo.readClassAndObject(input);
+
+ return new InternalPortStatusEvent(providerId, deviceId, portDescription);
+ }
+}
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/OnosDistributedDeviceStore.java b/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/OnosDistributedDeviceStore.java
deleted file mode 100644
index d912983..0000000
--- a/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/OnosDistributedDeviceStore.java
+++ /dev/null
@@ -1,339 +0,0 @@
-package org.onlab.onos.store.device.impl;
-
-import static com.google.common.base.Predicates.notNull;
-import static com.google.common.base.Preconditions.checkState;
-
-import com.google.common.collect.FluentIterable;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.ImmutableSet.Builder;
-
-import org.apache.felix.scr.annotations.Activate;
-import org.apache.felix.scr.annotations.Component;
-import org.apache.felix.scr.annotations.Deactivate;
-import org.apache.felix.scr.annotations.Reference;
-import org.apache.felix.scr.annotations.ReferenceCardinality;
-import org.apache.felix.scr.annotations.Service;
-import org.onlab.onos.net.DefaultDevice;
-import org.onlab.onos.net.DefaultPort;
-import org.onlab.onos.net.Device;
-import org.onlab.onos.net.DeviceId;
-import org.onlab.onos.net.Port;
-import org.onlab.onos.net.PortNumber;
-import org.onlab.onos.net.device.DeviceDescription;
-import org.onlab.onos.net.device.DeviceEvent;
-import org.onlab.onos.net.device.DeviceStore;
-import org.onlab.onos.net.device.DeviceStoreDelegate;
-import org.onlab.onos.net.device.PortDescription;
-import org.onlab.onos.net.provider.ProviderId;
-import org.onlab.onos.store.AbstractStore;
-import org.onlab.onos.store.ClockService;
-import org.onlab.onos.store.Timestamp;
-import org.slf4j.Logger;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
-
-import static com.google.common.base.Preconditions.checkArgument;
-import static org.onlab.onos.net.device.DeviceEvent.Type.*;
-import static org.slf4j.LoggerFactory.getLogger;
-
-/**
- * Manages inventory of infrastructure devices using a protocol that takes into consideration
- * the order in which device events occur.
- */
-@Component(immediate = true)
-@Service
-public class OnosDistributedDeviceStore
- extends AbstractStore<DeviceEvent, DeviceStoreDelegate>
- implements DeviceStore {
-
- private final Logger log = getLogger(getClass());
-
- public static final String DEVICE_NOT_FOUND = "Device with ID %s not found";
-
- private ConcurrentMap<DeviceId, VersionedValue<Device>> devices;
- private ConcurrentMap<DeviceId, Map<PortNumber, VersionedValue<Port>>> devicePorts;
-
- @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
- protected ClockService clockService;
-
- @Activate
- public void activate() {
-
- devices = new ConcurrentHashMap<>();
- devicePorts = new ConcurrentHashMap<>();
-
- log.info("Started");
- }
-
- @Deactivate
- public void deactivate() {
- log.info("Stopped");
- }
-
- @Override
- public int getDeviceCount() {
- return devices.size();
- }
-
- @Override
- public Iterable<Device> getDevices() {
- Builder<Device> builder = ImmutableSet.builder();
- synchronized (this) {
- for (VersionedValue<Device> device : devices.values()) {
- builder.add(device.entity());
- }
- return builder.build();
- }
- }
-
- @Override
- public Device getDevice(DeviceId deviceId) {
- VersionedValue<Device> device = devices.get(deviceId);
- checkArgument(device != null, DEVICE_NOT_FOUND, deviceId);
- return device.entity();
- }
-
- @Override
- public DeviceEvent createOrUpdateDevice(ProviderId providerId, DeviceId deviceId,
- DeviceDescription deviceDescription) {
- Timestamp newTimestamp = clockService.getTimestamp(deviceId);
- VersionedValue<Device> device = devices.get(deviceId);
-
- if (device == null) {
- return createDevice(providerId, deviceId, deviceDescription, newTimestamp);
- }
-
- checkState(newTimestamp.compareTo(device.timestamp()) > 0,
- "Existing device has a timestamp in the future!");
-
- return updateDevice(providerId, device.entity(), deviceDescription, newTimestamp);
- }
-
- // Creates the device and returns the appropriate event if necessary.
- private DeviceEvent createDevice(ProviderId providerId, DeviceId deviceId,
- DeviceDescription desc, Timestamp timestamp) {
- Device device = new DefaultDevice(providerId, deviceId, desc.type(),
- desc.manufacturer(),
- desc.hwVersion(), desc.swVersion(),
- desc.serialNumber());
-
- devices.put(deviceId, new VersionedValue<>(device, true, timestamp));
- // TODO,FIXME: broadcast a message telling peers of a device event.
- return new DeviceEvent(DEVICE_ADDED, device, null);
- }
-
- // Updates the device and returns the appropriate event if necessary.
- private DeviceEvent updateDevice(ProviderId providerId, Device device,
- DeviceDescription desc, Timestamp timestamp) {
- // We allow only certain attributes to trigger update
- if (!Objects.equals(device.hwVersion(), desc.hwVersion()) ||
- !Objects.equals(device.swVersion(), desc.swVersion())) {
-
- Device updated = new DefaultDevice(providerId, device.id(),
- desc.type(),
- desc.manufacturer(),
- desc.hwVersion(),
- desc.swVersion(),
- desc.serialNumber());
- devices.put(device.id(), new VersionedValue<Device>(updated, true, timestamp));
- // FIXME: broadcast a message telling peers of a device event.
- return new DeviceEvent(DeviceEvent.Type.DEVICE_UPDATED, updated, null);
- }
-
- // Otherwise merely attempt to change availability
- Device updated = new DefaultDevice(providerId, device.id(),
- desc.type(),
- desc.manufacturer(),
- desc.hwVersion(),
- desc.swVersion(),
- desc.serialNumber());
-
- VersionedValue<Device> oldDevice = devices.put(device.id(),
- new VersionedValue<Device>(updated, true, timestamp));
- if (!oldDevice.isUp()) {
- return new DeviceEvent(DEVICE_AVAILABILITY_CHANGED, device, null);
- } else {
- return null;
- }
- }
-
- @Override
- public DeviceEvent markOffline(DeviceId deviceId) {
- VersionedValue<Device> device = devices.get(deviceId);
- boolean willRemove = device != null && device.isUp();
- if (!willRemove) {
- return null;
- }
- Timestamp timestamp = clockService.getTimestamp(deviceId);
- if (replaceIfLatest(device.entity(), false, timestamp)) {
- return new DeviceEvent(DEVICE_AVAILABILITY_CHANGED, device.entity(), null);
- }
- return null;
- }
-
- // Replace existing value if its timestamp is older.
- private synchronized boolean replaceIfLatest(Device device, boolean isUp, Timestamp timestamp) {
- VersionedValue<Device> existingValue = devices.get(device.id());
- if (timestamp.compareTo(existingValue.timestamp()) > 0) {
- devices.put(device.id(), new VersionedValue<Device>(device, isUp, timestamp));
- return true;
- }
- return false;
- }
-
- @Override
- public List<DeviceEvent> updatePorts(ProviderId providerId, DeviceId deviceId,
- List<PortDescription> portDescriptions) {
- List<DeviceEvent> events = new ArrayList<>();
- synchronized (this) {
- VersionedValue<Device> device = devices.get(deviceId);
- checkArgument(device != null, DEVICE_NOT_FOUND, deviceId);
- Map<PortNumber, VersionedValue<Port>> ports = getPortMap(deviceId);
- Timestamp newTimestamp = clockService.getTimestamp(deviceId);
-
- // Add new ports
- Set<PortNumber> processed = new HashSet<>();
- for (PortDescription portDescription : portDescriptions) {
- VersionedValue<Port> port = ports.get(portDescription.portNumber());
- if (port == null) {
- events.add(createPort(device, portDescription, ports, newTimestamp));
- }
- checkState(newTimestamp.compareTo(port.timestamp()) > 0,
- "Existing port state has a timestamp in the future!");
- events.add(updatePort(device.entity(), port.entity(), portDescription, ports, newTimestamp));
- processed.add(portDescription.portNumber());
- }
-
- updatePortMap(deviceId, ports);
-
- events.addAll(pruneOldPorts(device.entity(), ports, processed));
- }
- return FluentIterable.from(events).filter(notNull()).toList();
- }
-
- // Creates a new port based on the port description adds it to the map and
- // Returns corresponding event.
- //@GuardedBy("this")
- private DeviceEvent createPort(VersionedValue<Device> device, PortDescription portDescription,
- Map<PortNumber, VersionedValue<Port>> ports, Timestamp timestamp) {
- Port port = new DefaultPort(device.entity(), portDescription.portNumber(),
- portDescription.isEnabled());
- ports.put(port.number(), new VersionedValue<Port>(port, true, timestamp));
- updatePortMap(device.entity().id(), ports);
- return new DeviceEvent(PORT_ADDED, device.entity(), port);
- }
-
- // Checks if the specified port requires update and if so, it replaces the
- // existing entry in the map and returns corresponding event.
- //@GuardedBy("this")
- private DeviceEvent updatePort(Device device, Port port,
- PortDescription portDescription,
- Map<PortNumber, VersionedValue<Port>> ports,
- Timestamp timestamp) {
- if (port.isEnabled() != portDescription.isEnabled()) {
- VersionedValue<Port> updatedPort = new VersionedValue<Port>(
- new DefaultPort(device, portDescription.portNumber(),
- portDescription.isEnabled()),
- portDescription.isEnabled(),
- timestamp);
- ports.put(port.number(), updatedPort);
- updatePortMap(device.id(), ports);
- return new DeviceEvent(PORT_UPDATED, device, updatedPort.entity());
- }
- return null;
- }
-
- // Prunes the specified list of ports based on which ports are in the
- // processed list and returns list of corresponding events.
- //@GuardedBy("this")
- private List<DeviceEvent> pruneOldPorts(Device device,
- Map<PortNumber, VersionedValue<Port>> ports,
- Set<PortNumber> processed) {
- List<DeviceEvent> events = new ArrayList<>();
- Iterator<PortNumber> iterator = ports.keySet().iterator();
- while (iterator.hasNext()) {
- PortNumber portNumber = iterator.next();
- if (!processed.contains(portNumber)) {
- events.add(new DeviceEvent(PORT_REMOVED, device,
- ports.get(portNumber).entity()));
- iterator.remove();
- }
- }
- if (!events.isEmpty()) {
- updatePortMap(device.id(), ports);
- }
- return events;
- }
-
- // Gets the map of ports for the specified device; if one does not already
- // exist, it creates and registers a new one.
- // WARN: returned value is a copy, changes made to the Map
- // needs to be written back using updatePortMap
- //@GuardedBy("this")
- private Map<PortNumber, VersionedValue<Port>> getPortMap(DeviceId deviceId) {
- Map<PortNumber, VersionedValue<Port>> ports = devicePorts.get(deviceId);
- if (ports == null) {
- ports = new HashMap<>();
- // this probably is waste of time in most cases.
- updatePortMap(deviceId, ports);
- }
- return ports;
- }
-
- //@GuardedBy("this")
- private void updatePortMap(DeviceId deviceId, Map<PortNumber, VersionedValue<Port>> ports) {
- devicePorts.put(deviceId, ports);
- }
-
- @Override
- public DeviceEvent updatePortStatus(ProviderId providerId, DeviceId deviceId,
- PortDescription portDescription) {
- VersionedValue<Device> device = devices.get(deviceId);
- checkArgument(device != null, DEVICE_NOT_FOUND, deviceId);
- Map<PortNumber, VersionedValue<Port>> ports = getPortMap(deviceId);
- VersionedValue<Port> port = ports.get(portDescription.portNumber());
- Timestamp timestamp = clockService.getTimestamp(deviceId);
- return updatePort(device.entity(), port.entity(), portDescription, ports, timestamp);
- }
-
- @Override
- public List<Port> getPorts(DeviceId deviceId) {
- Map<PortNumber, VersionedValue<Port>> versionedPorts = devicePorts.get(deviceId);
- if (versionedPorts == null) {
- return Collections.emptyList();
- }
- List<Port> ports = new ArrayList<>();
- for (VersionedValue<Port> port : versionedPorts.values()) {
- ports.add(port.entity());
- }
- return ports;
- }
-
- @Override
- public Port getPort(DeviceId deviceId, PortNumber portNumber) {
- Map<PortNumber, VersionedValue<Port>> ports = devicePorts.get(deviceId);
- return ports == null ? null : ports.get(portNumber).entity();
- }
-
- @Override
- public boolean isAvailable(DeviceId deviceId) {
- return devices.get(deviceId).isUp();
- }
-
- @Override
- public DeviceEvent removeDevice(DeviceId deviceId) {
- VersionedValue<Device> previousDevice = devices.remove(deviceId);
- return previousDevice == null ? null :
- new DeviceEvent(DEVICE_REMOVED, previousDevice.entity(), null);
- }
-}
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/package-info.java b/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/package-info.java
index aa644db..c1f5aad 100644
--- a/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/package-info.java
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/package-info.java
@@ -1,4 +1,4 @@
/**
* Implementation of device store using distributed distributed p2p synchronization protocol.
*/
-package org.onlab.onos.store.device.impl;
\ No newline at end of file
+package org.onlab.onos.store.device.impl;
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/flow/impl/DistributedFlowRuleStore.java b/core/store/dist/src/main/java/org/onlab/onos/store/flow/impl/DistributedFlowRuleStore.java
index 5a5592a..d49e00b 100644
--- a/core/store/dist/src/main/java/org/onlab/onos/store/flow/impl/DistributedFlowRuleStore.java
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/flow/impl/DistributedFlowRuleStore.java
@@ -1,6 +1,5 @@
package org.onlab.onos.store.flow.impl;
-import static org.onlab.onos.net.flow.FlowRuleEvent.Type.RULE_ADDED;
import static org.onlab.onos.net.flow.FlowRuleEvent.Type.RULE_REMOVED;
import static org.slf4j.LoggerFactory.getLogger;
@@ -13,9 +12,10 @@
import org.apache.felix.scr.annotations.Service;
import org.onlab.onos.ApplicationId;
import org.onlab.onos.net.DeviceId;
-import org.onlab.onos.net.flow.DefaultFlowRule;
+import org.onlab.onos.net.flow.DefaultFlowEntry;
+import org.onlab.onos.net.flow.FlowEntry;
+import org.onlab.onos.net.flow.FlowEntry.FlowEntryState;
import org.onlab.onos.net.flow.FlowRule;
-import org.onlab.onos.net.flow.FlowRule.FlowRuleState;
import org.onlab.onos.net.flow.FlowRuleEvent;
import org.onlab.onos.net.flow.FlowRuleEvent.Type;
import org.onlab.onos.net.flow.FlowRuleStore;
@@ -30,18 +30,18 @@
/**
* Manages inventory of flow rules using trivial in-memory implementation.
*/
-//FIXME: I LIE I AM NOT DISTRIBUTED
+//FIXME I LIE. I AIN'T DISTRIBUTED
@Component(immediate = true)
@Service
public class DistributedFlowRuleStore
-extends AbstractStore<FlowRuleEvent, FlowRuleStoreDelegate>
-implements FlowRuleStore {
+ extends AbstractStore<FlowRuleEvent, FlowRuleStoreDelegate>
+ implements FlowRuleStore {
private final Logger log = getLogger(getClass());
// store entries as a pile of rules, no info about device tables
- private final Multimap<DeviceId, FlowRule> flowEntries =
- ArrayListMultimap.<DeviceId, FlowRule>create();
+ private final Multimap<DeviceId, FlowEntry> flowEntries =
+ ArrayListMultimap.<DeviceId, FlowEntry>create();
private final Multimap<ApplicationId, FlowRule> flowEntriesById =
ArrayListMultimap.<ApplicationId, FlowRule>create();
@@ -58,8 +58,13 @@
@Override
- public synchronized FlowRule getFlowRule(FlowRule rule) {
- for (FlowRule f : flowEntries.get(rule.deviceId())) {
+ public int getFlowRuleCount() {
+ return flowEntries.size();
+ }
+
+ @Override
+ public synchronized FlowEntry getFlowEntry(FlowRule rule) {
+ for (FlowEntry f : flowEntries.get(rule.deviceId())) {
if (f.equals(rule)) {
return f;
}
@@ -68,8 +73,8 @@
}
@Override
- public synchronized Iterable<FlowRule> getFlowEntries(DeviceId deviceId) {
- Collection<FlowRule> rules = flowEntries.get(deviceId);
+ public synchronized Iterable<FlowEntry> getFlowEntries(DeviceId deviceId) {
+ Collection<FlowEntry> rules = flowEntries.get(deviceId);
if (rules == null) {
return Collections.emptyList();
}
@@ -77,7 +82,7 @@
}
@Override
- public synchronized Iterable<FlowRule> getFlowEntriesByAppId(ApplicationId appId) {
+ public synchronized Iterable<FlowRule> getFlowRulesByAppId(ApplicationId appId) {
Collection<FlowRule> rules = flowEntriesById.get(appId);
if (rules == null) {
return Collections.emptyList();
@@ -87,7 +92,7 @@
@Override
public synchronized void storeFlowRule(FlowRule rule) {
- FlowRule f = new DefaultFlowRule(rule, FlowRuleState.PENDING_ADD);
+ FlowEntry f = new DefaultFlowEntry(rule);
DeviceId did = f.deviceId();
if (!flowEntries.containsEntry(did, f)) {
flowEntries.put(did, f);
@@ -97,57 +102,41 @@
@Override
public synchronized void deleteFlowRule(FlowRule rule) {
- FlowRule f = new DefaultFlowRule(rule, FlowRuleState.PENDING_REMOVE);
- DeviceId did = f.deviceId();
-
- /*
- * find the rule and mark it for deletion.
- * Ultimately a flow removed will come remove it.
- */
-
- if (flowEntries.containsEntry(did, f)) {
- //synchronized (flowEntries) {
- flowEntries.remove(did, f);
- flowEntries.put(did, f);
- flowEntriesById.remove(rule.appId(), rule);
- //}
+ FlowEntry entry = getFlowEntry(rule);
+ if (entry == null) {
+ return;
}
+ entry.setState(FlowEntryState.PENDING_REMOVE);
}
@Override
- public synchronized FlowRuleEvent addOrUpdateFlowRule(FlowRule rule) {
+ public synchronized FlowRuleEvent addOrUpdateFlowRule(FlowEntry rule) {
DeviceId did = rule.deviceId();
// check if this new rule is an update to an existing entry
- if (flowEntries.containsEntry(did, rule)) {
- //synchronized (flowEntries) {
- // Multimaps support duplicates so we have to remove our rule
- // and replace it with the current version.
- flowEntries.remove(did, rule);
- flowEntries.put(did, rule);
- //}
+ FlowEntry stored = getFlowEntry(rule);
+ if (stored != null) {
+ stored.setBytes(rule.bytes());
+ stored.setLife(rule.life());
+ stored.setPackets(rule.packets());
+ if (stored.state() == FlowEntryState.PENDING_ADD) {
+ stored.setState(FlowEntryState.ADDED);
+ return new FlowRuleEvent(Type.RULE_ADDED, rule);
+ }
return new FlowRuleEvent(Type.RULE_UPDATED, rule);
}
flowEntries.put(did, rule);
- return new FlowRuleEvent(RULE_ADDED, rule);
+ return null;
}
@Override
- public synchronized FlowRuleEvent removeFlowRule(FlowRule rule) {
- //synchronized (this) {
+ public synchronized FlowRuleEvent removeFlowRule(FlowEntry rule) {
+ // This is where one could mark a rule as removed and still keep it in the store.
if (flowEntries.remove(rule.deviceId(), rule)) {
return new FlowRuleEvent(RULE_REMOVED, rule);
} else {
return null;
}
- //}
}
-
-
-
-
-
-
-
}
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/link/impl/OnosDistributedLinkStore.java b/core/store/dist/src/main/java/org/onlab/onos/store/link/impl/OnosDistributedLinkStore.java
index 5df25b4..a59b151 100644
--- a/core/store/dist/src/main/java/org/onlab/onos/store/link/impl/OnosDistributedLinkStore.java
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/link/impl/OnosDistributedLinkStore.java
@@ -42,6 +42,7 @@
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkState;
+//TODO: Add support for multiple provider and annotations
/**
* Manages inventory of infrastructure links using a protocol that takes into consideration
* the order in which events occur.
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/link/impl/package-info.java b/core/store/dist/src/main/java/org/onlab/onos/store/link/impl/package-info.java
index 127dc84..c675f84 100644
--- a/core/store/dist/src/main/java/org/onlab/onos/store/link/impl/package-info.java
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/link/impl/package-info.java
@@ -1,4 +1,4 @@
/**
* Implementation of link store using distributed p2p synchronization protocol.
*/
-package org.onlab.onos.store.link.impl;
\ No newline at end of file
+package org.onlab.onos.store.link.impl;
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/serializers/ClusterMessageSerializer.java b/core/store/dist/src/main/java/org/onlab/onos/store/serializers/ClusterMessageSerializer.java
new file mode 100644
index 0000000..c0cefd6
--- /dev/null
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/serializers/ClusterMessageSerializer.java
@@ -0,0 +1,38 @@
+package org.onlab.onos.store.serializers;
+
+import org.onlab.onos.cluster.NodeId;
+import org.onlab.onos.store.cluster.messaging.ClusterMessage;
+import org.onlab.onos.store.cluster.messaging.MessageSubject;
+import com.esotericsoftware.kryo.Kryo;
+import com.esotericsoftware.kryo.Serializer;
+import com.esotericsoftware.kryo.io.Input;
+import com.esotericsoftware.kryo.io.Output;
+
+public final class ClusterMessageSerializer extends Serializer<ClusterMessage> {
+
+ /**
+ * Creates a serializer for {@link ClusterMessage}.
+ */
+ public ClusterMessageSerializer() {
+ // does not accept null
+ super(false);
+ }
+
+ @Override
+ public void write(Kryo kryo, Output output, ClusterMessage message) {
+ kryo.writeClassAndObject(output, message.sender());
+ kryo.writeClassAndObject(output, message.subject());
+ output.writeInt(message.payload().length);
+ output.writeBytes(message.payload());
+ }
+
+ @Override
+ public ClusterMessage read(Kryo kryo, Input input,
+ Class<ClusterMessage> type) {
+ NodeId sender = (NodeId) kryo.readClassAndObject(input);
+ MessageSubject subject = (MessageSubject) kryo.readClassAndObject(input);
+ int payloadSize = input.readInt();
+ byte[] payload = input.readBytes(payloadSize);
+ return new ClusterMessage(sender, subject, payload);
+ }
+}
\ No newline at end of file
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/serializers/MastershipBasedTimestampSerializer.java b/core/store/dist/src/main/java/org/onlab/onos/store/serializers/MastershipBasedTimestampSerializer.java
new file mode 100644
index 0000000..516915e
--- /dev/null
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/serializers/MastershipBasedTimestampSerializer.java
@@ -0,0 +1,36 @@
+package org.onlab.onos.store.serializers;
+
+import org.onlab.onos.store.common.impl.MastershipBasedTimestamp;
+
+import com.esotericsoftware.kryo.Kryo;
+import com.esotericsoftware.kryo.Serializer;
+import com.esotericsoftware.kryo.io.Input;
+import com.esotericsoftware.kryo.io.Output;
+
+// To be used if Timestamp ever needs to cross bundle boundary.
+/**
+ * Kryo Serializer for {@link MastershipBasedTimestamp}.
+ */
+public class MastershipBasedTimestampSerializer extends Serializer<MastershipBasedTimestamp> {
+
+ /**
+ * Creates a serializer for {@link MastershipBasedTimestamp}.
+ */
+ public MastershipBasedTimestampSerializer() {
+ // non-null, immutable
+ super(false, true);
+ }
+
+ @Override
+ public void write(Kryo kryo, Output output, MastershipBasedTimestamp object) {
+ output.writeInt(object.termNumber());
+ output.writeInt(object.sequenceNumber());
+ }
+
+ @Override
+ public MastershipBasedTimestamp read(Kryo kryo, Input input, Class<MastershipBasedTimestamp> type) {
+ final int term = input.readInt();
+ final int sequence = input.readInt();
+ return new MastershipBasedTimestamp(term, sequence);
+ }
+}
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/serializers/MessageSubjectSerializer.java b/core/store/dist/src/main/java/org/onlab/onos/store/serializers/MessageSubjectSerializer.java
new file mode 100644
index 0000000..bb6b292
--- /dev/null
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/serializers/MessageSubjectSerializer.java
@@ -0,0 +1,31 @@
+package org.onlab.onos.store.serializers;
+
+import org.onlab.onos.store.cluster.messaging.MessageSubject;
+
+import com.esotericsoftware.kryo.Kryo;
+import com.esotericsoftware.kryo.Serializer;
+import com.esotericsoftware.kryo.io.Input;
+import com.esotericsoftware.kryo.io.Output;
+
+public final class MessageSubjectSerializer extends Serializer<MessageSubject> {
+
+ /**
+ * Creates a serializer for {@link MessageSubject}.
+ */
+ public MessageSubjectSerializer() {
+ // non-null, immutable
+ super(false, true);
+ }
+
+
+ @Override
+ public void write(Kryo kryo, Output output, MessageSubject object) {
+ output.writeString(object.value());
+ }
+
+ @Override
+ public MessageSubject read(Kryo kryo, Input input,
+ Class<MessageSubject> type) {
+ return new MessageSubject(input.readString());
+ }
+}
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/serializers/OnosTimestampSerializer.java b/core/store/dist/src/main/java/org/onlab/onos/store/serializers/OnosTimestampSerializer.java
deleted file mode 100644
index 192e035..0000000
--- a/core/store/dist/src/main/java/org/onlab/onos/store/serializers/OnosTimestampSerializer.java
+++ /dev/null
@@ -1,35 +0,0 @@
-package org.onlab.onos.store.serializers;
-
-import org.onlab.onos.store.impl.OnosTimestamp;
-
-import com.esotericsoftware.kryo.Kryo;
-import com.esotericsoftware.kryo.Serializer;
-import com.esotericsoftware.kryo.io.Input;
-import com.esotericsoftware.kryo.io.Output;
-
-/**
- * Kryo Serializer for {@link OnosTimestamp}.
- */
-public class OnosTimestampSerializer extends Serializer<OnosTimestamp> {
-
- /**
- * Default constructor.
- */
- public OnosTimestampSerializer() {
- // non-null, immutable
- super(false, true);
- }
-
- @Override
- public void write(Kryo kryo, Output output, OnosTimestamp object) {
- output.writeInt(object.termNumber());
- output.writeInt(object.sequenceNumber());
- }
-
- @Override
- public OnosTimestamp read(Kryo kryo, Input input, Class<OnosTimestamp> type) {
- final int term = input.readInt();
- final int sequence = input.readInt();
- return new OnosTimestamp(term, sequence);
- }
-}
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/topology/impl/DistributedTopologyStore.java b/core/store/dist/src/main/java/org/onlab/onos/store/topology/impl/DistributedTopologyStore.java
index 567861e..9ae9d7b 100644
--- a/core/store/dist/src/main/java/org/onlab/onos/store/topology/impl/DistributedTopologyStore.java
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/topology/impl/DistributedTopologyStore.java
@@ -125,7 +125,8 @@
// Promote the new topology to current and return a ready-to-send event.
synchronized (this) {
current = newTopology;
- return new TopologyEvent(TopologyEvent.Type.TOPOLOGY_CHANGED, current);
+ return new TopologyEvent(TopologyEvent.Type.TOPOLOGY_CHANGED,
+ current, reasons);
}
}
diff --git a/core/store/dist/src/test/java/org/onlab/onos/store/cluster/impl/ClusterCommunicationManagerTest.java b/core/store/dist/src/test/java/org/onlab/onos/store/cluster/impl/ClusterCommunicationManagerTest.java
index bba12f2..e63fcaa 100644
--- a/core/store/dist/src/test/java/org/onlab/onos/store/cluster/impl/ClusterCommunicationManagerTest.java
+++ b/core/store/dist/src/test/java/org/onlab/onos/store/cluster/impl/ClusterCommunicationManagerTest.java
@@ -7,6 +7,7 @@
import org.onlab.onos.cluster.DefaultControllerNode;
import org.onlab.onos.cluster.NodeId;
import org.onlab.onos.store.cluster.messaging.impl.ClusterCommunicationManager;
+import org.onlab.onos.store.cluster.messaging.impl.MessageSerializer;
import org.onlab.netty.NettyMessagingService;
import org.onlab.packet.IpPrefix;
diff --git a/core/store/dist/src/test/java/org/onlab/onos/store/common/impl/MastershipBasedTimestampTest.java b/core/store/dist/src/test/java/org/onlab/onos/store/common/impl/MastershipBasedTimestampTest.java
new file mode 100644
index 0000000..ea63ef8
--- /dev/null
+++ b/core/store/dist/src/test/java/org/onlab/onos/store/common/impl/MastershipBasedTimestampTest.java
@@ -0,0 +1,95 @@
+package org.onlab.onos.store.common.impl;
+
+import static org.junit.Assert.*;
+
+import java.nio.ByteBuffer;
+
+import org.junit.Test;
+import org.onlab.onos.store.Timestamp;
+import org.onlab.onos.store.serializers.MastershipBasedTimestampSerializer;
+import org.onlab.util.KryoPool;
+
+import com.google.common.testing.EqualsTester;
+
+/**
+ * Test of {@link MastershipBasedTimestamp}.
+ */
+public class MastershipBasedTimestampTest {
+
+ private static final Timestamp TS_1_1 = new MastershipBasedTimestamp(1, 1);
+ private static final Timestamp TS_1_2 = new MastershipBasedTimestamp(1, 2);
+ private static final Timestamp TS_2_1 = new MastershipBasedTimestamp(2, 1);
+ private static final Timestamp TS_2_2 = new MastershipBasedTimestamp(2, 2);
+
+ @Test
+ public final void testBasic() {
+ final int termNumber = 5;
+ final int sequenceNumber = 6;
+ MastershipBasedTimestamp ts = new MastershipBasedTimestamp(termNumber,
+ sequenceNumber);
+
+ assertEquals(termNumber, ts.termNumber());
+ assertEquals(sequenceNumber, ts.sequenceNumber());
+ }
+
+ @Test
+ public final void testCompareTo() {
+ assertTrue(TS_1_1.compareTo(TS_1_1) == 0);
+ assertTrue(TS_1_1.compareTo(new MastershipBasedTimestamp(1, 1)) == 0);
+
+ assertTrue(TS_1_1.compareTo(TS_1_2) < 0);
+ assertTrue(TS_1_2.compareTo(TS_1_1) > 0);
+
+ assertTrue(TS_1_2.compareTo(TS_2_1) < 0);
+ assertTrue(TS_1_2.compareTo(TS_2_2) < 0);
+ assertTrue(TS_2_1.compareTo(TS_1_1) > 0);
+ assertTrue(TS_2_2.compareTo(TS_1_1) > 0);
+ }
+
+ @Test
+ public final void testEqualsObject() {
+ new EqualsTester()
+ .addEqualityGroup(new MastershipBasedTimestamp(1, 1),
+ new MastershipBasedTimestamp(1, 1), TS_1_1)
+ .addEqualityGroup(new MastershipBasedTimestamp(1, 2),
+ new MastershipBasedTimestamp(1, 2), TS_1_2)
+ .addEqualityGroup(new MastershipBasedTimestamp(2, 1),
+ new MastershipBasedTimestamp(2, 1), TS_2_1)
+ .addEqualityGroup(new MastershipBasedTimestamp(2, 2),
+ new MastershipBasedTimestamp(2, 2), TS_2_2)
+ .testEquals();
+ }
+
+ @Test
+ public final void testKryoSerializable() {
+ final ByteBuffer buffer = ByteBuffer.allocate(1 * 1024 * 1024);
+ final KryoPool kryos = KryoPool.newBuilder()
+ .register(MastershipBasedTimestamp.class)
+ .build();
+
+ kryos.serialize(TS_2_1, buffer);
+ buffer.flip();
+ Timestamp copy = kryos.deserialize(buffer);
+
+ new EqualsTester()
+ .addEqualityGroup(TS_2_1, copy)
+ .testEquals();
+ }
+
+ @Test
+ public final void testKryoSerializableWithHandcraftedSerializer() {
+ final ByteBuffer buffer = ByteBuffer.allocate(1 * 1024 * 1024);
+ final KryoPool kryos = KryoPool.newBuilder()
+ .register(MastershipBasedTimestamp.class, new MastershipBasedTimestampSerializer())
+ .build();
+
+ kryos.serialize(TS_1_2, buffer);
+ buffer.flip();
+ Timestamp copy = kryos.deserialize(buffer);
+
+ new EqualsTester()
+ .addEqualityGroup(TS_1_2, copy)
+ .testEquals();
+ }
+
+}
diff --git a/core/store/dist/src/test/java/org/onlab/onos/store/common/impl/TimestampedTest.java b/core/store/dist/src/test/java/org/onlab/onos/store/common/impl/TimestampedTest.java
new file mode 100644
index 0000000..4b44d40
--- /dev/null
+++ b/core/store/dist/src/test/java/org/onlab/onos/store/common/impl/TimestampedTest.java
@@ -0,0 +1,94 @@
+package org.onlab.onos.store.common.impl;
+
+import static org.junit.Assert.*;
+
+import java.nio.ByteBuffer;
+
+import org.junit.Test;
+import org.onlab.onos.store.Timestamp;
+import org.onlab.util.KryoPool;
+
+import com.google.common.testing.EqualsTester;
+
+/**
+ * Test of {@link Timestamped}.
+ */
+public class TimestampedTest {
+
+ private static final Timestamp TS_1_1 = new MastershipBasedTimestamp(1, 1);
+ private static final Timestamp TS_1_2 = new MastershipBasedTimestamp(1, 2);
+ private static final Timestamp TS_2_1 = new MastershipBasedTimestamp(2, 1);
+
+ @Test
+ public final void testHashCode() {
+ Timestamped<String> a = new Timestamped<>("a", TS_1_1);
+ Timestamped<String> b = new Timestamped<>("b", TS_1_1);
+ assertTrue("value does not impact hashCode",
+ a.hashCode() == b.hashCode());
+ }
+
+ @Test
+ public final void testEquals() {
+ Timestamped<String> a = new Timestamped<>("a", TS_1_1);
+ Timestamped<String> b = new Timestamped<>("b", TS_1_1);
+ assertTrue("value does not impact equality",
+ a.equals(b));
+
+ new EqualsTester()
+ .addEqualityGroup(new Timestamped<>("a", TS_1_1),
+ new Timestamped<>("b", TS_1_1),
+ new Timestamped<>("c", TS_1_1))
+ .addEqualityGroup(new Timestamped<>("a", TS_1_2),
+ new Timestamped<>("b", TS_1_2),
+ new Timestamped<>("c", TS_1_2))
+ .addEqualityGroup(new Timestamped<>("a", TS_2_1),
+ new Timestamped<>("b", TS_2_1),
+ new Timestamped<>("c", TS_2_1))
+ .testEquals();
+
+ }
+
+ @Test
+ public final void testValue() {
+ final Integer n = Integer.valueOf(42);
+ Timestamped<Integer> tsv = new Timestamped<>(n, TS_1_1);
+ assertSame(n, tsv.value());
+
+ }
+
+ @Test(expected = NullPointerException.class)
+ public final void testValueNonNull() {
+ new Timestamped<>(null, TS_1_1);
+ }
+
+ @Test(expected = NullPointerException.class)
+ public final void testTimestampNonNull() {
+ new Timestamped<>("Foo", null);
+ }
+
+ @Test
+ public final void testIsNewer() {
+ Timestamped<String> a = new Timestamped<>("a", TS_1_2);
+ Timestamped<String> b = new Timestamped<>("b", TS_1_1);
+ assertTrue(a.isNewer(b));
+ assertFalse(b.isNewer(a));
+ }
+
+ @Test
+ public final void testKryoSerializable() {
+ final ByteBuffer buffer = ByteBuffer.allocate(1 * 1024 * 1024);
+ final KryoPool kryos = KryoPool.newBuilder()
+ .register(Timestamped.class,
+ MastershipBasedTimestamp.class)
+ .build();
+
+ Timestamped<String> original = new Timestamped<>("foobar", TS_1_1);
+ kryos.serialize(original, buffer);
+ buffer.flip();
+ Timestamped<String> copy = kryos.deserialize(buffer);
+
+ new EqualsTester()
+ .addEqualityGroup(original, copy)
+ .testEquals();
+ }
+}
diff --git a/core/store/dist/src/test/java/org/onlab/onos/store/device/impl/GossipDeviceStoreTest.java b/core/store/dist/src/test/java/org/onlab/onos/store/device/impl/GossipDeviceStoreTest.java
new file mode 100644
index 0000000..fa42a6b
--- /dev/null
+++ b/core/store/dist/src/test/java/org/onlab/onos/store/device/impl/GossipDeviceStoreTest.java
@@ -0,0 +1,619 @@
+package org.onlab.onos.store.device.impl;
+
+import static org.junit.Assert.*;
+import static org.onlab.onos.net.Device.Type.SWITCH;
+import static org.onlab.onos.net.DeviceId.deviceId;
+import static org.onlab.onos.net.device.DeviceEvent.Type.*;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.onlab.onos.cluster.ClusterEventListener;
+import org.onlab.onos.cluster.ClusterService;
+import org.onlab.onos.cluster.ControllerNode;
+import org.onlab.onos.cluster.ControllerNode.State;
+import org.onlab.onos.cluster.DefaultControllerNode;
+import org.onlab.onos.cluster.MastershipTerm;
+import org.onlab.onos.cluster.NodeId;
+import org.onlab.onos.net.Annotations;
+import org.onlab.onos.net.DefaultAnnotations;
+import org.onlab.onos.net.Device;
+import org.onlab.onos.net.DeviceId;
+import org.onlab.onos.net.Port;
+import org.onlab.onos.net.PortNumber;
+import org.onlab.onos.net.SparseAnnotations;
+import org.onlab.onos.net.device.DefaultDeviceDescription;
+import org.onlab.onos.net.device.DefaultPortDescription;
+import org.onlab.onos.net.device.DeviceDescription;
+import org.onlab.onos.net.device.DeviceEvent;
+import org.onlab.onos.net.device.DeviceStore;
+import org.onlab.onos.net.device.DeviceStoreDelegate;
+import org.onlab.onos.net.device.PortDescription;
+import org.onlab.onos.net.provider.ProviderId;
+import org.onlab.onos.store.ClockService;
+import org.onlab.onos.store.cluster.messaging.ClusterCommunicationService;
+import org.onlab.onos.store.cluster.messaging.ClusterMessage;
+import org.onlab.onos.store.cluster.messaging.ClusterMessageHandler;
+import org.onlab.onos.store.cluster.messaging.MessageSubject;
+import org.onlab.packet.IpPrefix;
+
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Sets;
+
+
+// TODO add tests for remote replication
+/**
+ * Test of the gossip based distributed DeviceStore implementation.
+ */
+public class GossipDeviceStoreTest {
+
+ private static final ProviderId PID = new ProviderId("of", "foo");
+ private static final ProviderId PIDA = new ProviderId("of", "bar", true);
+ private static final DeviceId DID1 = deviceId("of:foo");
+ private static final DeviceId DID2 = deviceId("of:bar");
+ private static final String MFR = "whitebox";
+ private static final String HW = "1.1.x";
+ private static final String SW1 = "3.8.1";
+ private static final String SW2 = "3.9.5";
+ private static final String SN = "43311-12345";
+
+ private static final PortNumber P1 = PortNumber.portNumber(1);
+ private static final PortNumber P2 = PortNumber.portNumber(2);
+ private static final PortNumber P3 = PortNumber.portNumber(3);
+
+ private static final SparseAnnotations A1 = DefaultAnnotations.builder()
+ .set("A1", "a1")
+ .set("B1", "b1")
+ .build();
+ private static final SparseAnnotations A1_2 = DefaultAnnotations.builder()
+ .remove("A1")
+ .set("B3", "b3")
+ .build();
+ private static final SparseAnnotations A2 = DefaultAnnotations.builder()
+ .set("A2", "a2")
+ .set("B2", "b2")
+ .build();
+ private static final SparseAnnotations A2_2 = DefaultAnnotations.builder()
+ .remove("A2")
+ .set("B4", "b4")
+ .build();
+
+ private static final NodeId MYSELF = new NodeId("myself");
+
+ private GossipDeviceStore gossipDeviceStore;
+ private DeviceStore deviceStore;
+
+ private DeviceClockManager deviceClockManager;
+ private ClockService clockService;
+
+ @BeforeClass
+ public static void setUpBeforeClass() throws Exception {
+ }
+
+ @AfterClass
+ public static void tearDownAfterClass() throws Exception {
+ }
+
+
+ @Before
+ public void setUp() throws Exception {
+ deviceClockManager = new DeviceClockManager();
+ deviceClockManager.activate();
+ clockService = deviceClockManager;
+
+ deviceClockManager.setMastershipTerm(DID1, MastershipTerm.of(MYSELF, 1));
+ deviceClockManager.setMastershipTerm(DID2, MastershipTerm.of(MYSELF, 2));
+
+ ClusterCommunicationService clusterCommunicator = new TestClusterCommunicationService();
+ ClusterService clusterService = new TestClusterService();
+
+ gossipDeviceStore = new TestGossipDeviceStore(clockService, clusterService, clusterCommunicator);
+ gossipDeviceStore.activate();
+ deviceStore = gossipDeviceStore;
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ gossipDeviceStore.deactivate();
+ deviceClockManager.deactivate();
+ }
+
+ private void putDevice(DeviceId deviceId, String swVersion,
+ SparseAnnotations... annotations) {
+ DeviceDescription description =
+ new DefaultDeviceDescription(deviceId.uri(), SWITCH, MFR,
+ HW, swVersion, SN, annotations);
+ deviceStore.createOrUpdateDevice(PID, deviceId, description);
+ }
+
+ private void putDeviceAncillary(DeviceId deviceId, String swVersion,
+ SparseAnnotations... annotations) {
+ DeviceDescription description =
+ new DefaultDeviceDescription(deviceId.uri(), SWITCH, MFR,
+ HW, swVersion, SN, annotations);
+ deviceStore.createOrUpdateDevice(PIDA, deviceId, description);
+ }
+
+ private static void assertDevice(DeviceId id, String swVersion, Device device) {
+ assertNotNull(device);
+ assertEquals(id, device.id());
+ assertEquals(MFR, device.manufacturer());
+ assertEquals(HW, device.hwVersion());
+ assertEquals(swVersion, device.swVersion());
+ assertEquals(SN, device.serialNumber());
+ }
+
+ /**
+ * Verifies that Annotations created by merging {@code annotations} is
+ * equal to actual Annotations.
+ *
+ * @param actual Annotations to check
+ * @param annotations
+ */
+ private static void assertAnnotationsEquals(Annotations actual, SparseAnnotations... annotations) {
+ DefaultAnnotations expected = DefaultAnnotations.builder().build();
+ for (SparseAnnotations a : annotations) {
+ expected = DefaultAnnotations.merge(expected, a);
+ }
+ assertEquals(expected.keys(), actual.keys());
+ for (String key : expected.keys()) {
+ assertEquals(expected.value(key), actual.value(key));
+ }
+ }
+
+ @Test
+ public final void testGetDeviceCount() {
+ assertEquals("initialy empty", 0, deviceStore.getDeviceCount());
+
+ putDevice(DID1, SW1);
+ putDevice(DID2, SW2);
+ putDevice(DID1, SW1);
+
+ assertEquals("expect 2 uniq devices", 2, deviceStore.getDeviceCount());
+ }
+
+ @Test
+ public final void testGetDevices() {
+ assertEquals("initialy empty", 0, Iterables.size(deviceStore.getDevices()));
+
+ putDevice(DID1, SW1);
+ putDevice(DID2, SW2);
+ putDevice(DID1, SW1);
+
+ assertEquals("expect 2 uniq devices",
+ 2, Iterables.size(deviceStore.getDevices()));
+
+ Map<DeviceId, Device> devices = new HashMap<>();
+ for (Device device : deviceStore.getDevices()) {
+ devices.put(device.id(), device);
+ }
+
+ assertDevice(DID1, SW1, devices.get(DID1));
+ assertDevice(DID2, SW2, devices.get(DID2));
+
+ // add case for new node?
+ }
+
+ @Test
+ public final void testGetDevice() {
+
+ putDevice(DID1, SW1);
+
+ assertDevice(DID1, SW1, deviceStore.getDevice(DID1));
+ assertNull("DID2 shouldn't be there", deviceStore.getDevice(DID2));
+ }
+
+ @Test
+ public final void testCreateOrUpdateDevice() {
+ DeviceDescription description =
+ new DefaultDeviceDescription(DID1.uri(), SWITCH, MFR,
+ HW, SW1, SN);
+ DeviceEvent event = deviceStore.createOrUpdateDevice(PID, DID1, description);
+ assertEquals(DEVICE_ADDED, event.type());
+ assertDevice(DID1, SW1, event.subject());
+
+ DeviceDescription description2 =
+ new DefaultDeviceDescription(DID1.uri(), SWITCH, MFR,
+ HW, SW2, SN);
+ DeviceEvent event2 = deviceStore.createOrUpdateDevice(PID, DID1, description2);
+ assertEquals(DEVICE_UPDATED, event2.type());
+ assertDevice(DID1, SW2, event2.subject());
+
+ assertNull("No change expected", deviceStore.createOrUpdateDevice(PID, DID1, description2));
+ }
+
+ @Test
+ public final void testCreateOrUpdateDeviceAncillary() {
+ DeviceDescription description =
+ new DefaultDeviceDescription(DID1.uri(), SWITCH, MFR,
+ HW, SW1, SN, A2);
+ DeviceEvent event = deviceStore.createOrUpdateDevice(PIDA, DID1, description);
+ assertEquals(DEVICE_ADDED, event.type());
+ assertDevice(DID1, SW1, event.subject());
+ assertEquals(PIDA, event.subject().providerId());
+ assertAnnotationsEquals(event.subject().annotations(), A2);
+ assertFalse("Ancillary will not bring device up", deviceStore.isAvailable(DID1));
+
+ DeviceDescription description2 =
+ new DefaultDeviceDescription(DID1.uri(), SWITCH, MFR,
+ HW, SW2, SN, A1);
+ DeviceEvent event2 = deviceStore.createOrUpdateDevice(PID, DID1, description2);
+ assertEquals(DEVICE_UPDATED, event2.type());
+ assertDevice(DID1, SW2, event2.subject());
+ assertEquals(PID, event2.subject().providerId());
+ assertAnnotationsEquals(event2.subject().annotations(), A1, A2);
+ assertTrue(deviceStore.isAvailable(DID1));
+
+ assertNull("No change expected", deviceStore.createOrUpdateDevice(PID, DID1, description2));
+
+ // For now, Ancillary is ignored once primary appears
+ assertNull("No change expected", deviceStore.createOrUpdateDevice(PIDA, DID1, description));
+
+ // But, Ancillary annotations will be in effect
+ DeviceDescription description3 =
+ new DefaultDeviceDescription(DID1.uri(), SWITCH, MFR,
+ HW, SW1, SN, A2_2);
+ DeviceEvent event3 = deviceStore.createOrUpdateDevice(PIDA, DID1, description3);
+ assertEquals(DEVICE_UPDATED, event3.type());
+ // basic information will be the one from Primary
+ assertDevice(DID1, SW2, event3.subject());
+ assertEquals(PID, event3.subject().providerId());
+ // but annotation from Ancillary will be merged
+ assertAnnotationsEquals(event3.subject().annotations(), A1, A2, A2_2);
+ assertTrue(deviceStore.isAvailable(DID1));
+ }
+
+
+ @Test
+ public final void testMarkOffline() {
+
+ putDevice(DID1, SW1);
+ assertTrue(deviceStore.isAvailable(DID1));
+
+ DeviceEvent event = deviceStore.markOffline(DID1);
+ assertEquals(DEVICE_AVAILABILITY_CHANGED, event.type());
+ assertDevice(DID1, SW1, event.subject());
+ assertFalse(deviceStore.isAvailable(DID1));
+
+ DeviceEvent event2 = deviceStore.markOffline(DID1);
+ assertNull("No change, no event", event2);
+}
+
+ @Test
+ public final void testUpdatePorts() {
+ putDevice(DID1, SW1);
+ List<PortDescription> pds = Arrays.<PortDescription>asList(
+ new DefaultPortDescription(P1, true),
+ new DefaultPortDescription(P2, true)
+ );
+
+ List<DeviceEvent> events = deviceStore.updatePorts(PID, DID1, pds);
+
+ Set<PortNumber> expectedPorts = Sets.newHashSet(P1, P2);
+ for (DeviceEvent event : events) {
+ assertEquals(PORT_ADDED, event.type());
+ assertDevice(DID1, SW1, event.subject());
+ assertTrue("PortNumber is one of expected",
+ expectedPorts.remove(event.port().number()));
+ assertTrue("Port is enabled", event.port().isEnabled());
+ }
+ assertTrue("Event for all expectedport appeared", expectedPorts.isEmpty());
+
+
+ List<PortDescription> pds2 = Arrays.<PortDescription>asList(
+ new DefaultPortDescription(P1, false),
+ new DefaultPortDescription(P2, true),
+ new DefaultPortDescription(P3, true)
+ );
+
+ events = deviceStore.updatePorts(PID, DID1, pds2);
+ assertFalse("event should be triggered", events.isEmpty());
+ for (DeviceEvent event : events) {
+ PortNumber num = event.port().number();
+ if (P1.equals(num)) {
+ assertEquals(PORT_UPDATED, event.type());
+ assertDevice(DID1, SW1, event.subject());
+ assertFalse("Port is disabled", event.port().isEnabled());
+ } else if (P2.equals(num)) {
+ fail("P2 event not expected.");
+ } else if (P3.equals(num)) {
+ assertEquals(PORT_ADDED, event.type());
+ assertDevice(DID1, SW1, event.subject());
+ assertTrue("Port is enabled", event.port().isEnabled());
+ } else {
+ fail("Unknown port number encountered: " + num);
+ }
+ }
+
+ List<PortDescription> pds3 = Arrays.<PortDescription>asList(
+ new DefaultPortDescription(P1, false),
+ new DefaultPortDescription(P2, true)
+ );
+ events = deviceStore.updatePorts(PID, DID1, pds3);
+ assertFalse("event should be triggered", events.isEmpty());
+ for (DeviceEvent event : events) {
+ PortNumber num = event.port().number();
+ if (P1.equals(num)) {
+ fail("P1 event not expected.");
+ } else if (P2.equals(num)) {
+ fail("P2 event not expected.");
+ } else if (P3.equals(num)) {
+ assertEquals(PORT_REMOVED, event.type());
+ assertDevice(DID1, SW1, event.subject());
+ assertTrue("Port was enabled", event.port().isEnabled());
+ } else {
+ fail("Unknown port number encountered: " + num);
+ }
+ }
+
+ }
+
+ @Test
+ public final void testUpdatePortStatus() {
+ putDevice(DID1, SW1);
+ List<PortDescription> pds = Arrays.<PortDescription>asList(
+ new DefaultPortDescription(P1, true)
+ );
+ deviceStore.updatePorts(PID, DID1, pds);
+
+ DeviceEvent event = deviceStore.updatePortStatus(PID, DID1,
+ new DefaultPortDescription(P1, false));
+ assertEquals(PORT_UPDATED, event.type());
+ assertDevice(DID1, SW1, event.subject());
+ assertEquals(P1, event.port().number());
+ assertFalse("Port is disabled", event.port().isEnabled());
+
+ }
+ @Test
+ public final void testUpdatePortStatusAncillary() {
+ putDeviceAncillary(DID1, SW1);
+ putDevice(DID1, SW1);
+ List<PortDescription> pds = Arrays.<PortDescription>asList(
+ new DefaultPortDescription(P1, true, A1)
+ );
+ deviceStore.updatePorts(PID, DID1, pds);
+
+ DeviceEvent event = deviceStore.updatePortStatus(PID, DID1,
+ new DefaultPortDescription(P1, false, A1_2));
+ assertEquals(PORT_UPDATED, event.type());
+ assertDevice(DID1, SW1, event.subject());
+ assertEquals(P1, event.port().number());
+ assertAnnotationsEquals(event.port().annotations(), A1, A1_2);
+ assertFalse("Port is disabled", event.port().isEnabled());
+
+ DeviceEvent event2 = deviceStore.updatePortStatus(PIDA, DID1,
+ new DefaultPortDescription(P1, true));
+ assertNull("Ancillary is ignored if primary exists", event2);
+
+ // but, Ancillary annotation update will be notified
+ DeviceEvent event3 = deviceStore.updatePortStatus(PIDA, DID1,
+ new DefaultPortDescription(P1, true, A2));
+ assertEquals(PORT_UPDATED, event3.type());
+ assertDevice(DID1, SW1, event3.subject());
+ assertEquals(P1, event3.port().number());
+ assertAnnotationsEquals(event3.port().annotations(), A1, A1_2, A2);
+ assertFalse("Port is disabled", event3.port().isEnabled());
+
+ // port only reported from Ancillary will be notified as down
+ DeviceEvent event4 = deviceStore.updatePortStatus(PIDA, DID1,
+ new DefaultPortDescription(P2, true));
+ assertEquals(PORT_ADDED, event4.type());
+ assertDevice(DID1, SW1, event4.subject());
+ assertEquals(P2, event4.port().number());
+ assertAnnotationsEquals(event4.port().annotations());
+ assertFalse("Port is disabled if not given from primary provider",
+ event4.port().isEnabled());
+ }
+
+ @Test
+ public final void testGetPorts() {
+ putDevice(DID1, SW1);
+ putDevice(DID2, SW1);
+ List<PortDescription> pds = Arrays.<PortDescription>asList(
+ new DefaultPortDescription(P1, true),
+ new DefaultPortDescription(P2, true)
+ );
+ deviceStore.updatePorts(PID, DID1, pds);
+
+ Set<PortNumber> expectedPorts = Sets.newHashSet(P1, P2);
+ List<Port> ports = deviceStore.getPorts(DID1);
+ for (Port port : ports) {
+ assertTrue("Port is enabled", port.isEnabled());
+ assertTrue("PortNumber is one of expected",
+ expectedPorts.remove(port.number()));
+ }
+ assertTrue("Event for all expectedport appeared", expectedPorts.isEmpty());
+
+
+ assertTrue("DID2 has no ports", deviceStore.getPorts(DID2).isEmpty());
+ }
+
+ @Test
+ public final void testGetPort() {
+ putDevice(DID1, SW1);
+ putDevice(DID2, SW1);
+ List<PortDescription> pds = Arrays.<PortDescription>asList(
+ new DefaultPortDescription(P1, true),
+ new DefaultPortDescription(P2, false)
+ );
+ deviceStore.updatePorts(PID, DID1, pds);
+
+ Port port1 = deviceStore.getPort(DID1, P1);
+ assertEquals(P1, port1.number());
+ assertTrue("Port is enabled", port1.isEnabled());
+
+ Port port2 = deviceStore.getPort(DID1, P2);
+ assertEquals(P2, port2.number());
+ assertFalse("Port is disabled", port2.isEnabled());
+
+ Port port3 = deviceStore.getPort(DID1, P3);
+ assertNull("P3 not expected", port3);
+ }
+
+ @Test
+ public final void testRemoveDevice() {
+ putDevice(DID1, SW1, A1);
+ List<PortDescription> pds = Arrays.<PortDescription>asList(
+ new DefaultPortDescription(P1, true, A2)
+ );
+ deviceStore.updatePorts(PID, DID1, pds);
+ putDevice(DID2, SW1);
+
+ assertEquals(2, deviceStore.getDeviceCount());
+ assertEquals(1, deviceStore.getPorts(DID1).size());
+ assertAnnotationsEquals(deviceStore.getDevice(DID1).annotations(), A1);
+ assertAnnotationsEquals(deviceStore.getPort(DID1, P1).annotations(), A2);
+
+ DeviceEvent event = deviceStore.removeDevice(DID1);
+ assertEquals(DEVICE_REMOVED, event.type());
+ assertDevice(DID1, SW1, event.subject());
+
+ assertEquals(1, deviceStore.getDeviceCount());
+ assertEquals(0, deviceStore.getPorts(DID1).size());
+
+ // putBack Device, Port w/o annotation
+ putDevice(DID1, SW1);
+ List<PortDescription> pds2 = Arrays.<PortDescription>asList(
+ new DefaultPortDescription(P1, true)
+ );
+ deviceStore.updatePorts(PID, DID1, pds2);
+
+ // annotations should not survive
+ assertEquals(2, deviceStore.getDeviceCount());
+ assertEquals(1, deviceStore.getPorts(DID1).size());
+ assertAnnotationsEquals(deviceStore.getDevice(DID1).annotations());
+ assertAnnotationsEquals(deviceStore.getPort(DID1, P1).annotations());
+ }
+
+ // If Delegates should be called only on remote events,
+ // then Simple* should never call them, thus not test required.
+ // TODO add test for Port events when we have them
+ @Ignore("Ignore until Delegate spec. is clear.")
+ @Test
+ public final void testEvents() throws InterruptedException {
+ final CountDownLatch addLatch = new CountDownLatch(1);
+ DeviceStoreDelegate checkAdd = new DeviceStoreDelegate() {
+ @Override
+ public void notify(DeviceEvent event) {
+ assertEquals(DEVICE_ADDED, event.type());
+ assertDevice(DID1, SW1, event.subject());
+ addLatch.countDown();
+ }
+ };
+ final CountDownLatch updateLatch = new CountDownLatch(1);
+ DeviceStoreDelegate checkUpdate = new DeviceStoreDelegate() {
+ @Override
+ public void notify(DeviceEvent event) {
+ assertEquals(DEVICE_UPDATED, event.type());
+ assertDevice(DID1, SW2, event.subject());
+ updateLatch.countDown();
+ }
+ };
+ final CountDownLatch removeLatch = new CountDownLatch(1);
+ DeviceStoreDelegate checkRemove = new DeviceStoreDelegate() {
+ @Override
+ public void notify(DeviceEvent event) {
+ assertEquals(DEVICE_REMOVED, event.type());
+ assertDevice(DID1, SW2, event.subject());
+ removeLatch.countDown();
+ }
+ };
+
+ DeviceDescription description =
+ new DefaultDeviceDescription(DID1.uri(), SWITCH, MFR,
+ HW, SW1, SN);
+ deviceStore.setDelegate(checkAdd);
+ deviceStore.createOrUpdateDevice(PID, DID1, description);
+ assertTrue("Add event fired", addLatch.await(1, TimeUnit.SECONDS));
+
+
+ DeviceDescription description2 =
+ new DefaultDeviceDescription(DID1.uri(), SWITCH, MFR,
+ HW, SW2, SN);
+ deviceStore.unsetDelegate(checkAdd);
+ deviceStore.setDelegate(checkUpdate);
+ deviceStore.createOrUpdateDevice(PID, DID1, description2);
+ assertTrue("Update event fired", updateLatch.await(1, TimeUnit.SECONDS));
+
+ deviceStore.unsetDelegate(checkUpdate);
+ deviceStore.setDelegate(checkRemove);
+ deviceStore.removeDevice(DID1);
+ assertTrue("Remove event fired", removeLatch.await(1, TimeUnit.SECONDS));
+ }
+
+ private static final class TestGossipDeviceStore extends GossipDeviceStore {
+
+ public TestGossipDeviceStore(
+ ClockService clockService,
+ ClusterService clusterService,
+ ClusterCommunicationService clusterCommunicator) {
+ this.clockService = clockService;
+ this.clusterService = clusterService;
+ this.clusterCommunicator = clusterCommunicator;
+ }
+ }
+
+ private static final class TestClusterCommunicationService implements ClusterCommunicationService {
+ @Override
+ public boolean broadcast(ClusterMessage message) throws IOException { return true; }
+ @Override
+ public boolean unicast(ClusterMessage message, NodeId nodeId) throws IOException { return true; }
+ @Override
+ public boolean multicast(ClusterMessage message, Set<NodeId> nodeIds) throws IOException { return true; }
+ @Override
+ public void addSubscriber(MessageSubject subject, ClusterMessageHandler subscriber) {}
+ }
+
+ private static final class TestClusterService implements ClusterService {
+
+ private static final ControllerNode ONOS1 =
+ new DefaultControllerNode(new NodeId("N1"), IpPrefix.valueOf("127.0.0.1"));
+ private final Map<NodeId, ControllerNode> nodes = new HashMap<>();
+ private final Map<NodeId, ControllerNode.State> nodeStates = new HashMap<>();
+
+ public TestClusterService() {
+ nodes.put(new NodeId("N1"), ONOS1);
+ nodeStates.put(new NodeId("N1"), ControllerNode.State.ACTIVE);
+ }
+
+ @Override
+ public ControllerNode getLocalNode() {
+ return ONOS1;
+ }
+
+ @Override
+ public Set<ControllerNode> getNodes() {
+ return Sets.newHashSet(nodes.values());
+ }
+
+ @Override
+ public ControllerNode getNode(NodeId nodeId) {
+ return nodes.get(nodeId);
+ }
+
+ @Override
+ public State getState(NodeId nodeId) {
+ return nodeStates.get(nodeId);
+ }
+
+ @Override
+ public void addListener(ClusterEventListener listener) {
+ }
+
+ @Override
+ public void removeListener(ClusterEventListener listener) {
+ }
+ }
+}
diff --git a/core/store/hz/cluster/src/main/java/org/onlab/onos/store/cluster/impl/DistributedClusterStore.java b/core/store/hz/cluster/src/main/java/org/onlab/onos/store/cluster/impl/DistributedClusterStore.java
index f83ac59..61a7374 100644
--- a/core/store/hz/cluster/src/main/java/org/onlab/onos/store/cluster/impl/DistributedClusterStore.java
+++ b/core/store/hz/cluster/src/main/java/org/onlab/onos/store/cluster/impl/DistributedClusterStore.java
@@ -57,7 +57,7 @@
rawNodes = theInstance.getMap("nodes");
OptionalCacheLoader<NodeId, DefaultControllerNode> nodeLoader
- = new OptionalCacheLoader<>(kryoSerializationService, rawNodes);
+ = new OptionalCacheLoader<>(serializer, rawNodes);
nodes = new AbsentInvalidatingLoadingCache<>(newBuilder().build(nodeLoader));
rawNodes.addEntryListener(new RemoteCacheEventHandler<>(nodes), true);
diff --git a/core/store/hz/cluster/src/test/java/org/onlab/onos/store/cluster/impl/DistributedMastershipStoreTest.java b/core/store/hz/cluster/src/test/java/org/onlab/onos/store/cluster/impl/DistributedMastershipStoreTest.java
index 9178e90..bf1bb38 100644
--- a/core/store/hz/cluster/src/test/java/org/onlab/onos/store/cluster/impl/DistributedMastershipStoreTest.java
+++ b/core/store/hz/cluster/src/test/java/org/onlab/onos/store/cluster/impl/DistributedMastershipStoreTest.java
@@ -30,8 +30,7 @@
import org.onlab.onos.store.common.StoreManager;
import org.onlab.onos.store.common.StoreService;
import org.onlab.onos.store.common.TestStoreManager;
-import org.onlab.onos.store.serializers.KryoSerializationManager;
-import org.onlab.onos.store.serializers.KryoSerializationService;
+import org.onlab.onos.store.serializers.KryoSerializer;
import org.onlab.packet.IpPrefix;
import com.google.common.collect.Sets;
@@ -57,7 +56,7 @@
private DistributedMastershipStore dms;
private TestDistributedMastershipStore testStore;
- private KryoSerializationManager serializationMgr;
+ private KryoSerializer serializationMgr;
private StoreManager storeMgr;
@BeforeClass
@@ -76,8 +75,7 @@
storeMgr = new TestStoreManager(Hazelcast.newHazelcastInstance(config));
storeMgr.activate();
- serializationMgr = new KryoSerializationManager();
- serializationMgr.activate();
+ serializationMgr = new KryoSerializer();
dms = new TestDistributedMastershipStore(storeMgr, serializationMgr);
dms.clusterService = new TestClusterService();
@@ -90,8 +88,6 @@
public void tearDown() throws Exception {
dms.deactivate();
- serializationMgr.deactivate();
-
storeMgr.deactivate();
}
@@ -234,9 +230,9 @@
private class TestDistributedMastershipStore extends
DistributedMastershipStore {
public TestDistributedMastershipStore(StoreService storeService,
- KryoSerializationService kryoSerializationService) {
+ KryoSerializer kryoSerialization) {
this.storeService = storeService;
- this.kryoSerializationService = kryoSerializationService;
+ this.serializer = kryoSerialization;
}
//helper to populate master/backup structures
@@ -260,6 +256,7 @@
}
}
+ //a dumb utility function.
public void dump() {
System.out.println("standbys");
for (Map.Entry<byte [], byte []> e : standbys.entrySet()) {
diff --git a/core/store/hz/common/src/main/java/org/onlab/onos/store/common/AbstractHazelcastStore.java b/core/store/hz/common/src/main/java/org/onlab/onos/store/common/AbstractHazelcastStore.java
index 0302105..a22dd89 100644
--- a/core/store/hz/common/src/main/java/org/onlab/onos/store/common/AbstractHazelcastStore.java
+++ b/core/store/hz/common/src/main/java/org/onlab/onos/store/common/AbstractHazelcastStore.java
@@ -15,7 +15,8 @@
import org.onlab.onos.event.Event;
import org.onlab.onos.store.AbstractStore;
import org.onlab.onos.store.StoreDelegate;
-import org.onlab.onos.store.serializers.KryoSerializationService;
+import org.onlab.onos.store.serializers.KryoSerializer;
+import org.onlab.onos.store.serializers.StoreSerializer;
import org.slf4j.Logger;
import static com.google.common.base.Preconditions.checkNotNull;
@@ -24,7 +25,7 @@
/**
* Abstraction of a distributed store based on Hazelcast.
*/
-@Component(componentAbstract = true)
+@Component
public abstract class AbstractHazelcastStore<E extends Event, D extends StoreDelegate<E>>
extends AbstractStore<E, D> {
@@ -33,13 +34,13 @@
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected StoreService storeService;
- @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
- protected KryoSerializationService kryoSerializationService;
+ protected StoreSerializer serializer;
protected HazelcastInstance theInstance;
@Activate
public void activate() {
+ serializer = new KryoSerializer();
theInstance = storeService.getHazelcastInstance();
}
@@ -50,7 +51,7 @@
* @return serialized object
*/
protected byte[] serialize(Object obj) {
- return kryoSerializationService.serialize(obj);
+ return serializer.encode(obj);
}
/**
@@ -61,7 +62,7 @@
* @return deserialized object
*/
protected <T> T deserialize(byte[] bytes) {
- return kryoSerializationService.deserialize(bytes);
+ return serializer.decode(bytes);
}
diff --git a/core/store/hz/common/src/main/java/org/onlab/onos/store/common/OptionalCacheLoader.java b/core/store/hz/common/src/main/java/org/onlab/onos/store/common/OptionalCacheLoader.java
index f96fdd8..d5fc380 100644
--- a/core/store/hz/common/src/main/java/org/onlab/onos/store/common/OptionalCacheLoader.java
+++ b/core/store/hz/common/src/main/java/org/onlab/onos/store/common/OptionalCacheLoader.java
@@ -2,7 +2,7 @@
import static com.google.common.base.Preconditions.checkNotNull;
-import org.onlab.onos.store.serializers.KryoSerializationService;
+import org.onlab.onos.store.serializers.StoreSerializer;
import com.google.common.base.Optional;
import com.google.common.cache.CacheLoader;
@@ -18,28 +18,28 @@
public final class OptionalCacheLoader<K, V> extends
CacheLoader<K, Optional<V>> {
- private final KryoSerializationService kryoSerializationService;
+ private final StoreSerializer serializer;
private IMap<byte[], byte[]> rawMap;
/**
* Constructor.
*
- * @param kryoSerializationService to use for serialization
+ * @param serializer to use for serialization
* @param rawMap underlying IMap
*/
- public OptionalCacheLoader(KryoSerializationService kryoSerializationService, IMap<byte[], byte[]> rawMap) {
- this.kryoSerializationService = checkNotNull(kryoSerializationService);
+ public OptionalCacheLoader(StoreSerializer serializer, IMap<byte[], byte[]> rawMap) {
+ this.serializer = checkNotNull(serializer);
this.rawMap = checkNotNull(rawMap);
}
@Override
public Optional<V> load(K key) throws Exception {
- byte[] keyBytes = kryoSerializationService.serialize(key);
+ byte[] keyBytes = serializer.encode(key);
byte[] valBytes = rawMap.get(keyBytes);
if (valBytes == null) {
return Optional.absent();
}
- V dev = kryoSerializationService.deserialize(valBytes);
+ V dev = serializer.decode(valBytes);
return Optional.of(dev);
}
}
diff --git a/core/store/hz/net/src/main/java/org/onlab/onos/store/device/impl/DistributedDeviceStore.java b/core/store/hz/net/src/main/java/org/onlab/onos/store/device/impl/DistributedDeviceStore.java
index 5feb1ba..0016939 100644
--- a/core/store/hz/net/src/main/java/org/onlab/onos/store/device/impl/DistributedDeviceStore.java
+++ b/core/store/hz/net/src/main/java/org/onlab/onos/store/device/impl/DistributedDeviceStore.java
@@ -47,6 +47,7 @@
import static org.onlab.onos.net.device.DeviceEvent.Type.*;
import static org.slf4j.LoggerFactory.getLogger;
+//TODO: Add support for multiple provider and annotations
/**
* Manages inventory of infrastructure devices using Hazelcast-backed map.
*/
@@ -87,7 +88,7 @@
// TODO decide on Map name scheme to avoid collision
rawDevices = theInstance.getMap("devices");
final OptionalCacheLoader<DeviceId, DefaultDevice> deviceLoader
- = new OptionalCacheLoader<>(kryoSerializationService, rawDevices);
+ = new OptionalCacheLoader<>(serializer, rawDevices);
devices = new AbsentInvalidatingLoadingCache<>(newBuilder().build(deviceLoader));
// refresh/populate cache based on notification from other instance
devicesListener = rawDevices.addEntryListener(new RemoteDeviceEventHandler(devices), includeValue);
@@ -97,7 +98,7 @@
rawDevicePorts = theInstance.getMap("devicePorts");
final OptionalCacheLoader<DeviceId, Map<PortNumber, Port>> devicePortLoader
- = new OptionalCacheLoader<>(kryoSerializationService, rawDevicePorts);
+ = new OptionalCacheLoader<>(serializer, rawDevicePorts);
devicePorts = new AbsentInvalidatingLoadingCache<>(newBuilder().build(devicePortLoader));
// refresh/populate cache based on notification from other instance
portsListener = rawDevicePorts.addEntryListener(new RemotePortEventHandler(devicePorts), includeValue);
diff --git a/core/store/hz/net/src/main/java/org/onlab/onos/store/device/impl/NoOpClockProviderService.java b/core/store/hz/net/src/main/java/org/onlab/onos/store/device/impl/NoOpClockProviderService.java
new file mode 100644
index 0000000..b68620a
--- /dev/null
+++ b/core/store/hz/net/src/main/java/org/onlab/onos/store/device/impl/NoOpClockProviderService.java
@@ -0,0 +1,20 @@
+package org.onlab.onos.store.device.impl;
+
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Service;
+import org.onlab.onos.cluster.MastershipTerm;
+import org.onlab.onos.net.DeviceId;
+import org.onlab.onos.store.ClockProviderService;
+
+// FIXME: Code clone in onos-core-trivial, onos-core-hz-net
+/**
+ * Dummy implementation of {@link ClockProviderService}.
+ */
+@Component(immediate = true)
+@Service
+public class NoOpClockProviderService implements ClockProviderService {
+
+ @Override
+ public void setMastershipTerm(DeviceId deviceId, MastershipTerm term) {
+ }
+}
diff --git a/core/store/hz/net/src/main/java/org/onlab/onos/store/device/impl/NoOpClockService.java b/core/store/hz/net/src/main/java/org/onlab/onos/store/device/impl/NoOpClockService.java
deleted file mode 100644
index 2c443e9..0000000
--- a/core/store/hz/net/src/main/java/org/onlab/onos/store/device/impl/NoOpClockService.java
+++ /dev/null
@@ -1,32 +0,0 @@
-package org.onlab.onos.store.device.impl;
-
-import org.apache.felix.scr.annotations.Component;
-import org.apache.felix.scr.annotations.Service;
-import org.onlab.onos.cluster.MastershipTerm;
-import org.onlab.onos.net.DeviceId;
-import org.onlab.onos.store.ClockService;
-import org.onlab.onos.store.Timestamp;
-
-// FIXME: Code clone in onos-core-trivial, onos-core-hz-net
-/**
- * Dummy implementation of {@link ClockService}.
- */
-@Component(immediate = true)
-@Service
-public class NoOpClockService implements ClockService {
-
- @Override
- public Timestamp getTimestamp(DeviceId deviceId) {
- return new Timestamp() {
-
- @Override
- public int compareTo(Timestamp o) {
- throw new IllegalStateException("Never expected to be used.");
- }
- };
- }
-
- @Override
- public void setMastershipTerm(DeviceId deviceId, MastershipTerm term) {
- }
-}
diff --git a/core/store/hz/net/src/main/java/org/onlab/onos/store/flow/impl/DistributedFlowRuleStore.java b/core/store/hz/net/src/main/java/org/onlab/onos/store/flow/impl/DistributedFlowRuleStore.java
index 6ec7c51..d49e00b 100644
--- a/core/store/hz/net/src/main/java/org/onlab/onos/store/flow/impl/DistributedFlowRuleStore.java
+++ b/core/store/hz/net/src/main/java/org/onlab/onos/store/flow/impl/DistributedFlowRuleStore.java
@@ -1,6 +1,5 @@
package org.onlab.onos.store.flow.impl;
-import static org.onlab.onos.net.flow.FlowRuleEvent.Type.RULE_ADDED;
import static org.onlab.onos.net.flow.FlowRuleEvent.Type.RULE_REMOVED;
import static org.slf4j.LoggerFactory.getLogger;
@@ -13,9 +12,10 @@
import org.apache.felix.scr.annotations.Service;
import org.onlab.onos.ApplicationId;
import org.onlab.onos.net.DeviceId;
-import org.onlab.onos.net.flow.DefaultFlowRule;
+import org.onlab.onos.net.flow.DefaultFlowEntry;
+import org.onlab.onos.net.flow.FlowEntry;
+import org.onlab.onos.net.flow.FlowEntry.FlowEntryState;
import org.onlab.onos.net.flow.FlowRule;
-import org.onlab.onos.net.flow.FlowRule.FlowRuleState;
import org.onlab.onos.net.flow.FlowRuleEvent;
import org.onlab.onos.net.flow.FlowRuleEvent.Type;
import org.onlab.onos.net.flow.FlowRuleStore;
@@ -28,20 +28,20 @@
import com.google.common.collect.Multimap;
/**
- * TEMPORARY: Manages inventory of flow rules using distributed store implementation.
+ * Manages inventory of flow rules using trivial in-memory implementation.
*/
-//FIXME: I LIE I AM NOT DISTRIBUTED
+//FIXME I LIE. I AIN'T DISTRIBUTED
@Component(immediate = true)
@Service
public class DistributedFlowRuleStore
-extends AbstractStore<FlowRuleEvent, FlowRuleStoreDelegate>
-implements FlowRuleStore {
+ extends AbstractStore<FlowRuleEvent, FlowRuleStoreDelegate>
+ implements FlowRuleStore {
private final Logger log = getLogger(getClass());
// store entries as a pile of rules, no info about device tables
- private final Multimap<DeviceId, FlowRule> flowEntries =
- ArrayListMultimap.<DeviceId, FlowRule>create();
+ private final Multimap<DeviceId, FlowEntry> flowEntries =
+ ArrayListMultimap.<DeviceId, FlowEntry>create();
private final Multimap<ApplicationId, FlowRule> flowEntriesById =
ArrayListMultimap.<ApplicationId, FlowRule>create();
@@ -58,8 +58,13 @@
@Override
- public synchronized FlowRule getFlowRule(FlowRule rule) {
- for (FlowRule f : flowEntries.get(rule.deviceId())) {
+ public int getFlowRuleCount() {
+ return flowEntries.size();
+ }
+
+ @Override
+ public synchronized FlowEntry getFlowEntry(FlowRule rule) {
+ for (FlowEntry f : flowEntries.get(rule.deviceId())) {
if (f.equals(rule)) {
return f;
}
@@ -68,8 +73,8 @@
}
@Override
- public synchronized Iterable<FlowRule> getFlowEntries(DeviceId deviceId) {
- Collection<FlowRule> rules = flowEntries.get(deviceId);
+ public synchronized Iterable<FlowEntry> getFlowEntries(DeviceId deviceId) {
+ Collection<FlowEntry> rules = flowEntries.get(deviceId);
if (rules == null) {
return Collections.emptyList();
}
@@ -77,7 +82,7 @@
}
@Override
- public synchronized Iterable<FlowRule> getFlowEntriesByAppId(ApplicationId appId) {
+ public synchronized Iterable<FlowRule> getFlowRulesByAppId(ApplicationId appId) {
Collection<FlowRule> rules = flowEntriesById.get(appId);
if (rules == null) {
return Collections.emptyList();
@@ -87,7 +92,7 @@
@Override
public synchronized void storeFlowRule(FlowRule rule) {
- FlowRule f = new DefaultFlowRule(rule, FlowRuleState.PENDING_ADD);
+ FlowEntry f = new DefaultFlowEntry(rule);
DeviceId did = f.deviceId();
if (!flowEntries.containsEntry(did, f)) {
flowEntries.put(did, f);
@@ -97,57 +102,41 @@
@Override
public synchronized void deleteFlowRule(FlowRule rule) {
- FlowRule f = new DefaultFlowRule(rule, FlowRuleState.PENDING_REMOVE);
- DeviceId did = f.deviceId();
-
- /*
- * find the rule and mark it for deletion.
- * Ultimately a flow removed will come remove it.
- */
-
- if (flowEntries.containsEntry(did, f)) {
- //synchronized (flowEntries) {
- flowEntries.remove(did, f);
- flowEntries.put(did, f);
- flowEntriesById.remove(rule.appId(), rule);
- //}
+ FlowEntry entry = getFlowEntry(rule);
+ if (entry == null) {
+ return;
}
+ entry.setState(FlowEntryState.PENDING_REMOVE);
}
@Override
- public synchronized FlowRuleEvent addOrUpdateFlowRule(FlowRule rule) {
+ public synchronized FlowRuleEvent addOrUpdateFlowRule(FlowEntry rule) {
DeviceId did = rule.deviceId();
// check if this new rule is an update to an existing entry
- if (flowEntries.containsEntry(did, rule)) {
- //synchronized (flowEntries) {
- // Multimaps support duplicates so we have to remove our rule
- // and replace it with the current version.
- flowEntries.remove(did, rule);
- flowEntries.put(did, rule);
- //}
+ FlowEntry stored = getFlowEntry(rule);
+ if (stored != null) {
+ stored.setBytes(rule.bytes());
+ stored.setLife(rule.life());
+ stored.setPackets(rule.packets());
+ if (stored.state() == FlowEntryState.PENDING_ADD) {
+ stored.setState(FlowEntryState.ADDED);
+ return new FlowRuleEvent(Type.RULE_ADDED, rule);
+ }
return new FlowRuleEvent(Type.RULE_UPDATED, rule);
}
flowEntries.put(did, rule);
- return new FlowRuleEvent(RULE_ADDED, rule);
+ return null;
}
@Override
- public synchronized FlowRuleEvent removeFlowRule(FlowRule rule) {
- //synchronized (this) {
+ public synchronized FlowRuleEvent removeFlowRule(FlowEntry rule) {
+ // This is where one could mark a rule as removed and still keep it in the store.
if (flowEntries.remove(rule.deviceId(), rule)) {
return new FlowRuleEvent(RULE_REMOVED, rule);
} else {
return null;
}
- //}
}
-
-
-
-
-
-
-
}
diff --git a/core/store/hz/net/src/main/java/org/onlab/onos/store/link/impl/DistributedLinkStore.java b/core/store/hz/net/src/main/java/org/onlab/onos/store/link/impl/DistributedLinkStore.java
index 5161f2f..3dd42a3 100644
--- a/core/store/hz/net/src/main/java/org/onlab/onos/store/link/impl/DistributedLinkStore.java
+++ b/core/store/hz/net/src/main/java/org/onlab/onos/store/link/impl/DistributedLinkStore.java
@@ -38,6 +38,7 @@
import com.google.common.collect.ImmutableSet.Builder;
import com.hazelcast.core.IMap;
+//TODO: Add support for multiple provider and annotations
/**
* Manages inventory of infrastructure links using Hazelcast-backed map.
*/
@@ -70,7 +71,7 @@
// TODO decide on Map name scheme to avoid collision
rawLinks = theInstance.getMap("links");
final OptionalCacheLoader<LinkKey, DefaultLink> linkLoader
- = new OptionalCacheLoader<>(kryoSerializationService, rawLinks);
+ = new OptionalCacheLoader<>(serializer, rawLinks);
links = new AbsentInvalidatingLoadingCache<>(newBuilder().build(linkLoader));
// refresh/populate cache based on notification from other instance
linksListener = rawLinks.addEntryListener(new RemoteLinkEventHandler(links), includeValue);
diff --git a/core/store/hz/net/src/main/java/org/onlab/onos/store/topology/impl/DistributedTopologyStore.java b/core/store/hz/net/src/main/java/org/onlab/onos/store/topology/impl/DistributedTopologyStore.java
index 4728850..04f5fce 100644
--- a/core/store/hz/net/src/main/java/org/onlab/onos/store/topology/impl/DistributedTopologyStore.java
+++ b/core/store/hz/net/src/main/java/org/onlab/onos/store/topology/impl/DistributedTopologyStore.java
@@ -125,7 +125,8 @@
// Promote the new topology to current and return a ready-to-send event.
synchronized (this) {
current = newTopology;
- return new TopologyEvent(TopologyEvent.Type.TOPOLOGY_CHANGED, current);
+ return new TopologyEvent(TopologyEvent.Type.TOPOLOGY_CHANGED,
+ current, reasons);
}
}
diff --git a/core/store/hz/net/src/test/java/org/onlab/onos/store/device/impl/DistributedDeviceStoreTest.java b/core/store/hz/net/src/test/java/org/onlab/onos/store/device/impl/DistributedDeviceStoreTest.java
index 80c9464..7e2924b 100644
--- a/core/store/hz/net/src/test/java/org/onlab/onos/store/device/impl/DistributedDeviceStoreTest.java
+++ b/core/store/hz/net/src/test/java/org/onlab/onos/store/device/impl/DistributedDeviceStoreTest.java
@@ -36,9 +36,6 @@
import org.onlab.onos.store.common.StoreManager;
import org.onlab.onos.store.common.StoreService;
import org.onlab.onos.store.common.TestStoreManager;
-import org.onlab.onos.store.serializers.KryoSerializationManager;
-import org.onlab.onos.store.serializers.KryoSerializationService;
-
import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;
import com.hazelcast.config.Config;
@@ -63,7 +60,6 @@
private static final PortNumber P3 = PortNumber.portNumber(3);
private DistributedDeviceStore deviceStore;
- private KryoSerializationManager serializationMgr;
private StoreManager storeManager;
@@ -85,10 +81,7 @@
storeManager = new TestStoreManager(Hazelcast.newHazelcastInstance(config));
storeManager.activate();
- serializationMgr = new KryoSerializationManager();
- serializationMgr.activate();
-
- deviceStore = new TestDistributedDeviceStore(storeManager, serializationMgr);
+ deviceStore = new TestDistributedDeviceStore(storeManager);
deviceStore.activate();
}
@@ -96,8 +89,6 @@
public void tearDown() throws Exception {
deviceStore.deactivate();
- serializationMgr.deactivate();
-
storeManager.deactivate();
}
@@ -392,10 +383,8 @@
}
private class TestDistributedDeviceStore extends DistributedDeviceStore {
- public TestDistributedDeviceStore(StoreService storeService,
- KryoSerializationService kryoSerializationService) {
+ public TestDistributedDeviceStore(StoreService storeService) {
this.storeService = storeService;
- this.kryoSerializationService = kryoSerializationService;
}
}
}
diff --git a/core/store/hz/net/src/test/java/org/onlab/onos/store/link/impl/DistributedLinkStoreTest.java b/core/store/hz/net/src/test/java/org/onlab/onos/store/link/impl/DistributedLinkStoreTest.java
index a76e901..dd959b5 100644
--- a/core/store/hz/net/src/test/java/org/onlab/onos/store/link/impl/DistributedLinkStoreTest.java
+++ b/core/store/hz/net/src/test/java/org/onlab/onos/store/link/impl/DistributedLinkStoreTest.java
@@ -30,9 +30,6 @@
import org.onlab.onos.store.common.StoreManager;
import org.onlab.onos.store.common.StoreService;
import org.onlab.onos.store.common.TestStoreManager;
-import org.onlab.onos.store.serializers.KryoSerializationManager;
-import org.onlab.onos.store.serializers.KryoSerializationService;
-
import com.google.common.collect.Iterables;
import com.hazelcast.config.Config;
import com.hazelcast.core.Hazelcast;
@@ -51,7 +48,6 @@
private static final PortNumber P3 = PortNumber.portNumber(3);
private StoreManager storeManager;
- private KryoSerializationManager serializationMgr;
private DistributedLinkStore linkStore;
@@ -71,17 +67,13 @@
storeManager = new TestStoreManager(Hazelcast.newHazelcastInstance(config));
storeManager.activate();
- serializationMgr = new KryoSerializationManager();
- serializationMgr.activate();
-
- linkStore = new TestDistributedLinkStore(storeManager, serializationMgr);
+ linkStore = new TestDistributedLinkStore(storeManager);
linkStore.activate();
}
@After
public void tearDown() throws Exception {
linkStore.deactivate();
- serializationMgr.deactivate();
storeManager.deactivate();
}
@@ -361,10 +353,8 @@
class TestDistributedLinkStore extends DistributedLinkStore {
- TestDistributedLinkStore(StoreService storeService,
- KryoSerializationService kryoSerializationService) {
+ TestDistributedLinkStore(StoreService storeService) {
this.storeService = storeService;
- this.kryoSerializationService = kryoSerializationService;
}
}
}
diff --git a/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/ConnectPointSerializer.java b/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/ConnectPointSerializer.java
index 46badcb..14a64d2 100644
--- a/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/ConnectPointSerializer.java
+++ b/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/ConnectPointSerializer.java
@@ -3,7 +3,6 @@
import org.onlab.onos.net.ConnectPoint;
import org.onlab.onos.net.ElementId;
import org.onlab.onos.net.PortNumber;
-
import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.Serializer;
import com.esotericsoftware.kryo.io.Input;
@@ -15,7 +14,7 @@
public class ConnectPointSerializer extends Serializer<ConnectPoint> {
/**
- * Default constructor.
+ * Creates {@link ConnectPointSerializer} serializer instance.
*/
public ConnectPointSerializer() {
// non-null, immutable
diff --git a/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/DefaultLinkSerializer.java b/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/DefaultLinkSerializer.java
index 5ee273d..06d01b5 100644
--- a/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/DefaultLinkSerializer.java
+++ b/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/DefaultLinkSerializer.java
@@ -16,7 +16,7 @@
public class DefaultLinkSerializer extends Serializer<DefaultLink> {
/**
- * Default constructor.
+ * Creates {@link DefaultLink} serializer instance.
*/
public DefaultLinkSerializer() {
// non-null, immutable
diff --git a/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/DefaultPortSerializer.java b/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/DefaultPortSerializer.java
index 8455e80..5dc310b 100644
--- a/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/DefaultPortSerializer.java
+++ b/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/DefaultPortSerializer.java
@@ -16,7 +16,7 @@
Serializer<DefaultPort> {
/**
- * Default constructor.
+ * Creates {@link DefaultPort} serializer instance.
*/
public DefaultPortSerializer() {
// non-null, immutable
diff --git a/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/DeviceIdSerializer.java b/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/DeviceIdSerializer.java
index c63b676..36d0a21 100644
--- a/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/DeviceIdSerializer.java
+++ b/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/DeviceIdSerializer.java
@@ -14,6 +14,14 @@
*/
public final class DeviceIdSerializer extends Serializer<DeviceId> {
+ /**
+ * Creates {@link DeviceId} serializer instance.
+ */
+ public DeviceIdSerializer() {
+ // non-null, immutable
+ super(false, true);
+ }
+
@Override
public void write(Kryo kryo, Output output, DeviceId object) {
kryo.writeObject(output, object.uri());
diff --git a/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/ImmutableMapSerializer.java b/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/ImmutableMapSerializer.java
index 244cc57..734033f 100644
--- a/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/ImmutableMapSerializer.java
+++ b/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/ImmutableMapSerializer.java
@@ -19,6 +19,9 @@
private final MapSerializer mapSerializer = new MapSerializer();
+ /**
+ * Creates {@link ImmutableMap} serializer instance.
+ */
public ImmutableMapSerializer() {
// non-null, immutable
super(false, true);
diff --git a/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/ImmutableSetSerializer.java b/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/ImmutableSetSerializer.java
index c08bf9a..051a843 100644
--- a/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/ImmutableSetSerializer.java
+++ b/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/ImmutableSetSerializer.java
@@ -18,6 +18,9 @@
private final CollectionSerializer serializer = new CollectionSerializer();
+ /**
+ * Creates {@link ImmutableSet} serializer instance.
+ */
public ImmutableSetSerializer() {
// non-null, immutable
super(false, true);
diff --git a/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/IpAddressSerializer.java b/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/IpAddressSerializer.java
new file mode 100644
index 0000000..b923df7
--- /dev/null
+++ b/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/IpAddressSerializer.java
@@ -0,0 +1,41 @@
+package org.onlab.onos.store.serializers;
+
+import org.onlab.packet.IpAddress;
+import com.esotericsoftware.kryo.Kryo;
+import com.esotericsoftware.kryo.Serializer;
+import com.esotericsoftware.kryo.io.Input;
+import com.esotericsoftware.kryo.io.Output;
+
+/**
+ * Kryo Serializer for {@link IpAddress}.
+ */
+public class IpAddressSerializer extends Serializer<IpAddress> {
+
+ /**
+ * Creates {@link IpAddress} serializer instance.
+ */
+ public IpAddressSerializer() {
+ // non-null, immutable
+ super(false, true);
+ }
+
+ @Override
+ public void write(Kryo kryo, Output output,
+ IpAddress object) {
+ byte[] octs = object.toOctets();
+ output.writeInt(octs.length);
+ output.writeBytes(octs);
+ output.writeInt(object.prefixLength());
+ }
+
+ @Override
+ public IpAddress read(Kryo kryo, Input input,
+ Class<IpAddress> type) {
+ int octLen = input.readInt();
+ byte[] octs = new byte[octLen];
+ input.read(octs);
+ int prefLen = input.readInt();
+ return IpAddress.valueOf(octs, prefLen);
+ }
+
+}
diff --git a/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/IpPrefixSerializer.java b/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/IpPrefixSerializer.java
index 2dbec57..2e92692 100644
--- a/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/IpPrefixSerializer.java
+++ b/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/IpPrefixSerializer.java
@@ -13,7 +13,7 @@
public final class IpPrefixSerializer extends Serializer<IpPrefix> {
/**
- * Default constructor.
+ * Creates {@link IpPrefix} serializer instance.
*/
public IpPrefixSerializer() {
// non-null, immutable
diff --git a/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/KryoPoolUtil.java b/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/KryoPoolUtil.java
new file mode 100644
index 0000000..0c33cfe
--- /dev/null
+++ b/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/KryoPoolUtil.java
@@ -0,0 +1,85 @@
+package org.onlab.onos.store.serializers;
+
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+
+import org.onlab.onos.cluster.ControllerNode;
+import org.onlab.onos.cluster.DefaultControllerNode;
+import org.onlab.onos.cluster.MastershipTerm;
+import org.onlab.onos.cluster.NodeId;
+import org.onlab.onos.net.ConnectPoint;
+import org.onlab.onos.net.DefaultAnnotations;
+import org.onlab.onos.net.DefaultDevice;
+import org.onlab.onos.net.DefaultLink;
+import org.onlab.onos.net.DefaultPort;
+import org.onlab.onos.net.Device;
+import org.onlab.onos.net.DeviceId;
+import org.onlab.onos.net.Element;
+import org.onlab.onos.net.Link;
+import org.onlab.onos.net.LinkKey;
+import org.onlab.onos.net.MastershipRole;
+import org.onlab.onos.net.Port;
+import org.onlab.onos.net.PortNumber;
+import org.onlab.onos.net.device.DefaultDeviceDescription;
+import org.onlab.onos.net.device.DefaultPortDescription;
+import org.onlab.onos.net.provider.ProviderId;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.IpPrefix;
+import org.onlab.util.KryoPool;
+
+import de.javakaffee.kryoserializers.URISerializer;
+
+public final class KryoPoolUtil {
+
+ /**
+ * KryoPool which can serialize ON.lab misc classes.
+ */
+ public static final KryoPool MISC = KryoPool.newBuilder()
+ .register(IpPrefix.class, new IpPrefixSerializer())
+ .register(IpAddress.class, new IpAddressSerializer())
+ .build();
+
+ // TODO: Populate other classes
+ /**
+ * KryoPool which can serialize API bundle classes.
+ */
+ public static final KryoPool API = KryoPool.newBuilder()
+ .register(MISC)
+ .register(
+ //
+ ArrayList.class,
+ Arrays.asList().getClass(),
+ HashMap.class,
+ //
+ ControllerNode.State.class,
+ Device.Type.class,
+ DefaultAnnotations.class,
+ DefaultControllerNode.class,
+ DefaultDevice.class,
+ DefaultDeviceDescription.class,
+ MastershipRole.class,
+ Port.class,
+ DefaultPortDescription.class,
+ Element.class,
+ Link.Type.class
+ )
+ .register(URI.class, new URISerializer())
+ .register(NodeId.class, new NodeIdSerializer())
+ .register(ProviderId.class, new ProviderIdSerializer())
+ .register(DeviceId.class, new DeviceIdSerializer())
+ .register(PortNumber.class, new PortNumberSerializer())
+ .register(DefaultPort.class, new DefaultPortSerializer())
+ .register(LinkKey.class, new LinkKeySerializer())
+ .register(ConnectPoint.class, new ConnectPointSerializer())
+ .register(DefaultLink.class, new DefaultLinkSerializer())
+ .register(MastershipTerm.class, new MastershipTermSerializer())
+ .register(MastershipRole.class, new MastershipRoleSerializer())
+
+ .build();
+
+
+ // not to be instantiated
+ private KryoPoolUtil() {}
+}
diff --git a/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/KryoSerializationManager.java b/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/KryoSerializationManager.java
deleted file mode 100644
index 04d1a88..0000000
--- a/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/KryoSerializationManager.java
+++ /dev/null
@@ -1,115 +0,0 @@
-package org.onlab.onos.store.serializers;
-
-import de.javakaffee.kryoserializers.URISerializer;
-import org.apache.felix.scr.annotations.Activate;
-import org.apache.felix.scr.annotations.Component;
-import org.apache.felix.scr.annotations.Deactivate;
-import org.apache.felix.scr.annotations.Service;
-import org.onlab.onos.cluster.ControllerNode;
-import org.onlab.onos.cluster.DefaultControllerNode;
-import org.onlab.onos.cluster.NodeId;
-import org.onlab.onos.net.ConnectPoint;
-import org.onlab.onos.net.DefaultAnnotations;
-import org.onlab.onos.net.DefaultDevice;
-import org.onlab.onos.net.DefaultLink;
-import org.onlab.onos.net.DefaultPort;
-import org.onlab.onos.net.Device;
-import org.onlab.onos.net.DeviceId;
-import org.onlab.onos.net.Element;
-import org.onlab.onos.net.Link;
-import org.onlab.onos.net.LinkKey;
-import org.onlab.onos.net.MastershipRole;
-import org.onlab.onos.net.Port;
-import org.onlab.onos.net.PortNumber;
-import org.onlab.onos.net.provider.ProviderId;
-import org.onlab.packet.IpPrefix;
-import org.onlab.util.KryoPool;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.net.URI;
-import java.nio.ByteBuffer;
-import java.util.ArrayList;
-import java.util.HashMap;
-
-/**
- * Serialization service using Kryo.
- */
-@Component(immediate = true)
-@Service
-public class KryoSerializationManager implements KryoSerializationService {
-
- private final Logger log = LoggerFactory.getLogger(getClass());
- private KryoPool serializerPool;
-
-
- @Activate
- public void activate() {
- setupKryoPool();
- log.info("Started");
- }
-
- @Deactivate
- public void deactivate() {
- log.info("Stopped");
- }
-
- /**
- * Sets up the common serialzers pool.
- */
- protected void setupKryoPool() {
- // FIXME Slice out types used in common to separate pool/namespace.
- serializerPool = KryoPool.newBuilder()
- .register(ArrayList.class,
- HashMap.class,
-
- ControllerNode.State.class,
- Device.Type.class,
-
- DefaultAnnotations.class,
- DefaultControllerNode.class,
- DefaultDevice.class,
- MastershipRole.class,
- Port.class,
- Element.class,
-
- Link.Type.class
- )
- .register(IpPrefix.class, new IpPrefixSerializer())
- .register(URI.class, new URISerializer())
- .register(NodeId.class, new NodeIdSerializer())
- .register(ProviderId.class, new ProviderIdSerializer())
- .register(DeviceId.class, new DeviceIdSerializer())
- .register(PortNumber.class, new PortNumberSerializer())
- .register(DefaultPort.class, new DefaultPortSerializer())
- .register(LinkKey.class, new LinkKeySerializer())
- .register(ConnectPoint.class, new ConnectPointSerializer())
- .register(DefaultLink.class, new DefaultLinkSerializer())
- .build()
- .populate(1);
- }
-
- @Override
- public byte[] serialize(final Object obj) {
- return serializerPool.serialize(obj);
- }
-
- @Override
- public <T> T deserialize(final byte[] bytes) {
- if (bytes == null) {
- return null;
- }
- return serializerPool.deserialize(bytes);
- }
-
- @Override
- public void serialize(Object obj, ByteBuffer buffer) {
- serializerPool.serialize(obj, buffer);
- }
-
- @Override
- public <T> T deserialize(ByteBuffer buffer) {
- return serializerPool.deserialize(buffer);
- }
-
-}
diff --git a/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/KryoSerializationService.java b/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/KryoSerializationService.java
deleted file mode 100644
index 385128c..0000000
--- a/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/KryoSerializationService.java
+++ /dev/null
@@ -1,46 +0,0 @@
-package org.onlab.onos.store.serializers;
-
-import java.nio.ByteBuffer;
-
-// TODO: To be replaced with SerializationService from IOLoop activity
-/**
- * Service to serialize Objects into byte array.
- */
-public interface KryoSerializationService {
-
- /**
- * Serializes the specified object into bytes using one of the
- * pre-registered serializers.
- *
- * @param obj object to be serialized
- * @return serialized bytes
- */
- public byte[] serialize(final Object obj);
-
- /**
- * Serializes the specified object into bytes using one of the
- * pre-registered serializers.
- *
- * @param obj object to be serialized
- * @param buffer to write serialized bytes
- */
- public void serialize(final Object obj, ByteBuffer buffer);
-
- /**
- * Deserializes the specified bytes into an object using one of the
- * pre-registered serializers.
- *
- * @param bytes bytes to be deserialized
- * @return deserialized object
- */
- public <T> T deserialize(final byte[] bytes);
-
- /**
- * Deserializes the specified bytes into an object using one of the
- * pre-registered serializers.
- *
- * @param buffer bytes to be deserialized
- * @return deserialized object
- */
- public <T> T deserialize(final ByteBuffer buffer);
-}
diff --git a/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/KryoSerializer.java b/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/KryoSerializer.java
new file mode 100644
index 0000000..738086e
--- /dev/null
+++ b/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/KryoSerializer.java
@@ -0,0 +1,55 @@
+package org.onlab.onos.store.serializers;
+
+import org.onlab.util.KryoPool;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.nio.ByteBuffer;
+
+/**
+ * StoreSerializer implementation using Kryo.
+ */
+public class KryoSerializer implements StoreSerializer {
+
+ private final Logger log = LoggerFactory.getLogger(getClass());
+ protected KryoPool serializerPool;
+
+
+ public KryoSerializer() {
+ setupKryoPool();
+ }
+
+ /**
+ * Sets up the common serialzers pool.
+ */
+ protected void setupKryoPool() {
+ serializerPool = KryoPool.newBuilder()
+ .register(KryoPoolUtil.API)
+ .build()
+ .populate(1);
+ }
+
+ @Override
+ public byte[] encode(final Object obj) {
+ return serializerPool.serialize(obj);
+ }
+
+ @Override
+ public <T> T decode(final byte[] bytes) {
+ if (bytes == null) {
+ return null;
+ }
+ return serializerPool.deserialize(bytes);
+ }
+
+ @Override
+ public void encode(Object obj, ByteBuffer buffer) {
+ serializerPool.serialize(obj, buffer);
+ }
+
+ @Override
+ public <T> T decode(ByteBuffer buffer) {
+ return serializerPool.deserialize(buffer);
+ }
+
+}
diff --git a/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/LinkKeySerializer.java b/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/LinkKeySerializer.java
index f635f3c..bafee4f 100644
--- a/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/LinkKeySerializer.java
+++ b/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/LinkKeySerializer.java
@@ -2,6 +2,7 @@
import org.onlab.onos.net.ConnectPoint;
import org.onlab.onos.net.LinkKey;
+
import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.Serializer;
import com.esotericsoftware.kryo.io.Input;
@@ -13,7 +14,7 @@
public class LinkKeySerializer extends Serializer<LinkKey> {
/**
- * Default constructor.
+ * Creates {@link LinkKey} serializer instance.
*/
public LinkKeySerializer() {
// non-null, immutable
diff --git a/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/MastershipRoleSerializer.java b/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/MastershipRoleSerializer.java
index 3903491..dab5aa8 100644
--- a/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/MastershipRoleSerializer.java
+++ b/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/MastershipRoleSerializer.java
@@ -12,6 +12,14 @@
*/
public class MastershipRoleSerializer extends Serializer<MastershipRole> {
+ /**
+ * Creates {@link MastershipRole} serializer instance.
+ */
+ public MastershipRoleSerializer() {
+ // non-null, immutable
+ super(false, true);
+ }
+
@Override
public MastershipRole read(Kryo kryo, Input input, Class<MastershipRole> type) {
final String role = kryo.readObject(input, String.class);
diff --git a/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/MastershipTermSerializer.java b/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/MastershipTermSerializer.java
index a5d6198..0ac61a8 100644
--- a/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/MastershipTermSerializer.java
+++ b/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/MastershipTermSerializer.java
@@ -2,7 +2,6 @@
import org.onlab.onos.cluster.MastershipTerm;
import org.onlab.onos.cluster.NodeId;
-
import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.Serializer;
import com.esotericsoftware.kryo.io.Input;
@@ -13,9 +12,17 @@
*/
public class MastershipTermSerializer extends Serializer<MastershipTerm> {
+ /**
+ * Creates {@link MastershipTerm} serializer instance.
+ */
+ public MastershipTermSerializer() {
+ // non-null, immutable
+ super(false, true);
+ }
+
@Override
public MastershipTerm read(Kryo kryo, Input input, Class<MastershipTerm> type) {
- final NodeId node = new NodeId(kryo.readObject(input, String.class));
+ final NodeId node = new NodeId(input.readString());
final int term = input.readInt();
return MastershipTerm.of(node, term);
}
diff --git a/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/NodeIdSerializer.java b/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/NodeIdSerializer.java
index ef9d3f1..460b63d 100644
--- a/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/NodeIdSerializer.java
+++ b/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/NodeIdSerializer.java
@@ -4,6 +4,7 @@
import com.esotericsoftware.kryo.Serializer;
import com.esotericsoftware.kryo.io.Input;
import com.esotericsoftware.kryo.io.Output;
+
import org.onlab.onos.cluster.NodeId;
/**
@@ -11,14 +12,22 @@
*/
public final class NodeIdSerializer extends Serializer<NodeId> {
+ /**
+ * Creates {@link NodeId} serializer instance.
+ */
+ public NodeIdSerializer() {
+ // non-null, immutable
+ super(false, true);
+ }
+
@Override
public void write(Kryo kryo, Output output, NodeId object) {
- kryo.writeObject(output, object.toString());
+ output.writeString(object.toString());
}
@Override
public NodeId read(Kryo kryo, Input input, Class<NodeId> type) {
- final String id = kryo.readObject(input, String.class);
+ final String id = input.readString();
return new NodeId(id);
}
}
diff --git a/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/PortNumberSerializer.java b/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/PortNumberSerializer.java
index 02805bb..3792966 100644
--- a/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/PortNumberSerializer.java
+++ b/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/PortNumberSerializer.java
@@ -14,7 +14,7 @@
Serializer<PortNumber> {
/**
- * Default constructor.
+ * Creates {@link PortNumber} serializer instance.
*/
public PortNumberSerializer() {
// non-null, immutable
diff --git a/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/ProviderIdSerializer.java b/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/ProviderIdSerializer.java
index 1a1c6f6..060ac7d 100644
--- a/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/ProviderIdSerializer.java
+++ b/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/ProviderIdSerializer.java
@@ -13,7 +13,7 @@
public class ProviderIdSerializer extends Serializer<ProviderId> {
/**
- * Default constructor.
+ * Creates {@link ProviderId} serializer instance.
*/
public ProviderIdSerializer() {
// non-null, immutable
@@ -24,13 +24,15 @@
public void write(Kryo kryo, Output output, ProviderId object) {
output.writeString(object.scheme());
output.writeString(object.id());
+ output.writeBoolean(object.isAncillary());
}
@Override
public ProviderId read(Kryo kryo, Input input, Class<ProviderId> type) {
String scheme = input.readString();
String id = input.readString();
- return new ProviderId(scheme, id);
+ boolean isAncillary = input.readBoolean();
+ return new ProviderId(scheme, id, isAncillary);
}
}
diff --git a/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/StoreSerializer.java b/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/StoreSerializer.java
new file mode 100644
index 0000000..6c43a1b
--- /dev/null
+++ b/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/StoreSerializer.java
@@ -0,0 +1,42 @@
+package org.onlab.onos.store.serializers;
+
+import java.nio.ByteBuffer;
+
+// TODO: To be replaced with SerializationService from IOLoop activity
+/**
+ * Service to serialize Objects into byte array.
+ */
+public interface StoreSerializer {
+
+ /**
+ * Serializes the specified object into bytes.
+ *
+ * @param obj object to be serialized
+ * @return serialized bytes
+ */
+ public byte[] encode(final Object obj);
+
+ /**
+ * Serializes the specified object into bytes.
+ *
+ * @param obj object to be serialized
+ * @param buffer to write serialized bytes
+ */
+ public void encode(final Object obj, ByteBuffer buffer);
+
+ /**
+ * Deserializes the specified bytes into an object.
+ *
+ * @param bytes bytes to be deserialized
+ * @return deserialized object
+ */
+ public <T> T decode(final byte[] bytes);
+
+ /**
+ * Deserializes the specified bytes into an object.
+ *
+ * @param buffer bytes to be deserialized
+ * @return deserialized object
+ */
+ public <T> T decode(final ByteBuffer buffer);
+}
diff --git a/core/store/serializers/src/test/java/org/onlab/onos/store/serializers/KryoSerializerTests.java b/core/store/serializers/src/test/java/org/onlab/onos/store/serializers/KryoSerializerTest.java
similarity index 61%
rename from core/store/serializers/src/test/java/org/onlab/onos/store/serializers/KryoSerializerTests.java
rename to core/store/serializers/src/test/java/org/onlab/onos/store/serializers/KryoSerializerTest.java
index c972d1a..d651d56 100644
--- a/core/store/serializers/src/test/java/org/onlab/onos/store/serializers/KryoSerializerTests.java
+++ b/core/store/serializers/src/test/java/org/onlab/onos/store/serializers/KryoSerializerTest.java
@@ -1,12 +1,10 @@
package org.onlab.onos.store.serializers;
+import static org.junit.Assert.assertEquals;
import static org.onlab.onos.net.DeviceId.deviceId;
import static org.onlab.onos.net.PortNumber.portNumber;
-import java.net.URI;
import java.nio.ByteBuffer;
-import java.util.ArrayList;
-import java.util.HashMap;
import org.junit.After;
import org.junit.Before;
@@ -14,7 +12,9 @@
import org.junit.Test;
import org.onlab.onos.cluster.MastershipTerm;
import org.onlab.onos.cluster.NodeId;
+import org.onlab.onos.net.Annotations;
import org.onlab.onos.net.ConnectPoint;
+import org.onlab.onos.net.DefaultAnnotations;
import org.onlab.onos.net.DefaultDevice;
import org.onlab.onos.net.DefaultLink;
import org.onlab.onos.net.DefaultPort;
@@ -24,7 +24,9 @@
import org.onlab.onos.net.LinkKey;
import org.onlab.onos.net.MastershipRole;
import org.onlab.onos.net.PortNumber;
+import org.onlab.onos.net.SparseAnnotations;
import org.onlab.onos.net.provider.ProviderId;
+import org.onlab.packet.IpAddress;
import org.onlab.packet.IpPrefix;
import org.onlab.util.KryoPool;
@@ -32,10 +34,10 @@
import com.google.common.collect.ImmutableSet;
import com.google.common.testing.EqualsTester;
-import de.javakaffee.kryoserializers.URISerializer;
+public class KryoSerializerTest {
-public class KryoSerializerTests {
private static final ProviderId PID = new ProviderId("of", "foo");
+ private static final ProviderId PIDA = new ProviderId("of", "foo", true);
private static final DeviceId DID1 = deviceId("of:foo");
private static final DeviceId DID2 = deviceId("of:bar");
private static final PortNumber P1 = portNumber(1);
@@ -48,44 +50,23 @@
private static final String SW2 = "3.9.5";
private static final String SN = "43311-12345";
private static final Device DEV1 = new DefaultDevice(PID, DID1, Device.Type.SWITCH, MFR, HW, SW1, SN);
+ private static final SparseAnnotations A1 = DefaultAnnotations.builder()
+ .set("A1", "a1")
+ .set("B1", "b1")
+ .build();
+ private static final SparseAnnotations A1_2 = DefaultAnnotations.builder()
+ .remove("A1")
+ .set("B3", "b3")
+ .build();
private static KryoPool kryos;
@BeforeClass
public static void setUpBeforeClass() throws Exception {
kryos = KryoPool.newBuilder()
- .register(
- ArrayList.class,
- HashMap.class
- )
- .register(
- Device.Type.class,
- Link.Type.class
-
-// ControllerNode.State.class,
-// DefaultControllerNode.class,
-// MastershipRole.class,
-// Port.class,
-// Element.class,
- )
- .register(ConnectPoint.class, new ConnectPointSerializer())
- .register(DefaultLink.class, new DefaultLinkSerializer())
- .register(DefaultPort.class, new DefaultPortSerializer())
- .register(DeviceId.class, new DeviceIdSerializer())
+ .register(KryoPoolUtil.API)
.register(ImmutableMap.class, new ImmutableMapSerializer())
.register(ImmutableSet.class, new ImmutableSetSerializer())
- .register(IpPrefix.class, new IpPrefixSerializer())
- .register(LinkKey.class, new LinkKeySerializer())
- .register(NodeId.class, new NodeIdSerializer())
- .register(PortNumber.class, new PortNumberSerializer())
- .register(ProviderId.class, new ProviderIdSerializer())
-
- .register(DefaultDevice.class)
-
- .register(URI.class, new URISerializer())
-
- .register(MastershipRole.class, new MastershipRoleSerializer())
- .register(MastershipTerm.class, new MastershipTermSerializer())
.build();
}
@@ -112,10 +93,12 @@
@Test
- public final void test() {
+ public final void testSerialization() {
testSerialized(new ConnectPoint(DID1, P1));
testSerialized(new DefaultLink(PID, CP1, CP2, Link.Type.DIRECT));
testSerialized(new DefaultPort(DEV1, P1, true));
+ testSerialized(new DefaultLink(PID, CP1, CP2, Link.Type.DIRECT, A1));
+ testSerialized(new DefaultPort(DEV1, P1, true, A1_2));
testSerialized(DID1);
testSerialized(ImmutableMap.of(DID1, DEV1, DID2, DEV1));
testSerialized(ImmutableMap.of(DID1, DEV1));
@@ -124,10 +107,41 @@
testSerialized(ImmutableSet.of(DID1));
testSerialized(ImmutableSet.of());
testSerialized(IpPrefix.valueOf("192.168.0.1/24"));
+ testSerialized(IpAddress.valueOf("192.168.0.1"));
testSerialized(new LinkKey(CP1, CP2));
testSerialized(new NodeId("SomeNodeIdentifier"));
testSerialized(P1);
testSerialized(PID);
+ testSerialized(PIDA);
+ testSerialized(new NodeId("bar"));
+ testSerialized(MastershipTerm.of(new NodeId("foo"), 2));
+ for (MastershipRole role : MastershipRole.values()) {
+ testSerialized(role);
+ }
+ }
+
+ @Test
+ public final void testAnnotations() {
+ // Annotations does not have equals defined, manually test equality
+ final byte[] a1Bytes = kryos.serialize(A1);
+ SparseAnnotations copiedA1 = kryos.deserialize(a1Bytes);
+ assertAnnotationsEquals(copiedA1, A1);
+
+ final byte[] a12Bytes = kryos.serialize(A1_2);
+ SparseAnnotations copiedA12 = kryos.deserialize(a12Bytes);
+ assertAnnotationsEquals(copiedA12, A1_2);
+ }
+
+ // code clone
+ public static void assertAnnotationsEquals(Annotations actual, SparseAnnotations... annotations) {
+ SparseAnnotations expected = DefaultAnnotations.builder().build();
+ for (SparseAnnotations a : annotations) {
+ expected = DefaultAnnotations.union(expected, a);
+ }
+ assertEquals(expected.keys(), actual.keys());
+ for (String key : expected.keys()) {
+ assertEquals(expected.value(key), actual.value(key));
+ }
}
}
diff --git a/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/NoOpClockProviderService.java b/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/NoOpClockProviderService.java
new file mode 100644
index 0000000..ff4b31a
--- /dev/null
+++ b/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/NoOpClockProviderService.java
@@ -0,0 +1,20 @@
+package org.onlab.onos.store.trivial.impl;
+
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Service;
+import org.onlab.onos.cluster.MastershipTerm;
+import org.onlab.onos.net.DeviceId;
+import org.onlab.onos.store.ClockProviderService;
+
+//FIXME: Code clone in onos-core-trivial, onos-core-hz-net
+/**
+ * Dummy implementation of {@link ClockProviderService}.
+ */
+@Component(immediate = true)
+@Service
+public class NoOpClockProviderService implements ClockProviderService {
+
+ @Override
+ public void setMastershipTerm(DeviceId deviceId, MastershipTerm term) {
+ }
+}
diff --git a/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/NoOpClockService.java b/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/NoOpClockService.java
deleted file mode 100644
index b3f8320..0000000
--- a/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/NoOpClockService.java
+++ /dev/null
@@ -1,32 +0,0 @@
-package org.onlab.onos.store.trivial.impl;
-
-import org.apache.felix.scr.annotations.Component;
-import org.apache.felix.scr.annotations.Service;
-import org.onlab.onos.cluster.MastershipTerm;
-import org.onlab.onos.net.DeviceId;
-import org.onlab.onos.store.ClockService;
-import org.onlab.onos.store.Timestamp;
-
-//FIXME: Code clone in onos-core-trivial, onos-core-hz-net
-/**
- * Dummy implementation of {@link ClockService}.
- */
-@Component(immediate = true)
-@Service
-public class NoOpClockService implements ClockService {
-
- @Override
- public Timestamp getTimestamp(DeviceId deviceId) {
- return new Timestamp() {
-
- @Override
- public int compareTo(Timestamp o) {
- throw new IllegalStateException("Never expected to be used.");
- }
- };
- }
-
- @Override
- public void setMastershipTerm(DeviceId deviceId, MastershipTerm term) {
- }
-}
diff --git a/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/SimpleDeviceStore.java b/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/SimpleDeviceStore.java
index 0b0ae37..514a22e 100644
--- a/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/SimpleDeviceStore.java
+++ b/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/SimpleDeviceStore.java
@@ -2,6 +2,8 @@
import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
import org.apache.commons.lang3.concurrent.ConcurrentException;
import org.apache.commons.lang3.concurrent.ConcurrentInitializer;
@@ -9,7 +11,7 @@
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Deactivate;
import org.apache.felix.scr.annotations.Service;
-import org.onlab.onos.net.Annotations;
+import org.onlab.onos.net.AnnotationsUtil;
import org.onlab.onos.net.DefaultAnnotations;
import org.onlab.onos.net.DefaultDevice;
import org.onlab.onos.net.DefaultPort;
@@ -28,10 +30,10 @@
import org.onlab.onos.net.device.PortDescription;
import org.onlab.onos.net.provider.ProviderId;
import org.onlab.onos.store.AbstractStore;
+import org.onlab.util.NewConcurrentHashMap;
import org.slf4j.Logger;
import java.util.ArrayList;
-import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
@@ -47,12 +49,13 @@
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Predicates.notNull;
+import static com.google.common.base.Verify.verify;
import static org.onlab.onos.net.device.DeviceEvent.Type.*;
import static org.slf4j.LoggerFactory.getLogger;
import static org.apache.commons.lang3.concurrent.ConcurrentUtils.createIfAbsentUnchecked;
+import static org.onlab.onos.net.DefaultAnnotations.union;
import static org.onlab.onos.net.DefaultAnnotations.merge;
-// TODO: synchronization should be done in more fine-grained manner.
/**
* Manages inventory of infrastructure devices using trivial in-memory
* structures implementation.
@@ -70,14 +73,14 @@
// collection of Description given from various providers
private final ConcurrentMap<DeviceId,
ConcurrentMap<ProviderId, DeviceDescriptions>>
- deviceDescs = new ConcurrentHashMap<>();
+ deviceDescs = Maps.newConcurrentMap();
// cache of Device and Ports generated by compositing descriptions from providers
- private final ConcurrentMap<DeviceId, Device> devices = new ConcurrentHashMap<>();
- private final ConcurrentMap<DeviceId, ConcurrentMap<PortNumber, Port>> devicePorts = new ConcurrentHashMap<>();
+ private final ConcurrentMap<DeviceId, Device> devices = Maps.newConcurrentMap();
+ private final ConcurrentMap<DeviceId, ConcurrentMap<PortNumber, Port>> devicePorts = Maps.newConcurrentMap();
// available(=UP) devices
- private final Set<DeviceId> availableDevices = new HashSet<>();
+ private final Set<DeviceId> availableDevices = Sets.newConcurrentHashSet();
@Activate
@@ -87,6 +90,10 @@
@Deactivate
public void deactivate() {
+ deviceDescs.clear();
+ devices.clear();
+ devicePorts.clear();
+ availableDevices.clear();
log.info("Stopped");
}
@@ -106,117 +113,142 @@
}
@Override
- public synchronized DeviceEvent createOrUpdateDevice(ProviderId providerId, DeviceId deviceId,
+ public DeviceEvent createOrUpdateDevice(ProviderId providerId,
+ DeviceId deviceId,
DeviceDescription deviceDescription) {
+
ConcurrentMap<ProviderId, DeviceDescriptions> providerDescs
- = createIfAbsentUnchecked(deviceDescs, deviceId,
- new InitConcurrentHashMap<ProviderId, DeviceDescriptions>());
+ = getDeviceDescriptions(deviceId);
- Device oldDevice = devices.get(deviceId);
+ synchronized (providerDescs) {
+ // locking per device
- DeviceDescriptions descs
- = createIfAbsentUnchecked(providerDescs, providerId,
- new InitDeviceDescs(deviceDescription));
+ DeviceDescriptions descs
+ = createIfAbsentUnchecked(providerDescs, providerId,
+ new InitDeviceDescs(deviceDescription));
- // update description
- descs.putDeviceDesc(deviceDescription);
- Device newDevice = composeDevice(deviceId, providerDescs);
+ Device oldDevice = devices.get(deviceId);
+ // update description
+ descs.putDeviceDesc(deviceDescription);
+ Device newDevice = composeDevice(deviceId, providerDescs);
- if (oldDevice == null) {
- // ADD
- return createDevice(providerId, newDevice);
- } else {
- // UPDATE or ignore (no change or stale)
- return updateDevice(providerId, oldDevice, newDevice);
+ if (oldDevice == null) {
+ // ADD
+ return createDevice(providerId, newDevice);
+ } else {
+ // UPDATE or ignore (no change or stale)
+ return updateDevice(providerId, oldDevice, newDevice);
+ }
}
}
// Creates the device and returns the appropriate event if necessary.
+ // Guarded by deviceDescs value (=Device lock)
private DeviceEvent createDevice(ProviderId providerId, Device newDevice) {
// update composed device cache
- synchronized (this) {
- devices.putIfAbsent(newDevice.id(), newDevice);
- if (!providerId.isAncillary()) {
- availableDevices.add(newDevice.id());
- }
+ Device oldDevice = devices.putIfAbsent(newDevice.id(), newDevice);
+ verify(oldDevice == null,
+ "Unexpected Device in cache. PID:%s [old=%s, new=%s]",
+ providerId, oldDevice, newDevice);
+
+ if (!providerId.isAncillary()) {
+ availableDevices.add(newDevice.id());
}
return new DeviceEvent(DeviceEvent.Type.DEVICE_ADDED, newDevice, null);
}
// Updates the device and returns the appropriate event if necessary.
+ // Guarded by deviceDescs value (=Device lock)
private DeviceEvent updateDevice(ProviderId providerId, Device oldDevice, Device newDevice) {
// We allow only certain attributes to trigger update
if (!Objects.equals(oldDevice.hwVersion(), newDevice.hwVersion()) ||
!Objects.equals(oldDevice.swVersion(), newDevice.swVersion()) ||
- !isAnnotationsEqual(oldDevice.annotations(), newDevice.annotations())) {
+ !AnnotationsUtil.isEqual(oldDevice.annotations(), newDevice.annotations())) {
- synchronized (this) {
- devices.replace(newDevice.id(), oldDevice, newDevice);
- if (!providerId.isAncillary()) {
- availableDevices.add(newDevice.id());
- }
+ boolean replaced = devices.replace(newDevice.id(), oldDevice, newDevice);
+ if (!replaced) {
+ verify(replaced,
+ "Replacing devices cache failed. PID:%s [expected:%s, found:%s, new=%s]",
+ providerId, oldDevice, devices.get(newDevice.id())
+ , newDevice);
+ }
+ if (!providerId.isAncillary()) {
+ availableDevices.add(newDevice.id());
}
return new DeviceEvent(DeviceEvent.Type.DEVICE_UPDATED, newDevice, null);
}
// Otherwise merely attempt to change availability if primary provider
if (!providerId.isAncillary()) {
- synchronized (this) {
boolean added = availableDevices.add(newDevice.id());
return !added ? null :
new DeviceEvent(DEVICE_AVAILABILITY_CHANGED, newDevice, null);
- }
}
return null;
}
@Override
public DeviceEvent markOffline(DeviceId deviceId) {
- synchronized (this) {
+ ConcurrentMap<ProviderId, DeviceDescriptions> providerDescs
+ = getDeviceDescriptions(deviceId);
+
+ // locking device
+ synchronized (providerDescs) {
Device device = devices.get(deviceId);
- boolean removed = (device != null) && availableDevices.remove(deviceId);
- return !removed ? null :
- new DeviceEvent(DEVICE_AVAILABILITY_CHANGED, device, null);
+ if (device == null) {
+ return null;
+ }
+ boolean removed = availableDevices.remove(deviceId);
+ if (removed) {
+ // TODO: broadcast ... DOWN only?
+ return new DeviceEvent(DEVICE_AVAILABILITY_CHANGED, device, null);
+ }
+ return null;
}
}
@Override
- public synchronized List<DeviceEvent> updatePorts(ProviderId providerId, DeviceId deviceId,
- List<PortDescription> portDescriptions) {
+ public List<DeviceEvent> updatePorts(ProviderId providerId,
+ DeviceId deviceId,
+ List<PortDescription> portDescriptions) {
- // TODO: implement multi-provider
Device device = devices.get(deviceId);
checkArgument(device != null, DEVICE_NOT_FOUND, deviceId);
ConcurrentMap<ProviderId, DeviceDescriptions> descsMap = deviceDescs.get(deviceId);
checkArgument(descsMap != null, DEVICE_NOT_FOUND, deviceId);
- DeviceDescriptions descs = descsMap.get(providerId);
- checkArgument(descs != null,
- "Device description for Device ID %s from Provider %s was not found",
- deviceId, providerId);
-
-
List<DeviceEvent> events = new ArrayList<>();
- synchronized (this) {
- ConcurrentMap<PortNumber, Port> ports = getPortMap(deviceId);
+ synchronized (descsMap) {
+ DeviceDescriptions descs = descsMap.get(providerId);
+ // every provider must provide DeviceDescription.
+ checkArgument(descs != null,
+ "Device description for Device ID %s from Provider %s was not found",
+ deviceId, providerId);
+
+ Map<PortNumber, Port> ports = getPortMap(deviceId);
// Add new ports
Set<PortNumber> processed = new HashSet<>();
for (PortDescription portDescription : portDescriptions) {
- PortNumber number = portDescription.portNumber();
- Port oldPort = ports.get(number);
+ final PortNumber number = portDescription.portNumber();
+ processed.add(portDescription.portNumber());
+
+ final Port oldPort = ports.get(number);
+ final Port newPort;
+
+// event suppression hook?
+
// update description
descs.putPortDesc(portDescription);
- Port newPort = composePort(device, number, descsMap);
+ newPort = composePort(device, number, descsMap);
events.add(oldPort == null ?
- createPort(device, newPort, ports) :
- updatePort(device, oldPort, newPort, ports));
- processed.add(portDescription.portNumber());
+ createPort(device, newPort, ports) :
+ updatePort(device, oldPort, newPort, ports));
}
events.addAll(pruneOldPorts(device, ports, processed));
@@ -226,19 +258,21 @@
// Creates a new port based on the port description adds it to the map and
// Returns corresponding event.
+ // Guarded by deviceDescs value (=Device lock)
private DeviceEvent createPort(Device device, Port newPort,
- ConcurrentMap<PortNumber, Port> ports) {
+ Map<PortNumber, Port> ports) {
ports.put(newPort.number(), newPort);
return new DeviceEvent(PORT_ADDED, device, newPort);
}
// Checks if the specified port requires update and if so, it replaces the
// existing entry in the map and returns corresponding event.
+ // Guarded by deviceDescs value (=Device lock)
private DeviceEvent updatePort(Device device, Port oldPort,
Port newPort,
- ConcurrentMap<PortNumber, Port> ports) {
+ Map<PortNumber, Port> ports) {
if (oldPort.isEnabled() != newPort.isEnabled() ||
- !isAnnotationsEqual(oldPort.annotations(), newPort.annotations())) {
+ !AnnotationsUtil.isEqual(oldPort.annotations(), newPort.annotations())) {
ports.put(oldPort.number(), newPort);
return new DeviceEvent(PORT_UPDATED, device, newPort);
@@ -248,6 +282,7 @@
// Prunes the specified list of ports based on which ports are in the
// processed list and returns list of corresponding events.
+ // Guarded by deviceDescs value (=Device lock)
private List<DeviceEvent> pruneOldPorts(Device device,
Map<PortNumber, Port> ports,
Set<PortNumber> processed) {
@@ -268,11 +303,17 @@
// exist, it creates and registers a new one.
private ConcurrentMap<PortNumber, Port> getPortMap(DeviceId deviceId) {
return createIfAbsentUnchecked(devicePorts, deviceId,
- new InitConcurrentHashMap<PortNumber, Port>());
+ NewConcurrentHashMap.<PortNumber, Port>ifNeeded());
+ }
+
+ private ConcurrentMap<ProviderId, DeviceDescriptions> getDeviceDescriptions(
+ DeviceId deviceId) {
+ return createIfAbsentUnchecked(deviceDescs, deviceId,
+ NewConcurrentHashMap.<ProviderId, DeviceDescriptions>ifNeeded());
}
@Override
- public synchronized DeviceEvent updatePortStatus(ProviderId providerId, DeviceId deviceId,
+ public DeviceEvent updatePortStatus(ProviderId providerId, DeviceId deviceId,
PortDescription portDescription) {
Device device = devices.get(deviceId);
checkArgument(device != null, DEVICE_NOT_FOUND, deviceId);
@@ -280,19 +321,22 @@
ConcurrentMap<ProviderId, DeviceDescriptions> descsMap = deviceDescs.get(deviceId);
checkArgument(descsMap != null, DEVICE_NOT_FOUND, deviceId);
- DeviceDescriptions descs = descsMap.get(providerId);
- // assuming all providers must to give DeviceDescription
- checkArgument(descs != null,
- "Device description for Device ID %s from Provider %s was not found",
- deviceId, providerId);
+ synchronized (descsMap) {
+ DeviceDescriptions descs = descsMap.get(providerId);
+ // assuming all providers must to give DeviceDescription
+ checkArgument(descs != null,
+ "Device description for Device ID %s from Provider %s was not found",
+ deviceId, providerId);
- synchronized (this) {
ConcurrentMap<PortNumber, Port> ports = getPortMap(deviceId);
final PortNumber number = portDescription.portNumber();
- Port oldPort = ports.get(number);
+ final Port oldPort = ports.get(number);
+ final Port newPort;
+
// update description
descs.putPortDesc(portDescription);
- Port newPort = composePort(device, number, descsMap);
+ newPort = composePort(device, number, descsMap);
+
if (oldPort == null) {
return createPort(device, newPort, ports);
} else {
@@ -323,31 +367,19 @@
@Override
public DeviceEvent removeDevice(DeviceId deviceId) {
- synchronized (this) {
+ ConcurrentMap<ProviderId, DeviceDescriptions> descs = getDeviceDescriptions(deviceId);
+ synchronized (descs) {
Device device = devices.remove(deviceId);
- return device == null ? null :
- new DeviceEvent(DEVICE_REMOVED, device, null);
- }
- }
-
- private static boolean isAnnotationsEqual(Annotations lhs, Annotations rhs) {
- if (lhs == rhs) {
- return true;
- }
- if (lhs == null || rhs == null) {
- return false;
- }
-
- if (!lhs.keys().equals(rhs.keys())) {
- return false;
- }
-
- for (String key : lhs.keys()) {
- if (!lhs.value(key).equals(rhs.value(key))) {
- return false;
+ // should DEVICE_REMOVED carry removed ports?
+ Map<PortNumber, Port> ports = devicePorts.get(deviceId);
+ if (ports != null) {
+ ports.clear();
}
+ availableDevices.remove(deviceId);
+ descs.clear();
+ return device == null ? null :
+ new DeviceEvent(DEVICE_REMOVED, device, null);
}
- return true;
}
/**
@@ -366,14 +398,14 @@
DeviceDescriptions desc = providerDescs.get(primary);
- // base
- Type type = desc.getDeviceDesc().type();
- String manufacturer = desc.getDeviceDesc().manufacturer();
- String hwVersion = desc.getDeviceDesc().hwVersion();
- String swVersion = desc.getDeviceDesc().swVersion();
- String serialNumber = desc.getDeviceDesc().serialNumber();
+ final DeviceDescription base = desc.getDeviceDesc();
+ Type type = base.type();
+ String manufacturer = base.manufacturer();
+ String hwVersion = base.hwVersion();
+ String swVersion = base.swVersion();
+ String serialNumber = base.serialNumber();
DefaultAnnotations annotations = DefaultAnnotations.builder().build();
- annotations = merge(annotations, desc.getDeviceDesc().annotations());
+ annotations = merge(annotations, base.annotations());
for (Entry<ProviderId, DeviceDescriptions> e : providerDescs.entrySet()) {
if (e.getKey().equals(primary)) {
@@ -392,7 +424,14 @@
hwVersion, swVersion, serialNumber, annotations);
}
- // probably want composePort"s" also
+ /**
+ * Returns a Port, merging description given from multiple Providers.
+ *
+ * @param device device the port is on
+ * @param number port number
+ * @param providerDescs Collection of Descriptions from multiple providers
+ * @return Port instance
+ */
private Port composePort(Device device, PortNumber number,
ConcurrentMap<ProviderId, DeviceDescriptions> providerDescs) {
@@ -445,18 +484,11 @@
return fallBackPrimary;
}
- // TODO: can be made generic
- private static final class InitConcurrentHashMap<K, V> implements
- ConcurrentInitializer<ConcurrentMap<K, V>> {
- @Override
- public ConcurrentMap<K, V> get() throws ConcurrentException {
- return new ConcurrentHashMap<>();
- }
- }
-
public static final class InitDeviceDescs
implements ConcurrentInitializer<DeviceDescriptions> {
+
private final DeviceDescription deviceDesc;
+
public InitDeviceDescs(DeviceDescription deviceDesc) {
this.deviceDesc = checkNotNull(deviceDesc);
}
@@ -471,8 +503,6 @@
* Collection of Description of a Device and it's Ports given from a Provider.
*/
private static class DeviceDescriptions {
- // private final DeviceId id;
- // private final ProviderId pid;
private final AtomicReference<DeviceDescription> deviceDesc;
private final ConcurrentMap<PortNumber, PortDescription> portDescs;
@@ -490,10 +520,6 @@
return portDescs.get(number);
}
- public Collection<PortDescription> getPortDescs() {
- return Collections.unmodifiableCollection(portDescs.values());
- }
-
/**
* Puts DeviceDescription, merging annotations as necessary.
*
@@ -504,7 +530,7 @@
DeviceDescription oldOne = deviceDesc.get();
DeviceDescription newOne = newDesc;
if (oldOne != null) {
- SparseAnnotations merged = merge(oldOne.annotations(),
+ SparseAnnotations merged = union(oldOne.annotations(),
newDesc.annotations());
newOne = new DefaultDeviceDescription(newOne, merged);
}
@@ -521,7 +547,7 @@
PortDescription oldOne = portDescs.get(newDesc.portNumber());
PortDescription newOne = newDesc;
if (oldOne != null) {
- SparseAnnotations merged = merge(oldOne.annotations(),
+ SparseAnnotations merged = union(oldOne.annotations(),
newDesc.annotations());
newOne = new DefaultPortDescription(newOne, merged);
}
diff --git a/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/SimpleFlowRuleStore.java b/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/SimpleFlowRuleStore.java
index d12d00e..7ff797c 100644
--- a/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/SimpleFlowRuleStore.java
+++ b/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/SimpleFlowRuleStore.java
@@ -12,9 +12,10 @@
import org.apache.felix.scr.annotations.Service;
import org.onlab.onos.ApplicationId;
import org.onlab.onos.net.DeviceId;
-import org.onlab.onos.net.flow.DefaultFlowRule;
+import org.onlab.onos.net.flow.DefaultFlowEntry;
+import org.onlab.onos.net.flow.FlowEntry;
+import org.onlab.onos.net.flow.FlowEntry.FlowEntryState;
import org.onlab.onos.net.flow.FlowRule;
-import org.onlab.onos.net.flow.FlowRule.FlowRuleState;
import org.onlab.onos.net.flow.FlowRuleEvent;
import org.onlab.onos.net.flow.FlowRuleEvent.Type;
import org.onlab.onos.net.flow.FlowRuleStore;
@@ -38,8 +39,8 @@
private final Logger log = getLogger(getClass());
// store entries as a pile of rules, no info about device tables
- private final Multimap<DeviceId, FlowRule> flowEntries =
- ArrayListMultimap.<DeviceId, FlowRule>create();
+ private final Multimap<DeviceId, FlowEntry> flowEntries =
+ ArrayListMultimap.<DeviceId, FlowEntry>create();
private final Multimap<ApplicationId, FlowRule> flowEntriesById =
ArrayListMultimap.<ApplicationId, FlowRule>create();
@@ -56,8 +57,13 @@
@Override
- public synchronized FlowRule getFlowRule(FlowRule rule) {
- for (FlowRule f : flowEntries.get(rule.deviceId())) {
+ public int getFlowRuleCount() {
+ return flowEntries.size();
+ }
+
+ @Override
+ public synchronized FlowEntry getFlowEntry(FlowRule rule) {
+ for (FlowEntry f : flowEntries.get(rule.deviceId())) {
if (f.equals(rule)) {
return f;
}
@@ -66,8 +72,8 @@
}
@Override
- public synchronized Iterable<FlowRule> getFlowEntries(DeviceId deviceId) {
- Collection<FlowRule> rules = flowEntries.get(deviceId);
+ public synchronized Iterable<FlowEntry> getFlowEntries(DeviceId deviceId) {
+ Collection<FlowEntry> rules = flowEntries.get(deviceId);
if (rules == null) {
return Collections.emptyList();
}
@@ -75,7 +81,7 @@
}
@Override
- public synchronized Iterable<FlowRule> getFlowEntriesByAppId(ApplicationId appId) {
+ public synchronized Iterable<FlowRule> getFlowRulesByAppId(ApplicationId appId) {
Collection<FlowRule> rules = flowEntriesById.get(appId);
if (rules == null) {
return Collections.emptyList();
@@ -85,7 +91,7 @@
@Override
public synchronized void storeFlowRule(FlowRule rule) {
- FlowRule f = new DefaultFlowRule(rule, FlowRuleState.PENDING_ADD);
+ FlowEntry f = new DefaultFlowEntry(rule);
DeviceId did = f.deviceId();
if (!flowEntries.containsEntry(did, f)) {
flowEntries.put(did, f);
@@ -95,51 +101,42 @@
@Override
public synchronized void deleteFlowRule(FlowRule rule) {
- FlowRule f = new DefaultFlowRule(rule, FlowRuleState.PENDING_REMOVE);
- DeviceId did = f.deviceId();
-
- /*
- * find the rule and mark it for deletion.
- * Ultimately a flow removed will come remove it.
- */
-
- if (flowEntries.containsEntry(did, f)) {
- flowEntries.remove(did, f);
- flowEntries.put(did, f);
- flowEntriesById.remove(rule.appId(), rule);
+ FlowEntry entry = getFlowEntry(rule);
+ if (entry == null) {
+ //log.warn("Cannot find rule {}", rule);
+ return;
}
+ entry.setState(FlowEntryState.PENDING_REMOVE);
}
@Override
- public synchronized FlowRuleEvent addOrUpdateFlowRule(FlowRule rule) {
+ public synchronized FlowRuleEvent addOrUpdateFlowRule(FlowEntry rule) {
DeviceId did = rule.deviceId();
// check if this new rule is an update to an existing entry
- FlowRule stored = getFlowRule(rule);
+ FlowEntry stored = getFlowEntry(rule);
if (stored != null) {
- // Multimaps support duplicates so we have to remove our rule
- // and replace it with the current version.
- flowEntries.remove(did, rule);
- flowEntries.put(did, rule);
-
- if (stored.state() == FlowRuleState.PENDING_ADD) {
+ stored.setBytes(rule.bytes());
+ stored.setLife(rule.life());
+ stored.setPackets(rule.packets());
+ if (stored.state() == FlowEntryState.PENDING_ADD) {
+ stored.setState(FlowEntryState.ADDED);
return new FlowRuleEvent(Type.RULE_ADDED, rule);
}
return new FlowRuleEvent(Type.RULE_UPDATED, rule);
}
- flowEntries.put(did, rule);
+ //flowEntries.put(did, rule);
return null;
}
@Override
- public synchronized FlowRuleEvent removeFlowRule(FlowRule rule) {
- //synchronized (this) {
+ public synchronized FlowRuleEvent removeFlowRule(FlowEntry rule) {
+ // This is where one could mark a rule as removed and still keep it in the store.
if (flowEntries.remove(rule.deviceId(), rule)) {
return new FlowRuleEvent(RULE_REMOVED, rule);
} else {
return null;
}
- //}
}
}
diff --git a/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/SimpleIntentStore.java b/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/SimpleIntentStore.java
new file mode 100644
index 0000000..732d753
--- /dev/null
+++ b/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/SimpleIntentStore.java
@@ -0,0 +1,108 @@
+package org.onlab.onos.store.trivial.impl;
+
+import com.google.common.collect.ImmutableSet;
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Service;
+import org.onlab.onos.net.intent.InstallableIntent;
+import org.onlab.onos.net.intent.Intent;
+import org.onlab.onos.net.intent.IntentEvent;
+import org.onlab.onos.net.intent.IntentId;
+import org.onlab.onos.net.intent.IntentState;
+import org.onlab.onos.net.intent.IntentStore;
+import org.onlab.onos.net.intent.IntentStoreDelegate;
+import org.onlab.onos.store.AbstractStore;
+import org.slf4j.Logger;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static org.onlab.onos.net.intent.IntentState.*;
+import static org.slf4j.LoggerFactory.getLogger;
+
+@Component(immediate = true)
+@Service
+public class SimpleIntentStore
+ extends AbstractStore<IntentEvent, IntentStoreDelegate>
+ implements IntentStore {
+
+ private final Logger log = getLogger(getClass());
+ private final Map<IntentId, Intent> intents = new HashMap<>();
+ private final Map<IntentId, IntentState> states = new HashMap<>();
+ private final Map<IntentId, List<InstallableIntent>> installable = new HashMap<>();
+
+ @Activate
+ public void activate() {
+ log.info("Started");
+ }
+
+ @Deactivate
+ public void deactivate() {
+ log.info("Stopped");
+ }
+
+ @Override
+ public IntentEvent createIntent(Intent intent) {
+ intents.put(intent.id(), intent);
+ return this.setState(intent, IntentState.SUBMITTED);
+ }
+
+ @Override
+ public IntentEvent removeIntent(IntentId intentId) {
+ Intent intent = intents.remove(intentId);
+ installable.remove(intentId);
+ IntentEvent event = this.setState(intent, WITHDRAWN);
+ states.remove(intentId);
+ return event;
+ }
+
+ @Override
+ public long getIntentCount() {
+ return intents.size();
+ }
+
+ @Override
+ public Iterable<Intent> getIntents() {
+ return ImmutableSet.copyOf(intents.values());
+ }
+
+ @Override
+ public Intent getIntent(IntentId intentId) {
+ return intents.get(intentId);
+ }
+
+ @Override
+ public IntentState getIntentState(IntentId id) {
+ return states.get(id);
+ }
+
+ @Override
+ public IntentEvent setState(Intent intent, IntentState state) {
+ IntentId id = intent.id();
+ states.put(id, state);
+ IntentEvent.Type type = (state == SUBMITTED ? IntentEvent.Type.SUBMITTED :
+ (state == INSTALLED ? IntentEvent.Type.INSTALLED :
+ (state == FAILED ? IntentEvent.Type.FAILED :
+ state == WITHDRAWN ? IntentEvent.Type.WITHDRAWN :
+ null)));
+ return type == null ? null : new IntentEvent(type, intent);
+ }
+
+ @Override
+ public void addInstallableIntents(IntentId intentId, List<InstallableIntent> result) {
+ installable.put(intentId, result);
+ }
+
+ @Override
+ public List<InstallableIntent> getInstallableIntents(IntentId intentId) {
+ return installable.get(intentId);
+ }
+
+ @Override
+ public void removeInstalledIntents(IntentId intentId) {
+ installable.remove(intentId);
+ }
+
+}
diff --git a/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/SimpleLinkStore.java b/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/SimpleLinkStore.java
index a0e569d..daf28df 100644
--- a/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/SimpleLinkStore.java
+++ b/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/SimpleLinkStore.java
@@ -1,36 +1,51 @@
package org.onlab.onos.store.trivial.impl;
+import com.google.common.base.Function;
+import com.google.common.base.Predicate;
+import com.google.common.collect.FluentIterable;
import com.google.common.collect.HashMultimap;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Multimap;
+import com.google.common.collect.SetMultimap;
+import org.apache.commons.lang3.concurrent.ConcurrentUtils;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Deactivate;
import org.apache.felix.scr.annotations.Service;
+import org.onlab.onos.net.AnnotationsUtil;
import org.onlab.onos.net.ConnectPoint;
+import org.onlab.onos.net.DefaultAnnotations;
import org.onlab.onos.net.DefaultLink;
import org.onlab.onos.net.DeviceId;
import org.onlab.onos.net.Link;
+import org.onlab.onos.net.SparseAnnotations;
+import org.onlab.onos.net.Link.Type;
import org.onlab.onos.net.LinkKey;
+import org.onlab.onos.net.Provided;
+import org.onlab.onos.net.link.DefaultLinkDescription;
import org.onlab.onos.net.link.LinkDescription;
import org.onlab.onos.net.link.LinkEvent;
import org.onlab.onos.net.link.LinkStore;
import org.onlab.onos.net.link.LinkStoreDelegate;
import org.onlab.onos.net.provider.ProviderId;
import org.onlab.onos.store.AbstractStore;
+import org.onlab.util.NewConcurrentHashMap;
import org.slf4j.Logger;
import java.util.Collections;
import java.util.HashSet;
-import java.util.Map;
import java.util.Set;
+import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import static org.onlab.onos.net.DefaultAnnotations.union;
+import static org.onlab.onos.net.DefaultAnnotations.merge;
import static org.onlab.onos.net.Link.Type.DIRECT;
import static org.onlab.onos.net.Link.Type.INDIRECT;
import static org.onlab.onos.net.link.LinkEvent.Type.*;
import static org.slf4j.LoggerFactory.getLogger;
+import static com.google.common.collect.Multimaps.synchronizedSetMultimap;
+import static com.google.common.base.Predicates.notNull;
/**
* Manages inventory of infrastructure links using trivial in-memory structures
@@ -45,11 +60,17 @@
private final Logger log = getLogger(getClass());
// Link inventory
- private final Map<LinkKey, DefaultLink> links = new ConcurrentHashMap<>();
+ private final ConcurrentMap<LinkKey,
+ ConcurrentMap<ProviderId, LinkDescription>>
+ linkDescs = new ConcurrentHashMap<>();
+
+ // Link instance cache
+ private final ConcurrentMap<LinkKey, Link> links = new ConcurrentHashMap<>();
// Egress and ingress link sets
- private final Multimap<DeviceId, Link> srcLinks = HashMultimap.create();
- private final Multimap<DeviceId, Link> dstLinks = HashMultimap.create();
+ private final SetMultimap<DeviceId, LinkKey> srcLinks = createSynchronizedHashMultiMap();
+ private final SetMultimap<DeviceId, LinkKey> dstLinks = createSynchronizedHashMultiMap();
+
@Activate
public void activate() {
@@ -58,6 +79,10 @@
@Deactivate
public void deactivate() {
+ linkDescs.clear();
+ links.clear();
+ srcLinks.clear();
+ dstLinks.clear();
log.info("Stopped");
}
@@ -68,17 +93,29 @@
@Override
public Iterable<Link> getLinks() {
- return Collections.unmodifiableSet(new HashSet<Link>(links.values()));
+ return Collections.unmodifiableCollection(links.values());
}
@Override
public Set<Link> getDeviceEgressLinks(DeviceId deviceId) {
- return ImmutableSet.copyOf(srcLinks.get(deviceId));
+ // lock for iteration
+ synchronized (srcLinks) {
+ return FluentIterable.from(srcLinks.get(deviceId))
+ .transform(lookupLink())
+ .filter(notNull())
+ .toSet();
+ }
}
@Override
public Set<Link> getDeviceIngressLinks(DeviceId deviceId) {
- return ImmutableSet.copyOf(dstLinks.get(deviceId));
+ // lock for iteration
+ synchronized (dstLinks) {
+ return FluentIterable.from(dstLinks.get(deviceId))
+ .transform(lookupLink())
+ .filter(notNull())
+ .toSet();
+ }
}
@Override
@@ -89,9 +126,9 @@
@Override
public Set<Link> getEgressLinks(ConnectPoint src) {
Set<Link> egress = new HashSet<>();
- for (Link link : srcLinks.get(src.deviceId())) {
- if (link.src().equals(src)) {
- egress.add(link);
+ for (LinkKey linkKey : srcLinks.get(src.deviceId())) {
+ if (linkKey.src().equals(src)) {
+ egress.add(links.get(linkKey));
}
}
return egress;
@@ -100,9 +137,9 @@
@Override
public Set<Link> getIngressLinks(ConnectPoint dst) {
Set<Link> ingress = new HashSet<>();
- for (Link link : dstLinks.get(dst.deviceId())) {
- if (link.dst().equals(dst)) {
- ingress.add(link);
+ for (LinkKey linkKey : dstLinks.get(dst.deviceId())) {
+ if (linkKey.dst().equals(dst)) {
+ ingress.add(links.get(linkKey));
}
}
return ingress;
@@ -112,56 +149,172 @@
public LinkEvent createOrUpdateLink(ProviderId providerId,
LinkDescription linkDescription) {
LinkKey key = new LinkKey(linkDescription.src(), linkDescription.dst());
- DefaultLink link = links.get(key);
- if (link == null) {
- return createLink(providerId, key, linkDescription);
+
+ ConcurrentMap<ProviderId, LinkDescription> descs = getLinkDescriptions(key);
+ synchronized (descs) {
+ final Link oldLink = links.get(key);
+ // update description
+ createOrUpdateLinkDescription(descs, providerId, linkDescription);
+ final Link newLink = composeLink(descs);
+ if (oldLink == null) {
+ return createLink(key, newLink);
+ }
+ return updateLink(key, oldLink, newLink);
}
- return updateLink(providerId, link, key, linkDescription);
+ }
+
+ // Guarded by linkDescs value (=locking each Link)
+ private LinkDescription createOrUpdateLinkDescription(
+ ConcurrentMap<ProviderId, LinkDescription> descs,
+ ProviderId providerId,
+ LinkDescription linkDescription) {
+
+ // merge existing attributes and merge
+ LinkDescription oldDesc = descs.get(providerId);
+ LinkDescription newDesc = linkDescription;
+ if (oldDesc != null) {
+ SparseAnnotations merged = union(oldDesc.annotations(),
+ linkDescription.annotations());
+ newDesc = new DefaultLinkDescription(
+ linkDescription.src(),
+ linkDescription.dst(),
+ linkDescription.type(), merged);
+ }
+ return descs.put(providerId, newDesc);
}
// Creates and stores the link and returns the appropriate event.
- private LinkEvent createLink(ProviderId providerId, LinkKey key,
- LinkDescription linkDescription) {
- DefaultLink link = new DefaultLink(providerId, key.src(), key.dst(),
- linkDescription.type());
- synchronized (this) {
- links.put(key, link);
- srcLinks.put(link.src().deviceId(), link);
- dstLinks.put(link.dst().deviceId(), link);
+ // Guarded by linkDescs value (=locking each Link)
+ private LinkEvent createLink(LinkKey key, Link newLink) {
+
+ if (newLink.providerId().isAncillary()) {
+ // TODO: revisit ancillary only Link handling
+
+ // currently treating ancillary only as down (not visible outside)
+ return null;
}
- return new LinkEvent(LINK_ADDED, link);
+
+ links.put(key, newLink);
+ srcLinks.put(newLink.src().deviceId(), key);
+ dstLinks.put(newLink.dst().deviceId(), key);
+ return new LinkEvent(LINK_ADDED, newLink);
}
// Updates, if necessary the specified link and returns the appropriate event.
- private LinkEvent updateLink(ProviderId providerId, DefaultLink link,
- LinkKey key, LinkDescription linkDescription) {
- if (link.type() == INDIRECT && linkDescription.type() == DIRECT) {
- synchronized (this) {
- srcLinks.remove(link.src().deviceId(), link);
- dstLinks.remove(link.dst().deviceId(), link);
+ // Guarded by linkDescs value (=locking each Link)
+ private LinkEvent updateLink(LinkKey key, Link oldLink, Link newLink) {
- DefaultLink updated =
- new DefaultLink(providerId, link.src(), link.dst(),
- linkDescription.type());
- links.put(key, updated);
- srcLinks.put(link.src().deviceId(), updated);
- dstLinks.put(link.dst().deviceId(), updated);
- return new LinkEvent(LINK_UPDATED, updated);
- }
+ if (newLink.providerId().isAncillary()) {
+ // TODO: revisit ancillary only Link handling
+
+ // currently treating ancillary only as down (not visible outside)
+ return null;
+ }
+
+ if ((oldLink.type() == INDIRECT && newLink.type() == DIRECT) ||
+ !AnnotationsUtil.isEqual(oldLink.annotations(), newLink.annotations())) {
+
+ links.put(key, newLink);
+ // strictly speaking following can be ommitted
+ srcLinks.put(oldLink.src().deviceId(), key);
+ dstLinks.put(oldLink.dst().deviceId(), key);
+ return new LinkEvent(LINK_UPDATED, newLink);
}
return null;
}
@Override
public LinkEvent removeLink(ConnectPoint src, ConnectPoint dst) {
- synchronized (this) {
- Link link = links.remove(new LinkKey(src, dst));
+ final LinkKey key = new LinkKey(src, dst);
+ ConcurrentMap<ProviderId, LinkDescription> descs = getLinkDescriptions(key);
+ synchronized (descs) {
+ Link link = links.remove(key);
+ descs.clear();
if (link != null) {
- srcLinks.remove(link.src().deviceId(), link);
- dstLinks.remove(link.dst().deviceId(), link);
+ srcLinks.remove(link.src().deviceId(), key);
+ dstLinks.remove(link.dst().deviceId(), key);
return new LinkEvent(LINK_REMOVED, link);
}
return null;
}
}
+
+ private static <K, V> SetMultimap<K, V> createSynchronizedHashMultiMap() {
+ return synchronizedSetMultimap(HashMultimap.<K, V>create());
+ }
+
+ /**
+ * @return primary ProviderID, or randomly chosen one if none exists
+ */
+ private ProviderId pickPrimaryPID(
+ ConcurrentMap<ProviderId, LinkDescription> providerDescs) {
+
+ ProviderId fallBackPrimary = null;
+ for (Entry<ProviderId, LinkDescription> e : providerDescs.entrySet()) {
+ if (!e.getKey().isAncillary()) {
+ return e.getKey();
+ } else if (fallBackPrimary == null) {
+ // pick randomly as a fallback in case there is no primary
+ fallBackPrimary = e.getKey();
+ }
+ }
+ return fallBackPrimary;
+ }
+
+ private Link composeLink(ConcurrentMap<ProviderId, LinkDescription> descs) {
+ ProviderId primary = pickPrimaryPID(descs);
+ LinkDescription base = descs.get(primary);
+
+ ConnectPoint src = base.src();
+ ConnectPoint dst = base.dst();
+ Type type = base.type();
+ DefaultAnnotations annotations = DefaultAnnotations.builder().build();
+ annotations = merge(annotations, base.annotations());
+
+ for (Entry<ProviderId, LinkDescription> e : descs.entrySet()) {
+ if (primary.equals(e.getKey())) {
+ continue;
+ }
+
+ // TODO: should keep track of Description timestamp
+ // and only merge conflicting keys when timestamp is newer
+ // Currently assuming there will never be a key conflict between
+ // providers
+
+ // annotation merging. not so efficient, should revisit later
+ annotations = merge(annotations, e.getValue().annotations());
+ }
+
+ return new DefaultLink(primary , src, dst, type, annotations);
+ }
+
+ private ConcurrentMap<ProviderId, LinkDescription> getLinkDescriptions(LinkKey key) {
+ return ConcurrentUtils.createIfAbsentUnchecked(linkDescs, key,
+ NewConcurrentHashMap.<ProviderId, LinkDescription>ifNeeded());
+ }
+
+ private final Function<LinkKey, Link> lookupLink = new LookupLink();
+ private Function<LinkKey, Link> lookupLink() {
+ return lookupLink;
+ }
+
+ private final class LookupLink implements Function<LinkKey, Link> {
+ @Override
+ public Link apply(LinkKey input) {
+ return links.get(input);
+ }
+ }
+
+ private static final Predicate<Provided> IS_PRIMARY = new IsPrimary();
+ private static final Predicate<Provided> isPrimary() {
+ return IS_PRIMARY;
+ }
+
+ private static final class IsPrimary implements Predicate<Provided> {
+
+ @Override
+ public boolean apply(Provided input) {
+ return !input.providerId().isAncillary();
+ }
+ }
}
diff --git a/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/SimpleTopologyStore.java b/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/SimpleTopologyStore.java
index 4e7d5ed..bd7db8a 100644
--- a/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/SimpleTopologyStore.java
+++ b/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/SimpleTopologyStore.java
@@ -124,7 +124,8 @@
// Promote the new topology to current and return a ready-to-send event.
synchronized (this) {
current = newTopology;
- return new TopologyEvent(TopologyEvent.Type.TOPOLOGY_CHANGED, current);
+ return new TopologyEvent(TopologyEvent.Type.TOPOLOGY_CHANGED,
+ current, reasons);
}
}
diff --git a/core/store/trivial/src/test/java/org/onlab/onos/store/trivial/impl/SimpleDeviceStoreTest.java b/core/store/trivial/src/test/java/org/onlab/onos/store/trivial/impl/SimpleDeviceStoreTest.java
index a0d6e1c..146086a 100644
--- a/core/store/trivial/src/test/java/org/onlab/onos/store/trivial/impl/SimpleDeviceStoreTest.java
+++ b/core/store/trivial/src/test/java/org/onlab/onos/store/trivial/impl/SimpleDeviceStoreTest.java
@@ -103,17 +103,19 @@
simpleDeviceStore.deactivate();
}
- private void putDevice(DeviceId deviceId, String swVersion) {
+ private void putDevice(DeviceId deviceId, String swVersion,
+ SparseAnnotations... annotations) {
DeviceDescription description =
new DefaultDeviceDescription(deviceId.uri(), SWITCH, MFR,
- HW, swVersion, SN);
+ HW, swVersion, SN, annotations);
deviceStore.createOrUpdateDevice(PID, deviceId, description);
}
- private void putDeviceAncillary(DeviceId deviceId, String swVersion) {
+ private void putDeviceAncillary(DeviceId deviceId, String swVersion,
+ SparseAnnotations... annotations) {
DeviceDescription description =
new DefaultDeviceDescription(deviceId.uri(), SWITCH, MFR,
- HW, swVersion, SN);
+ HW, swVersion, SN, annotations);
deviceStore.createOrUpdateDevice(PIDA, deviceId, description);
}
@@ -126,6 +128,7 @@
assertEquals(SN, device.serialNumber());
}
+ // TODO slice this out somewhere
/**
* Verifies that Annotations created by merging {@code annotations} is
* equal to actual Annotations.
@@ -133,7 +136,7 @@
* @param actual Annotations to check
* @param annotations
*/
- private static void assertAnnotationsEquals(Annotations actual, SparseAnnotations... annotations) {
+ public static void assertAnnotationsEquals(Annotations actual, SparseAnnotations... annotations) {
DefaultAnnotations expected = DefaultAnnotations.builder().build();
for (SparseAnnotations a : annotations) {
expected = DefaultAnnotations.merge(expected, a);
@@ -347,6 +350,7 @@
assertFalse("Port is disabled", event.port().isEnabled());
}
+
@Test
public final void testUpdatePortStatusAncillary() {
putDeviceAncillary(DID1, SW1);
@@ -435,16 +439,37 @@
@Test
public final void testRemoveDevice() {
- putDevice(DID1, SW1);
+ putDevice(DID1, SW1, A1);
+ List<PortDescription> pds = Arrays.<PortDescription>asList(
+ new DefaultPortDescription(P1, true, A2)
+ );
+ deviceStore.updatePorts(PID, DID1, pds);
putDevice(DID2, SW1);
assertEquals(2, deviceStore.getDeviceCount());
+ assertEquals(1, deviceStore.getPorts(DID1).size());
+ assertAnnotationsEquals(deviceStore.getDevice(DID1).annotations(), A1);
+ assertAnnotationsEquals(deviceStore.getPort(DID1, P1).annotations(), A2);
DeviceEvent event = deviceStore.removeDevice(DID1);
assertEquals(DEVICE_REMOVED, event.type());
assertDevice(DID1, SW1, event.subject());
assertEquals(1, deviceStore.getDeviceCount());
+ assertEquals(0, deviceStore.getPorts(DID1).size());
+
+ // putBack Device, Port w/o annotation
+ putDevice(DID1, SW1);
+ List<PortDescription> pds2 = Arrays.<PortDescription>asList(
+ new DefaultPortDescription(P1, true)
+ );
+ deviceStore.updatePorts(PID, DID1, pds2);
+
+ // annotations should not survive
+ assertEquals(2, deviceStore.getDeviceCount());
+ assertEquals(1, deviceStore.getPorts(DID1).size());
+ assertAnnotationsEquals(deviceStore.getDevice(DID1).annotations());
+ assertAnnotationsEquals(deviceStore.getPort(DID1, P1).annotations());
}
// If Delegates should be called only on remote events,
diff --git a/core/store/trivial/src/test/java/org/onlab/onos/store/trivial/impl/SimpleLinkStoreTest.java b/core/store/trivial/src/test/java/org/onlab/onos/store/trivial/impl/SimpleLinkStoreTest.java
index eb4a312..8a16609 100644
--- a/core/store/trivial/src/test/java/org/onlab/onos/store/trivial/impl/SimpleLinkStoreTest.java
+++ b/core/store/trivial/src/test/java/org/onlab/onos/store/trivial/impl/SimpleLinkStoreTest.java
@@ -4,7 +4,9 @@
import static org.onlab.onos.net.DeviceId.deviceId;
import static org.onlab.onos.net.Link.Type.*;
import static org.onlab.onos.net.link.LinkEvent.Type.*;
+import static org.onlab.onos.store.trivial.impl.SimpleDeviceStoreTest.assertAnnotationsEquals;
+import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
@@ -18,10 +20,12 @@
import org.junit.Ignore;
import org.junit.Test;
import org.onlab.onos.net.ConnectPoint;
+import org.onlab.onos.net.DefaultAnnotations;
import org.onlab.onos.net.DeviceId;
import org.onlab.onos.net.Link;
import org.onlab.onos.net.LinkKey;
import org.onlab.onos.net.PortNumber;
+import org.onlab.onos.net.SparseAnnotations;
import org.onlab.onos.net.Link.Type;
import org.onlab.onos.net.link.DefaultLinkDescription;
import org.onlab.onos.net.link.LinkEvent;
@@ -37,6 +41,7 @@
public class SimpleLinkStoreTest {
private static final ProviderId PID = new ProviderId("of", "foo");
+ private static final ProviderId PIDA = new ProviderId("of", "bar", true);
private static final DeviceId DID1 = deviceId("of:foo");
private static final DeviceId DID2 = deviceId("of:bar");
@@ -44,6 +49,23 @@
private static final PortNumber P2 = PortNumber.portNumber(2);
private static final PortNumber P3 = PortNumber.portNumber(3);
+ private static final SparseAnnotations A1 = DefaultAnnotations.builder()
+ .set("A1", "a1")
+ .set("B1", "b1")
+ .build();
+ private static final SparseAnnotations A1_2 = DefaultAnnotations.builder()
+ .remove("A1")
+ .set("B3", "b3")
+ .build();
+ private static final SparseAnnotations A2 = DefaultAnnotations.builder()
+ .set("A2", "a2")
+ .set("B2", "b2")
+ .build();
+ private static final SparseAnnotations A2_2 = DefaultAnnotations.builder()
+ .remove("A2")
+ .set("B4", "b4")
+ .build();
+
private SimpleLinkStore simpleLinkStore;
private LinkStore linkStore;
@@ -69,16 +91,17 @@
}
private void putLink(DeviceId srcId, PortNumber srcNum,
- DeviceId dstId, PortNumber dstNum, Type type) {
+ DeviceId dstId, PortNumber dstNum, Type type,
+ SparseAnnotations... annotations) {
ConnectPoint src = new ConnectPoint(srcId, srcNum);
ConnectPoint dst = new ConnectPoint(dstId, dstNum);
- linkStore.createOrUpdateLink(PID, new DefaultLinkDescription(src, dst, type));
+ linkStore.createOrUpdateLink(PID, new DefaultLinkDescription(src, dst, type, annotations));
}
- private void putLink(LinkKey key, Type type) {
+ private void putLink(LinkKey key, Type type, SparseAnnotations... annotations) {
putLink(key.src().deviceId(), key.src().port(),
key.dst().deviceId(), key.dst().port(),
- type);
+ type, annotations);
}
private static void assertLink(DeviceId srcId, PortNumber srcNum,
@@ -270,14 +293,67 @@
}
@Test
+ public final void testCreateOrUpdateLinkAncillary() {
+ ConnectPoint src = new ConnectPoint(DID1, P1);
+ ConnectPoint dst = new ConnectPoint(DID2, P2);
+
+ // add Ancillary link
+ LinkEvent event = linkStore.createOrUpdateLink(PIDA,
+ new DefaultLinkDescription(src, dst, INDIRECT, A1));
+
+ assertNull("Ancillary only link is ignored", event);
+
+ // add Primary link
+ LinkEvent event2 = linkStore.createOrUpdateLink(PID,
+ new DefaultLinkDescription(src, dst, INDIRECT, A2));
+
+ assertLink(DID1, P1, DID2, P2, INDIRECT, event2.subject());
+ assertAnnotationsEquals(event2.subject().annotations(), A2, A1);
+ assertEquals(LINK_ADDED, event2.type());
+
+ // update link type
+ LinkEvent event3 = linkStore.createOrUpdateLink(PID,
+ new DefaultLinkDescription(src, dst, DIRECT, A2));
+ assertLink(DID1, P1, DID2, P2, DIRECT, event3.subject());
+ assertAnnotationsEquals(event3.subject().annotations(), A2, A1);
+ assertEquals(LINK_UPDATED, event3.type());
+
+
+ // no change
+ LinkEvent event4 = linkStore.createOrUpdateLink(PID,
+ new DefaultLinkDescription(src, dst, DIRECT));
+ assertNull("No change event expected", event4);
+
+ // update link annotation (Primary)
+ LinkEvent event5 = linkStore.createOrUpdateLink(PID,
+ new DefaultLinkDescription(src, dst, DIRECT, A2_2));
+ assertLink(DID1, P1, DID2, P2, DIRECT, event5.subject());
+ assertAnnotationsEquals(event5.subject().annotations(), A2, A2_2, A1);
+ assertEquals(LINK_UPDATED, event5.type());
+
+ // update link annotation (Ancillary)
+ LinkEvent event6 = linkStore.createOrUpdateLink(PIDA,
+ new DefaultLinkDescription(src, dst, DIRECT, A1_2));
+ assertLink(DID1, P1, DID2, P2, DIRECT, event6.subject());
+ assertAnnotationsEquals(event6.subject().annotations(), A2, A2_2, A1, A1_2);
+ assertEquals(LINK_UPDATED, event6.type());
+
+ // update link type (Ancillary) : ignored
+ LinkEvent event7 = linkStore.createOrUpdateLink(PIDA,
+ new DefaultLinkDescription(src, dst, EDGE));
+ assertNull("Ancillary change other than annotation is ignored", event7);
+ }
+
+
+ @Test
public final void testRemoveLink() {
final ConnectPoint d1P1 = new ConnectPoint(DID1, P1);
final ConnectPoint d2P2 = new ConnectPoint(DID2, P2);
LinkKey linkId1 = new LinkKey(d1P1, d2P2);
LinkKey linkId2 = new LinkKey(d2P2, d1P1);
- putLink(linkId1, DIRECT);
- putLink(linkId2, DIRECT);
+ putLink(linkId1, DIRECT, A1);
+ putLink(linkId2, DIRECT, A2);
// DID1,P1 => DID2,P2
// DID2,P2 => DID1,P1
@@ -285,10 +361,41 @@
LinkEvent event = linkStore.removeLink(d1P1, d2P2);
assertEquals(LINK_REMOVED, event.type());
+ assertAnnotationsEquals(event.subject().annotations(), A1);
LinkEvent event2 = linkStore.removeLink(d1P1, d2P2);
assertNull(event2);
assertLink(linkId2, DIRECT, linkStore.getLink(d2P2, d1P1));
+ assertAnnotationsEquals(linkStore.getLink(d2P2, d1P1).annotations(), A2);
+
+ // annotations, etc. should not survive remove
+ putLink(linkId1, DIRECT);
+ assertLink(linkId1, DIRECT, linkStore.getLink(d1P1, d2P2));
+ assertAnnotationsEquals(linkStore.getLink(d1P1, d2P2).annotations());
+ }
+
+ @Test
+ public final void testAncillaryOnlyNotVisible() {
+ ConnectPoint src = new ConnectPoint(DID1, P1);
+ ConnectPoint dst = new ConnectPoint(DID2, P2);
+
+ // add Ancillary link
+ linkStore.createOrUpdateLink(PIDA,
+ new DefaultLinkDescription(src, dst, INDIRECT, A1));
+
+ // Ancillary only link should not be visible
+ assertEquals(0, linkStore.getLinkCount());
+
+ assertTrue(Iterables.isEmpty(linkStore.getLinks()));
+
+ assertNull(linkStore.getLink(src, dst));
+
+ assertEquals(Collections.emptySet(), linkStore.getIngressLinks(dst));
+
+ assertEquals(Collections.emptySet(), linkStore.getEgressLinks(src));
+
+ assertEquals(Collections.emptySet(), linkStore.getDeviceEgressLinks(DID1));
+ assertEquals(Collections.emptySet(), linkStore.getDeviceIngressLinks(DID2));
}
// If Delegates should be called only on remote events,
diff --git a/features/features.xml b/features/features.xml
index 68fa8c3..b240917 100644
--- a/features/features.xml
+++ b/features/features.xml
@@ -9,6 +9,12 @@
<bundle>mvn:org.apache.commons/commons-lang3/3.3.2</bundle>
<bundle>mvn:com.google.guava/guava/18.0</bundle>
<bundle>mvn:io.netty/netty/3.9.2.Final</bundle>
+ <bundle>mvn:io.netty/netty-common/4.0.23.Final</bundle>
+ <bundle>mvn:io.netty/netty-buffer/4.0.23.Final</bundle>
+ <bundle>mvn:io.netty/netty-transport/4.0.23.Final</bundle>
+ <bundle>mvn:io.netty/netty-handler/4.0.23.Final</bundle>
+ <bundle>mvn:io.netty/netty-codec/4.0.23.Final</bundle>
+ <bundle>mvn:commons-pool/commons-pool/1.6</bundle>
<bundle>mvn:com.hazelcast/hazelcast/3.3</bundle>
<bundle>mvn:io.dropwizard.metrics/metrics-core/3.1.0</bundle>
@@ -53,6 +59,11 @@
<feature>onos-api</feature>
<bundle>mvn:org.onlab.onos/onos-core-net/1.0.0-SNAPSHOT</bundle>
<bundle>mvn:org.onlab.onos/onos-core-dist/1.0.0-SNAPSHOT</bundle>
+ <bundle>mvn:org.onlab.onos/onos-core-serializers/1.0.0-SNAPSHOT</bundle>
+ <bundle>mvn:org.onlab.onos/onlab-netty/1.0.0-SNAPSHOT</bundle>
+
+ <bundle>mvn:org.onlab.onos/onos-core-hz-common/1.0.0-SNAPSHOT</bundle>
+ <bundle>mvn:org.onlab.onos/onos-core-hz-cluster/1.0.0-SNAPSHOT</bundle>
</feature>
<feature name="onos-core-hazelcast" version="1.0.0"
@@ -120,18 +131,29 @@
<bundle>mvn:org.onlab.onos/onos-app-fwd/1.0.0-SNAPSHOT</bundle>
</feature>
+ <feature name="onos-app-ifwd" version="1.0.0"
+ description="ONOS sample forwarding application using intents">
+ <feature>onos-api</feature>
+ <bundle>mvn:org.onlab.onos/onos-app-ifwd/1.0.0-SNAPSHOT</bundle>
+ </feature>
+
<feature name="onos-app-mobility" version="1.0.0"
- description="ONOS sample forwarding application">
+ description="ONOS sample mobility application">
<feature>onos-api</feature>
<bundle>mvn:org.onlab.onos/onos-app-mobility/1.0.0-SNAPSHOT</bundle>
</feature>
-
+ <feature name="onos-app-proxyarp" version="1.0.0"
+ description="ONOS sample proxyarp application">
+ <feature>onos-api</feature>
+ <bundle>mvn:org.onlab.onos/onos-app-proxyarp/1.0.0-SNAPSHOT</bundle>
+ </feature>
<feature name="onos-app-foo" version="1.0.0"
description="ONOS sample playground application">
<feature>onos-api</feature>
<bundle>mvn:org.onlab.onos/onos-app-foo/1.0.0-SNAPSHOT</bundle>
+ <bundle>mvn:org.onlab.onos/onlab-netty/1.0.0-SNAPSHOT</bundle>
</feature>
<feature name="onos-app-config" version="1.0.0"
diff --git a/features/old-features.xml b/features/old-features.xml
deleted file mode 100644
index 46e68ca..0000000
--- a/features/old-features.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
-<!--
- ~ Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
- ~
- ~ This program and the accompanying materials are made available under the
- ~ terms of the Eclipse Public License v1.0 which accompanies this distribution,
- ~ and is available at http://www.eclipse.org/legal/epl-v10.html
- -->
-
-<features xmlns="http://karaf.apache.org/xmlns/features/v1.2.0"
- name="net.onrc.onos-1.0.0">
- <repository>mvn:net.onrc.onos/onos-features/1.0.0-SNAPSHOT/xml/features</repository>
-
- <feature name="thirdparty" version="1.0.0"
- description="ONOS 3rd party dependencies">
- <bundle>mvn:com.google.code.findbugs/annotations/2.0.2</bundle>
- <bundle>mvn:io.netty/netty/3.9.2.Final</bundle>
- <bundle>mvn:com.google.guava/guava/17.0</bundle>
- <bundle>mvn:com.google.guava/guava/15.0</bundle>
-
- </feature>
-
- <feature name="base" version="1.0.0"
- description="ONOS Base">
- <feature>scr</feature>
- <feature>thirdparty</feature>
- <bundle>mvn:net.onrc.onos.sb/onos-sb/0.0.1</bundle>
- <bundle>mvn:org.projectfloodlight/openflowj/0.3.6-SNAPSHOT</bundle>
- </feature>
-
-</features>
diff --git a/openflow/api/pom.xml b/openflow/api/pom.xml
index 2c58e47..afc2faf 100644
--- a/openflow/api/pom.xml
+++ b/openflow/api/pom.xml
@@ -17,14 +17,11 @@
<description>ONOS OpenFlow controller subsystem API</description>
<repositories>
- <!-- FIXME: for Loxigen. Decide how to use Loxigen before release. -->
+ <!-- FIXME: for Loxigen + optical experimenter. Decide how to use Loxigen before release. -->
<repository>
- <id>sonatype-oss-snapshot</id>
- <name>Sonatype OSS snapshot repository</name>
- <url>https://oss.sonatype.org/content/repositories/snapshots</url>
- <releases>
- <enabled>false</enabled>
- </releases>
+ <id>onlab-temp</id>
+ <name>ON.lab temporary repository</name>
+ <url>http://mavenrepo.onlab.us:8081/nexus/content/repositories/releases</url>
</repository>
</repositories>
@@ -32,7 +29,8 @@
<dependency>
<groupId>org.projectfloodlight</groupId>
<artifactId>openflowj</artifactId>
- <version>0.3.8-SNAPSHOT</version>
+ <!-- FIXME once experimenter gets merged to upstream -->
+ <version>0.3.8-optical_experimenter</version>
</dependency>
<dependency>
<groupId>io.netty</groupId>
diff --git a/openflow/ctl/src/main/java/org/onlab/onos/openflow/controller/impl/OFChannelHandler.java b/openflow/ctl/src/main/java/org/onlab/onos/openflow/controller/impl/OFChannelHandler.java
index 0a3ee86..5be7c69 100644
--- a/openflow/ctl/src/main/java/org/onlab/onos/openflow/controller/impl/OFChannelHandler.java
+++ b/openflow/ctl/src/main/java/org/onlab/onos/openflow/controller/impl/OFChannelHandler.java
@@ -167,11 +167,11 @@
// TODO We could check for the optional bitmap, but for now
// we are just checking the version number.
if (m.getVersion() == OFVersion.OF_13) {
- log.info("Received {} Hello from {}", m.getVersion(),
+ log.debug("Received {} Hello from {}", m.getVersion(),
h.channel.getRemoteAddress());
h.ofVersion = OFVersion.OF_13;
} else if (m.getVersion() == OFVersion.OF_10) {
- log.info("Received {} Hello from {} - switching to OF "
+ log.debug("Received {} Hello from {} - switching to OF "
+ "version 1.0", m.getVersion(),
h.channel.getRemoteAddress());
h.ofVersion = OFVersion.OF_10;
@@ -222,7 +222,7 @@
void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
throws IOException {
h.thisdpid = m.getDatapathId().getLong();
- log.info("Received features reply for switch at {} with dpid {}",
+ log.debug("Received features reply for switch at {} with dpid {}",
h.getSwitchInfoString(), h.thisdpid);
h.featuresReply = m; //temp store
@@ -409,7 +409,7 @@
- log.info("Switch {} bound to class {}, description {}",
+ log.debug("Switch {} bound to class {}, description {}",
new Object[] {h.sw, h.sw.getClass(), drep });
//Put switch in EQUAL mode until we hear back from the global registry
//log.debug("Setting new switch {} to EQUAL and sending Role request",
@@ -651,7 +651,7 @@
* @param error The error message
*/
protected void logError(OFChannelHandler h, OFErrorMsg error) {
- log.info("{} from switch {} in state {}",
+ log.error("{} from switch {} in state {}",
new Object[] {
error,
h.getSwitchInfoString(),
@@ -1052,7 +1052,7 @@
throws Exception {
OFFactory factory = (ofVersion == OFVersion.OF_13) ? factory13 : factory10;
OFMessage m = factory.buildEchoRequest().build();
- log.info("Sending Echo Request on idle channel: {}",
+ log.debug("Sending Echo Request on idle channel: {}",
e.getChannel().getPipeline().getLast().toString());
e.getChannel().write(Collections.singletonList(m));
// XXX S some problems here -- echo request has no transaction id, and
diff --git a/openflow/ctl/src/main/java/org/onlab/onos/openflow/controller/impl/RoleManager.java b/openflow/ctl/src/main/java/org/onlab/onos/openflow/controller/impl/RoleManager.java
index b47bada..992270f 100644
--- a/openflow/ctl/src/main/java/org/onlab/onos/openflow/controller/impl/RoleManager.java
+++ b/openflow/ctl/src/main/java/org/onlab/onos/openflow/controller/impl/RoleManager.java
@@ -122,7 +122,7 @@
//FIXME fix below when we actually use generation ids
.setGenerationId(U64.ZERO)
.build();
- sw.sendMsg(rrm);
+ sw.write(rrm);
return xid;
}
diff --git a/openflow/ctl/src/main/java/org/onlab/onos/openflow/drivers/impl/OFSwitchImplOVS13.java b/openflow/ctl/src/main/java/org/onlab/onos/openflow/drivers/impl/OFSwitchImplOVS13.java
index 05ffdd1..6bb4586 100644
--- a/openflow/ctl/src/main/java/org/onlab/onos/openflow/drivers/impl/OFSwitchImplOVS13.java
+++ b/openflow/ctl/src/main/java/org/onlab/onos/openflow/drivers/impl/OFSwitchImplOVS13.java
@@ -138,7 +138,7 @@
.buildBarrierRequest()
.setXid(xid)
.build();
- sendMsg(br);
+ write(br);
}
@Override
@@ -227,7 +227,7 @@
.setHardTimeout(0)
.setXid(getNextTransactionId())
.build();
- sendMsg(tableMissEntry);
+ write(tableMissEntry);
}
}
diff --git a/openflow/openflowj/gen-src/main/java/org/projectfloodlight/openflow/protocol/OFType.java b/openflow/openflowj/gen-src/main/java/org/projectfloodlight/openflow/protocol/OFType.java
index aef779f..51de582 100644
--- a/openflow/openflowj/gen-src/main/java/org/projectfloodlight/openflow/protocol/OFType.java
+++ b/openflow/openflowj/gen-src/main/java/org/projectfloodlight/openflow/protocol/OFType.java
@@ -40,15 +40,15 @@
PORT_STATUS,
PACKET_OUT,
FLOW_MOD,
+ GROUP_MOD,
PORT_MOD,
+ TABLE_MOD,
STATS_REQUEST,
STATS_REPLY,
BARRIER_REQUEST,
BARRIER_REPLY,
QUEUE_GET_CONFIG_REQUEST,
QUEUE_GET_CONFIG_REPLY,
- GROUP_MOD,
- TABLE_MOD,
ROLE_REQUEST,
ROLE_REPLY,
GET_ASYNC_REQUEST,
diff --git a/pom.xml b/pom.xml
index cb00f32..ad4ddcb 100644
--- a/pom.xml
+++ b/pom.xml
@@ -69,6 +69,13 @@
<dependency>
<groupId>org.slf4j</groupId>
+ <artifactId>slf4j-core</artifactId>
+ <version>1.7.6</version>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.slf4j</groupId>
<artifactId>slf4j-jdk14</artifactId>
<version>1.7.6</version>
<scope>test</scope>
@@ -122,6 +129,12 @@
<version>1.9.13</version>
</dependency>
+ <dependency>
+ <groupId>org.easymock</groupId>
+ <artifactId>easymock</artifactId>
+ <version>3.2</version>
+ <scope>test</scope>
+ </dependency>
<!-- Web related -->
<dependency>
@@ -480,7 +493,7 @@
<group>
<title>Core Subsystems</title>
<packages>
- 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.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
</packages>
</group>
<group>
diff --git a/providers/openflow/flow/src/main/java/org/onlab/onos/provider/of/flow/impl/FlowRuleBuilder.java b/providers/openflow/flow/src/main/java/org/onlab/onos/provider/of/flow/impl/FlowEntryBuilder.java
similarity index 78%
rename from providers/openflow/flow/src/main/java/org/onlab/onos/provider/of/flow/impl/FlowRuleBuilder.java
rename to providers/openflow/flow/src/main/java/org/onlab/onos/provider/of/flow/impl/FlowEntryBuilder.java
index eba2282..14c2c22 100644
--- a/providers/openflow/flow/src/main/java/org/onlab/onos/provider/of/flow/impl/FlowRuleBuilder.java
+++ b/providers/openflow/flow/src/main/java/org/onlab/onos/provider/of/flow/impl/FlowEntryBuilder.java
@@ -6,11 +6,13 @@
import org.onlab.onos.net.DeviceId;
import org.onlab.onos.net.PortNumber;
+import org.onlab.onos.net.flow.DefaultFlowEntry;
import org.onlab.onos.net.flow.DefaultFlowRule;
import org.onlab.onos.net.flow.DefaultTrafficSelector;
import org.onlab.onos.net.flow.DefaultTrafficTreatment;
+import org.onlab.onos.net.flow.FlowEntry;
+import org.onlab.onos.net.flow.FlowEntry.FlowEntryState;
import org.onlab.onos.net.flow.FlowRule;
-import org.onlab.onos.net.flow.FlowRule.FlowRuleState;
import org.onlab.onos.net.flow.TrafficSelector;
import org.onlab.onos.net.flow.TrafficTreatment;
import org.onlab.onos.openflow.controller.Dpid;
@@ -18,8 +20,8 @@
import org.onlab.packet.MacAddress;
import org.onlab.packet.VlanId;
import org.projectfloodlight.openflow.protocol.OFFlowRemoved;
-import org.projectfloodlight.openflow.protocol.OFFlowRemovedReason;
import org.projectfloodlight.openflow.protocol.OFFlowStatsEntry;
+import org.projectfloodlight.openflow.protocol.OFInstructionType;
import org.projectfloodlight.openflow.protocol.action.OFAction;
import org.projectfloodlight.openflow.protocol.action.OFActionOutput;
import org.projectfloodlight.openflow.protocol.action.OFActionSetDlDst;
@@ -28,12 +30,16 @@
import org.projectfloodlight.openflow.protocol.action.OFActionSetNwSrc;
import org.projectfloodlight.openflow.protocol.action.OFActionSetVlanPcp;
import org.projectfloodlight.openflow.protocol.action.OFActionSetVlanVid;
+import org.projectfloodlight.openflow.protocol.instruction.OFInstruction;
+import org.projectfloodlight.openflow.protocol.instruction.OFInstructionApplyActions;
import org.projectfloodlight.openflow.protocol.match.Match;
import org.projectfloodlight.openflow.protocol.match.MatchField;
import org.projectfloodlight.openflow.types.IPv4Address;
import org.slf4j.Logger;
-public class FlowRuleBuilder {
+import com.google.common.collect.Lists;
+
+public class FlowEntryBuilder {
private final Logger log = getLogger(getClass());
private final OFFlowStatsEntry stat;
@@ -44,49 +50,70 @@
private final Dpid dpid;
+ private final boolean addedRule;
-
- public FlowRuleBuilder(Dpid dpid, OFFlowStatsEntry entry) {
+ public FlowEntryBuilder(Dpid dpid, OFFlowStatsEntry entry) {
this.stat = entry;
this.match = entry.getMatch();
- this.actions = entry.getActions();
+ this.actions = getActions(entry);
this.dpid = dpid;
this.removed = null;
+ this.addedRule = true;
}
- public FlowRuleBuilder(Dpid dpid, OFFlowRemoved removed) {
+ public FlowEntryBuilder(Dpid dpid, OFFlowRemoved removed) {
this.match = removed.getMatch();
this.removed = removed;
this.dpid = dpid;
this.actions = null;
this.stat = null;
+ this.addedRule = false;
}
- public FlowRule build() {
- if (stat != null) {
- return new DefaultFlowRule(DeviceId.deviceId(Dpid.uri(dpid)),
+ public FlowEntry build() {
+ if (addedRule) {
+ FlowRule rule = new DefaultFlowRule(DeviceId.deviceId(Dpid.uri(dpid)),
buildSelector(), buildTreatment(), stat.getPriority(),
- FlowRuleState.ADDED, stat.getDurationNsec() / 1000000,
- stat.getPacketCount().getValue(), stat.getByteCount().getValue(),
- stat.getCookie().getValue(), false, stat.getIdleTimeout());
+ stat.getCookie().getValue(), stat.getIdleTimeout());
+ return new DefaultFlowEntry(rule, FlowEntryState.ADDED,
+ stat.getDurationSec(), stat.getPacketCount().getValue(),
+ stat.getByteCount().getValue());
+
} else {
- // TODO: revisit potentially.
- return new DefaultFlowRule(DeviceId.deviceId(Dpid.uri(dpid)),
+ FlowRule rule = new DefaultFlowRule(DeviceId.deviceId(Dpid.uri(dpid)),
buildSelector(), null, removed.getPriority(),
- FlowRuleState.REMOVED, removed.getDurationNsec() / 1000000,
- removed.getPacketCount().getValue(), removed.getByteCount().getValue(),
- removed.getCookie().getValue(),
- removed.getReason() == OFFlowRemovedReason.IDLE_TIMEOUT.ordinal(),
- stat.getIdleTimeout());
+ removed.getCookie().getValue(), removed.getIdleTimeout());
+ return new DefaultFlowEntry(rule, FlowEntryState.REMOVED, removed.getDurationSec(),
+ removed.getPacketCount().getValue(), removed.getByteCount().getValue());
}
}
+ private List<OFAction> getActions(OFFlowStatsEntry entry) {
+ switch (entry.getVersion()) {
+ case OF_10:
+ return entry.getActions();
+ case OF_11:
+ case OF_12:
+ case OF_13:
+ List<OFInstruction> ins = entry.getInstructions();
+ for (OFInstruction in : ins) {
+ if (in.getType().equals(OFInstructionType.APPLY_ACTIONS)) {
+ OFInstructionApplyActions apply = (OFInstructionApplyActions) in;
+ return apply.getActions();
+ }
+ }
+ return Lists.newLinkedList();
+ default:
+ log.warn("Unknown OF version {}", entry.getVersion());
+ }
+ return Lists.newLinkedList();
+ }
private TrafficTreatment buildTreatment() {
- TrafficTreatment.Builder builder = new DefaultTrafficTreatment.Builder();
+ TrafficTreatment.Builder builder = DefaultTrafficTreatment.builder();
// If this is a drop rule
if (actions.size() == 0) {
builder.drop();
@@ -171,7 +198,7 @@
}
private TrafficSelector buildSelector() {
- TrafficSelector.Builder builder = new DefaultTrafficSelector.Builder();
+ TrafficSelector.Builder builder = DefaultTrafficSelector.builder();
for (MatchField<?> field : match.getMatchFields()) {
switch (field.id) {
case IN_PORT:
diff --git a/providers/openflow/flow/src/main/java/org/onlab/onos/provider/of/flow/impl/FlowModBuilder.java b/providers/openflow/flow/src/main/java/org/onlab/onos/provider/of/flow/impl/FlowModBuilder.java
index ade651e..78f5874 100644
--- a/providers/openflow/flow/src/main/java/org/onlab/onos/provider/of/flow/impl/FlowModBuilder.java
+++ b/providers/openflow/flow/src/main/java/org/onlab/onos/provider/of/flow/impl/FlowModBuilder.java
@@ -68,7 +68,7 @@
this.cookie = flowRule.id();
}
- public OFFlowMod buildFlowMod() {
+ public OFFlowMod buildFlowAdd() {
Match match = buildMatch();
List<OFAction> actions = buildActions();
@@ -86,6 +86,24 @@
}
+ public OFFlowMod buildFlowMod() {
+ Match match = buildMatch();
+ List<OFAction> actions = buildActions();
+
+ //TODO: what to do without bufferid? do we assume that there will be a pktout as well?
+ OFFlowMod fm = factory.buildFlowModify()
+ .setCookie(U64.of(cookie.value()))
+ .setBufferId(OFBufferId.NO_BUFFER)
+ .setActions(actions)
+ .setMatch(match)
+ .setFlags(Collections.singleton(OFFlowModFlags.SEND_FLOW_REM))
+ .setPriority(priority)
+ .build();
+
+ return fm;
+
+ }
+
public OFFlowMod buildFlowDel() {
Match match = buildMatch();
List<OFAction> actions = buildActions();
diff --git a/providers/openflow/flow/src/main/java/org/onlab/onos/provider/of/flow/impl/OpenFlowRuleProvider.java b/providers/openflow/flow/src/main/java/org/onlab/onos/provider/of/flow/impl/OpenFlowRuleProvider.java
index 24a7ea8..19cfb6a 100644
--- a/providers/openflow/flow/src/main/java/org/onlab/onos/provider/of/flow/impl/OpenFlowRuleProvider.java
+++ b/providers/openflow/flow/src/main/java/org/onlab/onos/provider/of/flow/impl/OpenFlowRuleProvider.java
@@ -2,7 +2,17 @@
import static org.slf4j.LoggerFactory.getLogger;
+import java.util.HashSet;
+import java.util.List;
import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicBoolean;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
@@ -11,10 +21,13 @@
import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.onlab.onos.ApplicationId;
import org.onlab.onos.net.DeviceId;
+import org.onlab.onos.net.flow.FlowEntry;
import org.onlab.onos.net.flow.FlowRule;
+import org.onlab.onos.net.flow.FlowRuleBatchEntry;
import org.onlab.onos.net.flow.FlowRuleProvider;
import org.onlab.onos.net.flow.FlowRuleProviderRegistry;
import org.onlab.onos.net.flow.FlowRuleProviderService;
+import org.onlab.onos.net.intent.BatchOperation;
import org.onlab.onos.net.provider.AbstractProvider;
import org.onlab.onos.net.provider.ProviderId;
import org.onlab.onos.net.topology.TopologyService;
@@ -24,17 +37,29 @@
import org.onlab.onos.openflow.controller.OpenFlowSwitch;
import org.onlab.onos.openflow.controller.OpenFlowSwitchListener;
import org.onlab.onos.openflow.controller.RoleState;
+import org.projectfloodlight.openflow.protocol.OFActionType;
+import org.projectfloodlight.openflow.protocol.OFBarrierRequest;
+import org.projectfloodlight.openflow.protocol.OFErrorMsg;
import org.projectfloodlight.openflow.protocol.OFFlowRemoved;
import org.projectfloodlight.openflow.protocol.OFFlowStatsEntry;
import org.projectfloodlight.openflow.protocol.OFFlowStatsReply;
+import org.projectfloodlight.openflow.protocol.OFInstructionType;
import org.projectfloodlight.openflow.protocol.OFMessage;
import org.projectfloodlight.openflow.protocol.OFPortStatus;
import org.projectfloodlight.openflow.protocol.OFStatsReply;
import org.projectfloodlight.openflow.protocol.OFStatsReplyFlags;
import org.projectfloodlight.openflow.protocol.OFStatsType;
+import org.projectfloodlight.openflow.protocol.OFVersion;
+import org.projectfloodlight.openflow.protocol.action.OFAction;
+import org.projectfloodlight.openflow.protocol.action.OFActionOutput;
+import org.projectfloodlight.openflow.protocol.instruction.OFInstruction;
+import org.projectfloodlight.openflow.protocol.instruction.OFInstructionApplyActions;
+import org.projectfloodlight.openflow.types.OFPort;
+import org.projectfloodlight.openflow.types.U32;
import org.slf4j.Logger;
import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
@@ -60,6 +85,9 @@
private final InternalFlowProvider listener = new InternalFlowProvider();
+ private final Map<Long, InstallationFuture> pendingFutures =
+ new ConcurrentHashMap<Long, InstallationFuture>();
+
/**
* Creates an OpenFlow host provider.
*/
@@ -91,7 +119,7 @@
private void applyRule(FlowRule flowRule) {
OpenFlowSwitch sw = controller.getSwitch(Dpid.dpid(flowRule.deviceId().uri()));
- sw.sendMsg(new FlowModBuilder(flowRule, sw.factory()).buildFlowMod());
+ sw.sendMsg(new FlowModBuilder(flowRule, sw.factory()).buildFlowAdd());
}
@@ -122,7 +150,7 @@
implements OpenFlowSwitchListener, OpenFlowEventListener {
private final Map<Dpid, FlowStatsCollector> collectors = Maps.newHashMap();
- private final Multimap<DeviceId, FlowRule> completeEntries =
+ private final Multimap<DeviceId, FlowEntry> completeEntries =
ArrayListMultimap.create();
@Override
@@ -144,19 +172,30 @@
@Override
public void handleMessage(Dpid dpid, OFMessage msg) {
+ InstallationFuture future = null;
switch (msg.getType()) {
case FLOW_REMOVED:
//TODO: make this better
OFFlowRemoved removed = (OFFlowRemoved) msg;
- FlowRule fr = new FlowRuleBuilder(dpid, removed).build();
+ FlowEntry fr = new FlowEntryBuilder(dpid, removed).build();
providerService.flowRemoved(fr);
break;
case STATS_REPLY:
pushFlowMetrics(dpid, (OFStatsReply) msg);
break;
case BARRIER_REPLY:
+ future = pendingFutures.get(msg.getXid());
+ if (future != null) {
+ future.satisfyRequirement(dpid);
+ }
+ break;
case ERROR:
+ future = pendingFutures.get(msg.getXid());
+ if (future != null) {
+ future.fail((OFErrorMsg) msg, dpid);
+ }
+ break;
default:
log.debug("Unhandled message type: {}", msg.getType());
}
@@ -178,8 +217,9 @@
//final List<FlowRule> entries = Lists.newLinkedList();
for (OFFlowStatsEntry reply : replies.getEntries()) {
- completeEntries.put(did, new FlowRuleBuilder(dpid, reply).build());
- //entries.add(new FlowRuleBuilder(dpid, reply).build());
+ if (!tableMissRule(dpid, reply)) {
+ completeEntries.put(did, new FlowEntryBuilder(dpid, reply).build());
+ }
}
if (!stats.getFlags().contains(OFStatsReplyFlags.REPLY_MORE)) {
@@ -189,9 +229,170 @@
}
}
+ private boolean tableMissRule(Dpid dpid, OFFlowStatsEntry reply) {
+ // TODO NEED TO FIND A BETTER WAY TO AVOID DOING THIS
+ if (reply.getVersion().equals(OFVersion.OF_10) ||
+ reply.getMatch().getMatchFields().iterator().hasNext()) {
+ return false;
+ }
+ for (OFInstruction ins : reply.getInstructions()) {
+ if (ins.getType() == OFInstructionType.APPLY_ACTIONS) {
+ OFInstructionApplyActions apply = (OFInstructionApplyActions) ins;
+ List<OFAction> acts = apply.getActions();
+ for (OFAction act : acts) {
+ if (act.getType() == OFActionType.OUTPUT) {
+ OFActionOutput out = (OFActionOutput) act;
+ if (out.getPort() == OFPort.CONTROLLER) {
+ return true;
+ }
+ }
+ }
+ }
+ }
+ return false;
+ }
+
}
+ @Override
+ public Future<Void> executeBatch(BatchOperation<FlowRuleBatchEntry> batch) {
+ final Set<Dpid> sws = new HashSet<Dpid>();
+ for (FlowRuleBatchEntry fbe : batch.getOperations()) {
+ FlowRule flowRule = fbe.getTarget();
+ OpenFlowSwitch sw = controller.getSwitch(Dpid.dpid(flowRule.deviceId().uri()));
+ sws.add(new Dpid(sw.getId()));
+ switch (fbe.getOperator()) {
+ case ADD:
+ //TODO: Track XID for each flowmod
+ sw.sendMsg(new FlowModBuilder(flowRule, sw.factory()).buildFlowAdd());
+ break;
+ case REMOVE:
+ //TODO: Track XID for each flowmod
+ sw.sendMsg(new FlowModBuilder(flowRule, sw.factory()).buildFlowDel());
+ break;
+ case MODIFY:
+ //TODO: Track XID for each flowmod
+ sw.sendMsg(new FlowModBuilder(flowRule, sw.factory()).buildFlowMod());
+ break;
+ default:
+ log.error("Unsupported batch operation {}", fbe.getOperator());
+ }
+ }
+ InstallationFuture installation = new InstallationFuture(sws);
+ pendingFutures.put(U32.f(batch.hashCode()), installation);
+ installation.verify(batch.hashCode());
+ return installation;
+ }
+
+ private class InstallationFuture implements Future<Void> {
+
+ private final Set<Dpid> sws;
+ private final AtomicBoolean ok = new AtomicBoolean(true);
+ private final List<FlowEntry> offendingFlowMods = Lists.newLinkedList();
+
+ private final CountDownLatch countDownLatch;
+
+ public InstallationFuture(Set<Dpid> sws) {
+ this.sws = sws;
+ countDownLatch = new CountDownLatch(sws.size());
+ }
+
+ public void fail(OFErrorMsg msg, Dpid dpid) {
+ ok.set(false);
+ //TODO add reason to flowentry
+ //TODO handle specific error msgs
+ //offendingFlowMods.add(new FlowEntryBuilder(dpid, msg.));
+ switch (msg.getErrType()) {
+ case BAD_ACTION:
+ break;
+ case BAD_INSTRUCTION:
+ break;
+ case BAD_MATCH:
+ break;
+ case BAD_REQUEST:
+ break;
+ case EXPERIMENTER:
+ break;
+ case FLOW_MOD_FAILED:
+ break;
+ case GROUP_MOD_FAILED:
+ break;
+ case HELLO_FAILED:
+ break;
+ case METER_MOD_FAILED:
+ break;
+ case PORT_MOD_FAILED:
+ break;
+ case QUEUE_OP_FAILED:
+ break;
+ case ROLE_REQUEST_FAILED:
+ break;
+ case SWITCH_CONFIG_FAILED:
+ break;
+ case TABLE_FEATURES_FAILED:
+ break;
+ case TABLE_MOD_FAILED:
+ break;
+ default:
+ break;
+
+ }
+
+ }
+
+ public void satisfyRequirement(Dpid dpid) {
+ log.warn("Satisfaction from switch {}", dpid);
+ sws.remove(dpid);
+ countDownLatch.countDown();
+ }
+
+ public void verify(Integer id) {
+ for (Dpid dpid : sws) {
+ OpenFlowSwitch sw = controller.getSwitch(dpid);
+ OFBarrierRequest.Builder builder = sw.factory()
+ .buildBarrierRequest()
+ .setXid(id);
+ sw.sendMsg(builder.build());
+ }
+
+
+ }
+
+ @Override
+ public boolean cancel(boolean mayInterruptIfRunning) {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+ @Override
+ public boolean isCancelled() {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+ @Override
+ public boolean isDone() {
+ return sws.isEmpty();
+ }
+
+ @Override
+ public Void get() throws InterruptedException, ExecutionException {
+ countDownLatch.await();
+ //return offendingFlowMods;
+ return null;
+ }
+
+ @Override
+ public Void get(long timeout, TimeUnit unit)
+ throws InterruptedException, ExecutionException,
+ TimeoutException {
+ countDownLatch.await(timeout, unit);
+ //return offendingFlowMods;
+ return null;
+ }
+
+ }
}
diff --git a/providers/openflow/packet/src/test/java/org/onlab/onos/provider/of/packet/impl/OpenFlowPacketProviderTest.java b/providers/openflow/packet/src/test/java/org/onlab/onos/provider/of/packet/impl/OpenFlowPacketProviderTest.java
index 030563c..37971e3 100644
--- a/providers/openflow/packet/src/test/java/org/onlab/onos/provider/of/packet/impl/OpenFlowPacketProviderTest.java
+++ b/providers/openflow/packet/src/test/java/org/onlab/onos/provider/of/packet/impl/OpenFlowPacketProviderTest.java
@@ -181,7 +181,7 @@
}
private static TrafficTreatment treatment(Instruction ... insts) {
- TrafficTreatment.Builder builder = new DefaultTrafficTreatment.Builder();
+ TrafficTreatment.Builder builder = DefaultTrafficTreatment.builder();
for (Instruction i : insts) {
builder.add(i);
}
diff --git a/tools/build/envDefaults b/tools/build/envDefaults
index 473095c..cbc6577 100644
--- a/tools/build/envDefaults
+++ b/tools/build/envDefaults
@@ -9,10 +9,14 @@
export KARAF_TAR=${KARAF_TAR:-~/Downloads/apache-karaf-3.0.1.tar.gz}
export KARAF_DIST=$(basename $KARAF_ZIP .zip)
+# Fallback build number us derived from from the user name & time
+export BUILD_NUMBER=${BUILD_NUMBER:-$(id -un)~$(date +'%Y/%m/%d@%H:%M')}
+
# ONOS Version and onos.tar.gz staging environment
-export ONOS_VERSION=${ONOS_VERSION:-1.0.0-SNAPSHOT}
+export ONOS_POM_VERSION="1.0.0-SNAPSHOT"
+export ONOS_VERSION=${ONOS_VERSION:-1.0.0.$BUILD_NUMBER}
+export ONOS_BITS=onos-${ONOS_VERSION%~*}
export ONOS_STAGE_ROOT=${ONOS_STAGE_ROOT:-/tmp}
-export ONOS_BITS=onos-$ONOS_VERSION
export ONOS_STAGE=$ONOS_STAGE_ROOT/$ONOS_BITS
export ONOS_TAR=$ONOS_STAGE.tar.gz
diff --git a/tools/build/onos-package b/tools/build/onos-package
index 2d6b954..a55a613 100755
--- a/tools/build/onos-package
+++ b/tools/build/onos-package
@@ -49,7 +49,7 @@
# ONOS Patching ----------------------------------------------------------------
# Patch the Apache Karaf distribution file to add ONOS features repository
-perl -pi.old -e "s|^(featuresRepositories=.*)|\1,mvn:org.onlab.onos/onos-features/$ONOS_VERSION/xml/features|" \
+perl -pi.old -e "s|^(featuresRepositories=.*)|\1,mvn:org.onlab.onos/onos-features/$ONOS_POM_VERSION/xml/features|" \
$ONOS_STAGE/$KARAF_DIST/etc/org.apache.karaf.features.cfg
# Patch the Apache Karaf distribution file to load ONOS features
@@ -57,10 +57,14 @@
$ONOS_STAGE/$KARAF_DIST/etc/org.apache.karaf.features.cfg
# Patch the Apache Karaf distribution with ONOS branding bundle
-cp $M2_REPO/org/onlab/onos/onos-branding/$ONOS_VERSION/onos-branding-*.jar \
+cp $M2_REPO/org/onlab/onos/onos-branding/$ONOS_POM_VERSION/onos-branding-*.jar \
$ONOS_STAGE/$KARAF_DIST/lib
+# Patch in the ONOS version file
+echo $ONOS_VERSION > $ONOS_STAGE/VERSION
+
# Now package up the ONOS tar file
cd $ONOS_STAGE_ROOT
COPYFILE_DISABLE=1 tar zcf $ONOS_TAR $ONOS_BITS
ls -l $ONOS_TAR >&2
+rm -r $ONOS_STAGE
diff --git a/tools/dev/bash_profile b/tools/dev/bash_profile
index 8332a47..6c1444f 100644
--- a/tools/dev/bash_profile
+++ b/tools/dev/bash_profile
@@ -21,7 +21,7 @@
# e.g. 'o api', 'o dev', 'o'
function o {
cd $(find $ONOS_ROOT/ -type d | egrep -v '\.git|target' | \
- egrep "${1:-$ONOS_ROOT}" | head -n 1)
+ egrep "${1:-$ONOS_ROOT}" | egrep -v "$ONOS_ROOT/.+/src/" | head -n 1)
}
# Short-hand for 'mvn clean install' for us lazy folk
@@ -32,6 +32,9 @@
alias obs='onos-build-selective'
alias op='onos-package'
alias ot='onos-test'
+alias ol='onos-log'
+alias go='ob && ot && onos -w'
+alias pub='onos-push-update-bundle'
# Short-hand for tailing the ONOS (karaf) log
alias tl='$ONOS_ROOT/tools/dev/bin/onos-local-log'
@@ -88,5 +91,5 @@
}
function nuke {
- spy | cut -c7-11 | xargs kill
+ spy "$@" | cut -c7-11 | xargs kill
}
diff --git a/tools/package/bin/onos-service b/tools/package/bin/onos-service
index a5de5bd..c030887 100755
--- a/tools/package/bin/onos-service
+++ b/tools/package/bin/onos-service
@@ -4,6 +4,7 @@
#-------------------------------------------------------------------------------
export JAVA_HOME=${JAVA_HOME:-/usr/lib/jvm/java-7-openjdk-amd64/}
+export JAVA_OPTS="-Xms256M -Xmx2048M"
cd /opt/onos
/opt/onos/apache-karaf-3.0.1/bin/karaf "$@"
diff --git a/tools/test/bin/onos-install b/tools/test/bin/onos-install
index a87ff17..38934fd 100755
--- a/tools/test/bin/onos-install
+++ b/tools/test/bin/onos-install
@@ -32,6 +32,10 @@
# Remove any previous ON.Lab bits from ~/.m2 repo
rm -fr ~/.m2/repository/org/onlab
+
+ # Drop log level for the console
+ echo "log4j.logger.org.apache.sshd = WARN" >> $ONOS_INSTALL_DIR/$KARAF_DIST/etc/org.ops4j.pax.logging.cfg
+
"
# Configure the ONOS installation
diff --git a/tools/test/bin/onos-patch-vm b/tools/test/bin/onos-patch-vm
index f80f349..ccc4007 100755
--- a/tools/test/bin/onos-patch-vm
+++ b/tools/test/bin/onos-patch-vm
@@ -15,7 +15,7 @@
ssh $remote "
sudo perl -pi.bak -e \"s/127.0.1.1.*/127.0.1.1 $name/g\" /etc/hosts
- sudo perl -pi.bak -e \"s/.*/$name/g\" /etc/hostname
+ sudo bash -c \"echo $name >/etc/hostname\"
sudo hostname $name
" 2>/dev/null
diff --git a/tools/test/bin/onos-push-keys b/tools/test/bin/onos-push-keys
index fd49f86..247d331 100755
--- a/tools/test/bin/onos-push-keys
+++ b/tools/test/bin/onos-push-keys
@@ -9,5 +9,9 @@
remote=$ONOS_USER@${1:-$OCI}
scp -q ~/.ssh/id_rsa.pub $remote:/tmp
-ssh $remote "cat /tmp/id_rsa.pub >> ~/.ssh/authorized_keys"
+ssh $remote "
+ cat /tmp/id_rsa.pub >> ~/.ssh/authorized_keys
+ sort -u ~/.ssh/authorized_keys > ~/.ssh/authorized_keys.bak
+ mv ~/.ssh/authorized_keys.bak ~/.ssh/authorized_keys
+"
ssh -n -o PasswordAuthentication=no $remote true
diff --git a/tools/test/bin/onos-push-update-bundle b/tools/test/bin/onos-push-update-bundle
new file mode 100755
index 0000000..4f8ca7d
--- /dev/null
+++ b/tools/test/bin/onos-push-update-bundle
@@ -0,0 +1,21 @@
+#!/bin/bash
+#-------------------------------------------------------------------------------
+# Pushes the specified bundle to the remote ONOS cell machines and updates it.
+#-------------------------------------------------------------------------------
+
+[ ! -d "$ONOS_ROOT" ] && echo "ONOS_ROOT is not defined" >&2 && exit 1
+. $ONOS_ROOT/tools/build/envDefaults
+
+cd ~/.m2/repository
+jar=$(find org/onlab -type f -name '*.jar' | grep $1 | grep -v -e -tests | head -n 1)
+
+[ -z "$jar" ] && echo "No bundle $1 found for" && exit 1
+
+bundle=$(echo $(basename $jar .jar) | sed 's/-[0-9].*//g')
+
+nodes=$(env | sort | egrep "OC[0-9]+" | cut -d= -f2)
+for node in $nodes; do
+ scp -q $jar $ONOS_USER@$node:.m2/repository/$jar
+ scp -q $jar $ONOS_USER@$node:$ONOS_INSTALL_DIR/$KARAF_DIST/system/$jar
+ ssh $ONOS_USER@$node "$ONOS_INSTALL_DIR/bin/onos \"bundle:update -f $bundle\"" 2>/dev/null
+done
diff --git a/tools/test/topos/tower.py b/tools/test/topos/tower.py
new file mode 100644
index 0000000..b8a5f7c
--- /dev/null
+++ b/tools/test/topos/tower.py
@@ -0,0 +1,101 @@
+#!/usr/bin/env python
+from mininet.cli import CLI
+from mininet.net import Mininet
+from mininet.node import RemoteController, OVSKernelSwitch
+
+MAC = 12
+DPID = 16
+
+def string_to_hex(s, length):
+ """ Convert a string like 00:00 in to hex 0x0000 format"""
+ tmp = '{0:#x}'.format(int(s.replace(':', '').lstrip('0'),length))
+ return tmp
+
+def hex_to_string(h, length):
+ """Convert a hex number from 0x0000 to 00:00 format"""
+ tmp = h.lstrip('0x').zfill(length)
+ tmp = ':'.join(a+b for a,b in zip(tmp[::2], tmp[1::2]))
+ return tmp
+
+class Tower(object):
+ """ Create a tower topology from semi-scratch in Mininet """
+
+ def __init__(self, cname='flare', cip='15.255.126.183', k=4, h=6,
+ proto=None):
+ """Create tower topology for mininet
+ cname: controller name
+ cip: controller ip
+ k: number of leaf switches
+ h: number of hosts perl leaf switch
+ """
+
+ # We are creating the controller with local-loopback on purpose to avoid
+ # having the switches connect immediately. Instead, we'll set controller
+ # explicitly for each switch after configuring it as we want.
+ self.flare = RemoteController(cname, '127.0.0.1', 6633)
+ self.net = Mininet(controller=self.flare, switch = OVSKernelSwitch,
+ build=False)
+
+ self.cip = cip
+ self.spines = []
+ self.leaves = []
+ self.hosts = []
+ self.proto = proto
+
+ # Create the two spine switches
+ self.spines.append(self.net.addSwitch('s1'))
+ self.spines.append(self.net.addSwitch('s2'))
+
+ # Create two links between the spine switches
+ self.net.addLink(self.spines[0], self.spines[1])
+ self.net.addLink(self.spines[1], self.spines[0])
+
+ # Now create the leaf switches, their hosts and connect them together
+ i = 1
+ c = 0
+ while i <= k:
+ self.leaves.append(self.net.addSwitch('s1%d' % i))
+ for spine in self.spines:
+ self.net.addLink(self.leaves[i-1], spine)
+
+ j = 1
+ while j <= h:
+ self.hosts.append(self.net.addHost('h%d%d' % (i, j)))
+ self.net.addLink(self.hosts[c], self.leaves[i-1])
+ j+=1
+ c+=1
+
+ i+=1
+
+ def run(self):
+ """ Runs the created network topology and launches mininet cli"""
+ self.run_silent()
+ CLI(self.net)
+ self.net.stop()
+
+ def run_silent(self):
+ """ Runs silently - for unit testing """
+ self.net.build()
+
+ # Start the switches, configure them with desired protocols and only
+ # then set the controller
+ for sw in self.spines:
+ sw.start([self.flare])
+ if self.proto:
+ sw.cmd('ovs-vsctl set bridge %(sw)s protocols=%(proto)s' % \
+ { 'sw': sw.name, 'proto': self.proto})
+ sw.cmdPrint('ovs-vsctl set-controller %(sw)s tcp:%(ctl)s:6633' % \
+ {'sw': sw.name, 'ctl': self.cip})
+
+ for sw in self.leaves:
+ sw.start([self.flare])
+ sw.cmdPrint('ovs-vsctl set-controller %(sw)s tcp:%(ctl)s:6633' % \
+ {'sw': sw.name, 'ctl': self.cip})
+
+ def pingAll(self):
+ """ PingAll to create flows - for unit testing """
+ self.net.pingAll()
+
+ def stop(self):
+ "Stops the topology. You should call this after run_silent"
+ self.net.stop()
diff --git a/tools/test/topos/tt.py b/tools/test/topos/tt.py
new file mode 100644
index 0000000..b74d446
--- /dev/null
+++ b/tools/test/topos/tt.py
@@ -0,0 +1,5 @@
+#!/usr/bin/python
+# Launches mininet with Tower topology configuration.
+import sys, tower
+net = tower.Tower(cip=sys.argv[1])
+net.run()
diff --git a/utils/misc/src/main/java/org/onlab/metrics/MetricsFeature.java b/utils/misc/src/main/java/org/onlab/metrics/MetricsFeature.java
index 7a97b08..75c1018 100644
--- a/utils/misc/src/main/java/org/onlab/metrics/MetricsFeature.java
+++ b/utils/misc/src/main/java/org/onlab/metrics/MetricsFeature.java
@@ -11,7 +11,7 @@
*
* @param newName name of the Feature
*/
- MetricsFeature(final String newName) {
+ public MetricsFeature(final String newName) {
name = newName;
}
diff --git a/utils/misc/src/main/java/org/onlab/metrics/MetricsManager.java b/utils/misc/src/main/java/org/onlab/metrics/MetricsManager.java
index e07d3f9..98e8310 100644
--- a/utils/misc/src/main/java/org/onlab/metrics/MetricsManager.java
+++ b/utils/misc/src/main/java/org/onlab/metrics/MetricsManager.java
@@ -1,7 +1,5 @@
package org.onlab.metrics;
-import java.io.File;
-import java.util.Locale;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
@@ -10,9 +8,11 @@
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Deactivate;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import com.codahale.metrics.ConsoleReporter;
import com.codahale.metrics.Counter;
-import com.codahale.metrics.CsvReporter;
import com.codahale.metrics.Gauge;
import com.codahale.metrics.Histogram;
import com.codahale.metrics.Meter;
@@ -56,6 +56,7 @@
@Component(immediate = true)
public final class MetricsManager implements MetricsService {
+ private final Logger log = LoggerFactory.getLogger(getClass());
/**
* Registry to hold the Components defined in the system.
*/
@@ -69,27 +70,31 @@
/**
* Default Reporter for this metrics manager.
*/
- private final CsvReporter reporter;
+ //private final Slf4jReporter reporter;
+ private final ConsoleReporter reporter;
public MetricsManager() {
- this.componentsRegistry = new ConcurrentHashMap<>();
this.metricsRegistry = new MetricRegistry();
-
- this.reporter = CsvReporter.forRegistry(metricsRegistry)
- .formatFor(Locale.US)
+// this.reporter = Slf4jReporter.forRegistry(this.metricsRegistry)
+// .outputTo(log)
+// .convertRatesTo(TimeUnit.SECONDS)
+// .convertDurationsTo(TimeUnit.NANOSECONDS)
+// .build();
+ this.reporter = ConsoleReporter.forRegistry(this.metricsRegistry)
.convertRatesTo(TimeUnit.SECONDS)
- .convertDurationsTo(TimeUnit.MICROSECONDS)
- .build(new File("/tmp/"));
-
- reporter.start(10, TimeUnit.SECONDS);
+ .convertDurationsTo(TimeUnit.NANOSECONDS)
+ .build();
}
@Activate
public void activate() {
+ this.componentsRegistry = new ConcurrentHashMap<>();
+ reporter.start(10, TimeUnit.SECONDS);
}
@Deactivate
public void deactivate() {
+ reporter.stop();
}
/**
diff --git a/utils/misc/src/main/java/org/onlab/packet/MacAddress.java b/utils/misc/src/main/java/org/onlab/packet/MacAddress.java
index 33764bb..814660b 100644
--- a/utils/misc/src/main/java/org/onlab/packet/MacAddress.java
+++ b/utils/misc/src/main/java/org/onlab/packet/MacAddress.java
@@ -22,6 +22,12 @@
*
*/
public class MacAddress {
+ public static final byte[] ZERO_MAC_ADDRESS =
+ MacAddress.valueOf("00:00:00:00:00:00").getAddress();
+
+ public static final byte[] BROADCAST_MAC =
+ MacAddress.valueOf("ff:ff:ff:ff:ff:ff").getAddress();
+
public static final int MAC_ADDRESS_LENGTH = 6;
private byte[] address = new byte[MacAddress.MAC_ADDRESS_LENGTH];
diff --git a/utils/misc/src/main/java/org/onlab/util/NewConcurrentHashMap.java b/utils/misc/src/main/java/org/onlab/util/NewConcurrentHashMap.java
new file mode 100644
index 0000000..bd17867
--- /dev/null
+++ b/utils/misc/src/main/java/org/onlab/util/NewConcurrentHashMap.java
@@ -0,0 +1,33 @@
+package org.onlab.util;
+
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import org.apache.commons.lang3.concurrent.ConcurrentException;
+import org.apache.commons.lang3.concurrent.ConcurrentInitializer;
+
+/**
+ * Creates an instance of new ConcurrentHashMap on each {@link #get()} call.
+ * <p>
+ * To be used with
+ * {@link org.apache.commons.lang3.concurrent.ConcurrentUtils#createIfAbsent()
+ * ConcurrentUtils#createIfAbsent}
+ *
+ * @param <K> ConcurrentHashMap key type
+ * @param <V> ConcurrentHashMap value type
+ */
+public final class NewConcurrentHashMap<K, V>
+ implements ConcurrentInitializer<ConcurrentMap<K, V>> {
+
+ public static final NewConcurrentHashMap<?, ?> INSTANCE = new NewConcurrentHashMap<>();
+
+ @SuppressWarnings("unchecked")
+ public static <K, V> NewConcurrentHashMap<K, V> ifNeeded() {
+ return (NewConcurrentHashMap<K, V>) INSTANCE;
+ }
+
+ @Override
+ public ConcurrentMap<K, V> get() throws ConcurrentException {
+ return new ConcurrentHashMap<>();
+ }
+}
diff --git a/utils/misc/src/main/java/org/onlab/util/Timer.java b/utils/misc/src/main/java/org/onlab/util/Timer.java
index 276138f..6a3be49 100644
--- a/utils/misc/src/main/java/org/onlab/util/Timer.java
+++ b/utils/misc/src/main/java/org/onlab/util/Timer.java
@@ -8,7 +8,7 @@
*/
public final class Timer {
- private static HashedWheelTimer timer;
+ private static volatile HashedWheelTimer timer;
// Ban public construction
private Timer() {
@@ -21,10 +21,17 @@
*/
public static HashedWheelTimer getTimer() {
if (Timer.timer == null) {
- Timer.timer = new HashedWheelTimer();
- Timer.timer.start();
+ initTimer();
}
return Timer.timer;
}
+ private static synchronized void initTimer() {
+ if (Timer.timer == null) {
+ HashedWheelTimer hwTimer = new HashedWheelTimer();
+ hwTimer.start();
+ Timer.timer = hwTimer;
+ }
+ }
+
}
diff --git a/utils/misc/src/main/java/org/onlab/util/Tools.java b/utils/misc/src/main/java/org/onlab/util/Tools.java
index c5162f6..5643098 100644
--- a/utils/misc/src/main/java/org/onlab/util/Tools.java
+++ b/utils/misc/src/main/java/org/onlab/util/Tools.java
@@ -4,6 +4,12 @@
import com.google.common.primitives.UnsignedLongs;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
import java.util.concurrent.ThreadFactory;
public abstract class Tools {
@@ -66,4 +72,24 @@
}
}
+ /**
+ * Slurps the contents of a file into a list of strings, one per line.
+ *
+ * @param path file path
+ * @return file contents
+ */
+ public static List<String> slurp(File path) {
+ try (BufferedReader br = new BufferedReader(new FileReader(path))) {
+ List<String> lines = new ArrayList<>();
+ String line;
+ while ((line = br.readLine()) != null) {
+ lines.add(line);
+ }
+ return lines;
+
+ } catch (IOException e) {
+ return null;
+ }
+ }
+
}
diff --git a/utils/netty/src/main/java/org/onlab/netty/AsyncResponse.java b/utils/netty/src/main/java/org/onlab/netty/AsyncResponse.java
index b2b490e..1772a3c 100644
--- a/utils/netty/src/main/java/org/onlab/netty/AsyncResponse.java
+++ b/utils/netty/src/main/java/org/onlab/netty/AsyncResponse.java
@@ -8,17 +8,16 @@
* This class provides a base implementation of Response, with methods to retrieve the
* result and query to see if the result is ready. The result can only be retrieved when
* it is ready and the get methods will block if the result is not ready yet.
- * @param <T> type of response.
*/
-public class AsyncResponse<T> implements Response<T> {
+public class AsyncResponse implements Response {
- private T value;
+ private byte[] value;
private boolean done = false;
private final long start = System.nanoTime();
@Override
- public T get(long timeout, TimeUnit tu) throws TimeoutException {
- timeout = tu.toNanos(timeout);
+ public byte[] get(long timeout, TimeUnit timeUnit) throws TimeoutException {
+ timeout = timeUnit.toNanos(timeout);
boolean interrupted = false;
try {
synchronized (this) {
@@ -43,7 +42,7 @@
}
@Override
- public T get() throws InterruptedException {
+ public byte[] get() throws InterruptedException {
throw new UnsupportedOperationException();
}
@@ -57,11 +56,10 @@
* available.
* @param data response data.
*/
- @SuppressWarnings("unchecked")
- public synchronized void setResponse(Object data) {
+ public synchronized void setResponse(byte[] data) {
if (!done) {
done = true;
- value = (T) data;
+ value = data;
this.notifyAll();
}
}
diff --git a/utils/netty/src/main/java/org/onlab/netty/DecoderState.java b/utils/netty/src/main/java/org/onlab/netty/DecoderState.java
new file mode 100644
index 0000000..638fe6c
--- /dev/null
+++ b/utils/netty/src/main/java/org/onlab/netty/DecoderState.java
@@ -0,0 +1,12 @@
+package org.onlab.netty;
+
+/**
+ * State transitions a decoder goes through as it is decoding an incoming message.
+ */
+public enum DecoderState {
+ READ_HEADER_VERSION,
+ READ_PREAMBLE,
+ READ_CONTENT_LENGTH,
+ READ_SERIALIZER_VERSION,
+ READ_CONTENT
+}
diff --git a/utils/netty/src/main/java/org/onlab/netty/EchoHandler.java b/utils/netty/src/main/java/org/onlab/netty/EchoHandler.java
index 313a448..b038db8 100644
--- a/utils/netty/src/main/java/org/onlab/netty/EchoHandler.java
+++ b/utils/netty/src/main/java/org/onlab/netty/EchoHandler.java
@@ -2,14 +2,20 @@
import java.io.IOException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+//FIXME: Should be move out to test or app
/**
* Message handler that echos the message back to the sender.
*/
public class EchoHandler implements MessageHandler {
+ private final Logger log = LoggerFactory.getLogger(getClass());
+
@Override
public void handle(Message message) throws IOException {
- System.out.println("Received: " + message.payload() + ". Echoing it back to the sender.");
+ log.info("Received message. Echoing it back to the sender.");
message.respond(message.payload());
}
}
diff --git a/utils/netty/src/main/java/org/onlab/netty/Endpoint.java b/utils/netty/src/main/java/org/onlab/netty/Endpoint.java
index 8681093..660f2b9 100644
--- a/utils/netty/src/main/java/org/onlab/netty/Endpoint.java
+++ b/utils/netty/src/main/java/org/onlab/netty/Endpoint.java
@@ -1,5 +1,9 @@
package org.onlab.netty;
+import java.util.Objects;
+
+import com.google.common.base.MoreObjects;
+
/**
* Representation of a TCP/UDP communication end point.
*/
@@ -8,6 +12,15 @@
private final int port;
private final String host;
+ /**
+ * Used for serialization.
+ */
+ @SuppressWarnings("unused")
+ private Endpoint() {
+ port = 0;
+ host = null;
+ }
+
public Endpoint(String host, int port) {
this.host = host;
this.port = port;
@@ -23,16 +36,15 @@
@Override
public String toString() {
- return "Endpoint [port=" + port + ", host=" + host + "]";
+ return MoreObjects.toStringHelper(getClass())
+ .add("port", port)
+ .add("host", host)
+ .toString();
}
@Override
public int hashCode() {
- final int prime = 31;
- int result = 1;
- result = prime * result + ((host == null) ? 0 : host.hashCode());
- result = prime * result + port;
- return result;
+ return Objects.hash(host, port);
}
@Override
@@ -46,17 +58,8 @@
if (getClass() != obj.getClass()) {
return false;
}
- Endpoint other = (Endpoint) obj;
- if (host == null) {
- if (other.host != null) {
- return false;
- }
- } else if (!host.equals(other.host)) {
- return false;
- }
- if (port != other.port) {
- return false;
- }
- return true;
+ Endpoint that = (Endpoint) obj;
+ return Objects.equals(this.port, that.port) &&
+ Objects.equals(this.host, that.host);
}
}
diff --git a/utils/netty/src/main/java/org/onlab/netty/InternalMessage.java b/utils/netty/src/main/java/org/onlab/netty/InternalMessage.java
index bcf6f52..938ec7b 100644
--- a/utils/netty/src/main/java/org/onlab/netty/InternalMessage.java
+++ b/utils/netty/src/main/java/org/onlab/netty/InternalMessage.java
@@ -8,12 +8,13 @@
*/
public final class InternalMessage implements Message {
+ public static final String REPLY_MESSAGE_TYPE = "NETTY_MESSAGIG_REQUEST_REPLY";
+
private long id;
private Endpoint sender;
private String type;
- private Object payload;
+ private byte[] payload;
private transient NettyMessagingService messagingService;
- public static final String REPLY_MESSAGE_TYPE = "NETTY_MESSAGIG_REQUEST_REPLY";
// Must be created using the Builder.
private InternalMessage() {}
@@ -31,12 +32,16 @@
}
@Override
- public Object payload() {
+ public byte[] payload() {
return payload;
}
+ protected void setMessagingService(NettyMessagingService messagingService) {
+ this.messagingService = messagingService;
+ }
+
@Override
- public void respond(Object data) throws IOException {
+ public void respond(byte[] data) throws IOException {
Builder builder = new Builder(messagingService);
InternalMessage message = builder.withId(this.id)
// FIXME: Sender should be messagingService.localEp.
@@ -51,7 +56,7 @@
/**
* Builder for InternalMessages.
*/
- public static class Builder {
+ public static final class Builder {
private InternalMessage message;
public Builder(NettyMessagingService messagingService) {
@@ -73,7 +78,7 @@
message.sender = sender;
return this;
}
- public Builder withPayload(Object payload) {
+ public Builder withPayload(byte[] payload) {
message.payload = payload;
return this;
}
diff --git a/utils/netty/src/main/java/org/onlab/netty/KryoSerializer.java b/utils/netty/src/main/java/org/onlab/netty/KryoSerializer.java
index 73c01a0..b8efb51 100644
--- a/utils/netty/src/main/java/org/onlab/netty/KryoSerializer.java
+++ b/utils/netty/src/main/java/org/onlab/netty/KryoSerializer.java
@@ -1,18 +1,16 @@
package org.onlab.netty;
import org.onlab.util.KryoPool;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.HashMap;
+//FIXME: Should be move out to test or app
/**
* Kryo Serializer.
*/
-public class KryoSerializer implements Serializer {
-
- private final Logger log = LoggerFactory.getLogger(getClass());
+public class KryoSerializer {
private KryoPool serializerPool;
@@ -28,20 +26,29 @@
serializerPool = KryoPool.newBuilder()
.register(ArrayList.class,
HashMap.class,
- ArrayList.class
+ ArrayList.class,
+ InternalMessage.class,
+ Endpoint.class,
+ byte[].class
)
.build()
.populate(1);
}
- @Override
- public Object decode(byte[] data) {
+ public <T> T decode(byte[] data) {
return serializerPool.deserialize(data);
}
- @Override
public byte[] encode(Object payload) {
return serializerPool.serialize(payload);
}
+
+ public <T> T decode(ByteBuffer buffer) {
+ return serializerPool.deserialize(buffer);
+ }
+
+ public void encode(Object obj, ByteBuffer buffer) {
+ serializerPool.serialize(obj, buffer);
+ }
}
diff --git a/utils/netty/src/main/java/org/onlab/netty/LoggingHandler.java b/utils/netty/src/main/java/org/onlab/netty/LoggingHandler.java
index ed6cdb4..366898b 100644
--- a/utils/netty/src/main/java/org/onlab/netty/LoggingHandler.java
+++ b/utils/netty/src/main/java/org/onlab/netty/LoggingHandler.java
@@ -1,12 +1,17 @@
package org.onlab.netty;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
/**
* A MessageHandler that simply logs the information.
*/
public class LoggingHandler implements MessageHandler {
+ private final Logger log = LoggerFactory.getLogger(getClass());
+
@Override
public void handle(Message message) {
- System.out.println("Received: " + message.payload());
+ log.info("Received message. Payload has {} bytes", message.payload().length);
}
}
diff --git a/utils/netty/src/main/java/org/onlab/netty/Message.java b/utils/netty/src/main/java/org/onlab/netty/Message.java
index 54b9526..87a8bb6 100644
--- a/utils/netty/src/main/java/org/onlab/netty/Message.java
+++ b/utils/netty/src/main/java/org/onlab/netty/Message.java
@@ -12,12 +12,12 @@
* Returns the payload of this message.
* @return message payload.
*/
- public Object payload();
+ public byte[] payload();
/**
- * Sends a reply back to the sender of this messge.
+ * Sends a reply back to the sender of this message.
* @param data payload of the response.
* @throws IOException if there is a communication error.
*/
- public void respond(Object data) throws IOException;
+ public void respond(byte[] data) throws IOException;
}
diff --git a/utils/netty/src/main/java/org/onlab/netty/MessageDecoder.java b/utils/netty/src/main/java/org/onlab/netty/MessageDecoder.java
index ecf2d62..b494ce9 100644
--- a/utils/netty/src/main/java/org/onlab/netty/MessageDecoder.java
+++ b/utils/netty/src/main/java/org/onlab/netty/MessageDecoder.java
@@ -1,58 +1,71 @@
package org.onlab.netty;
+import static com.google.common.base.Preconditions.checkState;
+import io.netty.buffer.ByteBuf;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.handler.codec.ReplayingDecoder;
+
import java.util.Arrays;
import java.util.List;
-import static com.google.common.base.Preconditions.checkState;
-
-import io.netty.buffer.ByteBuf;
-import io.netty.channel.ChannelHandlerContext;
-import io.netty.handler.codec.ByteToMessageDecoder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
/**
- * Decode bytes into a InternalMessage.
+ * Decoder for inbound messages.
*/
-public class MessageDecoder extends ByteToMessageDecoder {
+public class MessageDecoder extends ReplayingDecoder<DecoderState> {
+
+ private final Logger log = LoggerFactory.getLogger(getClass());
private final NettyMessagingService messagingService;
- private final Serializer serializer;
- public MessageDecoder(NettyMessagingService messagingService, Serializer serializer) {
+ private static final KryoSerializer SERIALIZER = new KryoSerializer();
+
+ private int contentLength;
+
+ public MessageDecoder(NettyMessagingService messagingService) {
+ super(DecoderState.READ_HEADER_VERSION);
this.messagingService = messagingService;
- this.serializer = serializer;
}
@Override
- protected void decode(ChannelHandlerContext context, ByteBuf in,
- List<Object> messages) throws Exception {
+ protected void decode(
+ ChannelHandlerContext context,
+ ByteBuf buffer,
+ List<Object> out) throws Exception {
- byte[] preamble = in.readBytes(MessageEncoder.PREAMBLE.length).array();
- checkState(Arrays.equals(MessageEncoder.PREAMBLE, preamble), "Message has wrong preamble");
+ switch(state()) {
+ case READ_HEADER_VERSION:
+ int headerVersion = buffer.readInt();
+ checkState(headerVersion == MessageEncoder.HEADER_VERSION, "Unexpected header version");
+ checkpoint(DecoderState.READ_PREAMBLE);
+ case READ_PREAMBLE:
+ byte[] preamble = new byte[MessageEncoder.PREAMBLE.length];
+ buffer.readBytes(preamble);
+ checkState(Arrays.equals(MessageEncoder.PREAMBLE, preamble), "Message has wrong preamble");
+ checkpoint(DecoderState.READ_CONTENT_LENGTH);
+ case READ_CONTENT_LENGTH:
+ contentLength = buffer.readInt();
+ checkpoint(DecoderState.READ_SERIALIZER_VERSION);
+ case READ_SERIALIZER_VERSION:
+ int serializerVersion = buffer.readInt();
+ checkState(serializerVersion == MessageEncoder.SERIALIZER_VERSION, "Unexpected serializer version");
+ checkpoint(DecoderState.READ_CONTENT);
+ case READ_CONTENT:
+ InternalMessage message = SERIALIZER.decode(buffer.readBytes(contentLength).nioBuffer());
+ message.setMessagingService(messagingService);
+ out.add(message);
+ checkpoint(DecoderState.READ_HEADER_VERSION);
+ break;
+ default:
+ checkState(false, "Must not be here");
+ }
+ }
- // read message Id.
- long id = in.readLong();
-
- // read message type; first read size and then bytes.
- String type = new String(in.readBytes(in.readInt()).array());
-
- // read sender host name; first read size and then bytes.
- String host = new String(in.readBytes(in.readInt()).array());
-
- // read sender port.
- int port = in.readInt();
-
- Endpoint sender = new Endpoint(host, port);
-
- // read message payload; first read size and then bytes.
- Object payload = serializer.decode(in.readBytes(in.readInt()).array());
-
- InternalMessage message = new InternalMessage.Builder(messagingService)
- .withId(id)
- .withSender(sender)
- .withType(type)
- .withPayload(payload)
- .build();
-
- messages.add(message);
+ @Override
+ public void exceptionCaught(ChannelHandlerContext context, Throwable cause) {
+ log.error("Exception inside channel handling pipeline.", cause);
+ context.close();
}
}
diff --git a/utils/netty/src/main/java/org/onlab/netty/MessageEncoder.java b/utils/netty/src/main/java/org/onlab/netty/MessageEncoder.java
index 1b52a0f..d026dec 100644
--- a/utils/netty/src/main/java/org/onlab/netty/MessageEncoder.java
+++ b/utils/netty/src/main/java/org/onlab/netty/MessageEncoder.java
@@ -1,60 +1,56 @@
package org.onlab.netty;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
import io.netty.buffer.ByteBuf;
+import io.netty.channel.ChannelHandler.Sharable;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToByteEncoder;
/**
* Encode InternalMessage out into a byte buffer.
*/
+@Sharable
public class MessageEncoder extends MessageToByteEncoder<InternalMessage> {
+ private final Logger log = LoggerFactory.getLogger(getClass());
+
// onosiscool in ascii
public static final byte[] PREAMBLE = "onosiscool".getBytes();
+ public static final int HEADER_VERSION = 1;
+ public static final int SERIALIZER_VERSION = 1;
- private final Serializer serializer;
- public MessageEncoder(Serializer serializer) {
- this.serializer = serializer;
- }
+ private static final KryoSerializer SERIALIZER = new KryoSerializer();
@Override
- protected void encode(ChannelHandlerContext context, InternalMessage message,
+ protected void encode(
+ ChannelHandlerContext context,
+ InternalMessage message,
ByteBuf out) throws Exception {
+ // write version
+ out.writeInt(HEADER_VERSION);
+
// write preamble
out.writeBytes(PREAMBLE);
- // write id
- out.writeLong(message.id());
+ byte[] payload = SERIALIZER.encode(message);
- // write type length
- out.writeInt(message.type().length());
-
- // write type
- out.writeBytes(message.type().getBytes());
-
- // write sender host name size
- out.writeInt(message.sender().host().length());
-
- // write sender host name.
- out.writeBytes(message.sender().host().getBytes());
-
- // write port
- out.writeInt(message.sender().port());
-
- try {
- serializer.encode(message.payload());
- } catch (Exception e) {
- e.printStackTrace();
- }
-
- byte[] payload = serializer.encode(message.payload());
-
- // write payload length.
+ // write payload length
out.writeInt(payload.length);
- // write payload bytes
+ // write payloadSerializer version
+ out.writeInt(SERIALIZER_VERSION);
+
+ // write payload.
out.writeBytes(payload);
}
+
+ @Override
+ public void exceptionCaught(ChannelHandlerContext context, Throwable cause) {
+ log.error("Exception inside channel handling pipeline.", cause);
+ context.close();
+ }
}
diff --git a/utils/netty/src/main/java/org/onlab/netty/MessagingService.java b/utils/netty/src/main/java/org/onlab/netty/MessagingService.java
index ebad442..08676ac 100644
--- a/utils/netty/src/main/java/org/onlab/netty/MessagingService.java
+++ b/utils/netty/src/main/java/org/onlab/netty/MessagingService.java
@@ -11,10 +11,10 @@
* The message is specified using the type and payload.
* @param ep end point to send the message to.
* @param type type of message.
- * @param payload message payload.
+ * @param payload message payload bytes.
* @throws IOException
*/
- public void sendAsync(Endpoint ep, String type, Object payload) throws IOException;
+ public void sendAsync(Endpoint ep, String type, byte[] payload) throws IOException;
/**
* Sends a message synchronously and waits for a response.
@@ -24,7 +24,7 @@
* @return a response future
* @throws IOException
*/
- public <T> Response<T> sendAndReceive(Endpoint ep, String type, Object payload) throws IOException;
+ public Response sendAndReceive(Endpoint ep, String type, byte[] payload) throws IOException;
/**
* Registers a new message handler for message type.
@@ -38,4 +38,4 @@
* @param type message type
*/
public void unregisterHandler(String type);
-}
+}
\ No newline at end of file
diff --git a/utils/netty/src/main/java/org/onlab/netty/NettyMessagingService.java b/utils/netty/src/main/java/org/onlab/netty/NettyMessagingService.java
index 54da8cc..5a51ad4 100644
--- a/utils/netty/src/main/java/org/onlab/netty/NettyMessagingService.java
+++ b/utils/netty/src/main/java/org/onlab/netty/NettyMessagingService.java
@@ -11,6 +11,7 @@
import io.netty.buffer.PooledByteBufAllocator;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
+import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
@@ -22,7 +23,6 @@
import io.netty.channel.socket.nio.NioSocketChannel;
import org.apache.commons.lang.math.RandomUtils;
-import org.apache.commons.pool.KeyedObjectPool;
import org.apache.commons.pool.KeyedPoolableObjectFactory;
import org.apache.commons.pool.impl.GenericKeyedObjectPool;
import org.slf4j.Logger;
@@ -38,16 +38,19 @@
private final Logger log = LoggerFactory.getLogger(getClass());
- private KeyedObjectPool<Endpoint, Channel> channels =
- new GenericKeyedObjectPool<Endpoint, Channel>(new OnosCommunicationChannelFactory());
private final int port;
+ private final Endpoint localEp;
private final EventLoopGroup bossGroup = new NioEventLoopGroup();
private final EventLoopGroup workerGroup = new NioEventLoopGroup();
private final ConcurrentMap<String, MessageHandler> handlers = new ConcurrentHashMap<>();
- private Cache<Long, AsyncResponse<?>> responseFutures;
- private final Endpoint localEp;
-
- protected Serializer serializer;
+ private final Cache<Long, AsyncResponse> responseFutures = CacheBuilder.newBuilder()
+ .maximumSize(100000)
+ .weakValues()
+ // TODO: Once the entry expires, notify blocking threads (if any).
+ .expireAfterWrite(10, TimeUnit.MINUTES)
+ .build();
+ private final GenericKeyedObjectPool<Endpoint, Channel> channels
+ = new GenericKeyedObjectPool<Endpoint, Channel>(new OnosCommunicationChannelFactory());
public NettyMessagingService() {
// TODO: Default port should be configurable.
@@ -66,12 +69,8 @@
}
public void activate() throws Exception {
- responseFutures = CacheBuilder.newBuilder()
- .maximumSize(100000)
- .weakValues()
- // TODO: Once the entry expires, notify blocking threads (if any).
- .expireAfterWrite(10, TimeUnit.MINUTES)
- .build();
+ channels.setTestOnBorrow(true);
+ channels.setTestOnReturn(true);
startAcceptingConnections();
}
@@ -82,7 +81,7 @@
}
@Override
- public void sendAsync(Endpoint ep, String type, Object payload) throws IOException {
+ public void sendAsync(Endpoint ep, String type, byte[] payload) throws IOException {
InternalMessage message = new InternalMessage.Builder(this)
.withId(RandomUtils.nextLong())
.withSender(localEp)
@@ -95,24 +94,21 @@
protected void sendAsync(Endpoint ep, InternalMessage message) throws IOException {
Channel channel = null;
try {
- channel = channels.borrowObject(ep);
- channel.eventLoop().execute(new WriteTask(channel, message));
+ try {
+ channel = channels.borrowObject(ep);
+ channel.eventLoop().execute(new WriteTask(channel, message));
+ } finally {
+ channels.returnObject(ep, channel);
+ }
} catch (Exception e) {
throw new IOException(e);
- } finally {
- try {
- channels.returnObject(ep, channel);
- } catch (Exception e) {
- log.warn("Error returning object back to the pool", e);
- // ignored.
- }
}
}
@Override
- public <T> Response<T> sendAndReceive(Endpoint ep, String type, Object payload)
+ public Response sendAndReceive(Endpoint ep, String type, byte[] payload)
throws IOException {
- AsyncResponse<T> futureResponse = new AsyncResponse<T>();
+ AsyncResponse futureResponse = new AsyncResponse();
Long messageId = RandomUtils.nextLong();
responseFutures.put(messageId, futureResponse);
InternalMessage message = new InternalMessage.Builder(this)
@@ -141,6 +137,9 @@
private void startAcceptingConnections() throws InterruptedException {
ServerBootstrap b = new ServerBootstrap();
+ b.option(ChannelOption.WRITE_BUFFER_HIGH_WATER_MARK, 32 * 1024);
+ b.option(ChannelOption.WRITE_BUFFER_LOW_WATER_MARK, 8 * 1024);
+ // TODO: Need JVM options to configure PooledByteBufAllocator.
b.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
@@ -167,17 +166,18 @@
@Override
public Channel makeObject(Endpoint ep) throws Exception {
- Bootstrap b = new Bootstrap();
- b.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);
- b.group(workerGroup);
+ Bootstrap bootstrap = new Bootstrap();
+ bootstrap.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);
+ bootstrap.option(ChannelOption.WRITE_BUFFER_HIGH_WATER_MARK, 32 * 1024);
+ bootstrap.option(ChannelOption.WRITE_BUFFER_LOW_WATER_MARK, 8 * 1024);
+ bootstrap.group(workerGroup);
// TODO: Make this faster:
// http://normanmaurer.me/presentations/2014-facebook-eng-netty/slides.html#37.0
- b.channel(NioSocketChannel.class);
- b.option(ChannelOption.SO_KEEPALIVE, true);
- b.handler(new OnosCommunicationChannelInitializer());
-
+ bootstrap.channel(NioSocketChannel.class);
+ bootstrap.option(ChannelOption.SO_KEEPALIVE, true);
+ bootstrap.handler(new OnosCommunicationChannelInitializer());
// Start the client.
- ChannelFuture f = b.connect(ep.host(), ep.port()).sync();
+ ChannelFuture f = bootstrap.connect(ep.host(), ep.port()).sync();
return f.channel();
}
@@ -194,31 +194,35 @@
private class OnosCommunicationChannelInitializer extends ChannelInitializer<SocketChannel> {
+ private final ChannelHandler dispatcher = new InboundMessageDispatcher();
+ private final ChannelHandler encoder = new MessageEncoder();
+
@Override
protected void initChannel(SocketChannel channel) throws Exception {
channel.pipeline()
- .addLast(new MessageEncoder(serializer))
- .addLast(new MessageDecoder(NettyMessagingService.this, serializer))
- .addLast(new NettyMessagingService.InboundMessageDispatcher());
+ .addLast("encoder", encoder)
+ .addLast("decoder", new MessageDecoder(NettyMessagingService.this))
+ .addLast("handler", dispatcher);
}
}
private class WriteTask implements Runnable {
- private final Object message;
+ private final InternalMessage message;
private final Channel channel;
- public WriteTask(Channel channel, Object message) {
- this.message = message;
+ public WriteTask(Channel channel, InternalMessage message) {
this.channel = channel;
+ this.message = message;
}
@Override
public void run() {
- channel.writeAndFlush(message);
+ channel.writeAndFlush(message, channel.voidPromise());
}
}
+ @ChannelHandler.Sharable
private class InboundMessageDispatcher extends SimpleChannelInboundHandler<InternalMessage> {
@Override
@@ -226,12 +230,13 @@
String type = message.type();
if (type.equals(InternalMessage.REPLY_MESSAGE_TYPE)) {
try {
- AsyncResponse<?> futureResponse =
+ AsyncResponse futureResponse =
NettyMessagingService.this.responseFutures.getIfPresent(message.id());
if (futureResponse != null) {
futureResponse.setResponse(message.payload());
+ } else {
+ log.warn("Received a reply. But was unable to locate the request handle");
}
- log.warn("Received a reply. But was unable to locate the request handle");
} finally {
NettyMessagingService.this.responseFutures.invalidate(message.id());
}
@@ -240,5 +245,11 @@
MessageHandler handler = NettyMessagingService.this.getMessageHandler(type);
handler.handle(message);
}
+
+ @Override
+ public void exceptionCaught(ChannelHandlerContext context, Throwable cause) {
+ log.error("Exception inside channel handling pipeline.", cause);
+ context.close();
+ }
}
}
diff --git a/utils/netty/src/main/java/org/onlab/netty/Response.java b/utils/netty/src/main/java/org/onlab/netty/Response.java
index 04675ce..150755e 100644
--- a/utils/netty/src/main/java/org/onlab/netty/Response.java
+++ b/utils/netty/src/main/java/org/onlab/netty/Response.java
@@ -7,26 +7,24 @@
* Response object returned when making synchronous requests.
* Can you used to check is a response is ready and/or wait for a response
* to become available.
- *
- * @param <T> type of response.
*/
-public interface Response<T> {
+public interface Response {
/**
* Gets the response waiting for a designated timeout period.
* @param timeout timeout period (since request was sent out)
* @param tu unit of time.
- * @return response
+ * @return response payload
* @throws TimeoutException if the timeout expires before the response arrives.
*/
- public T get(long timeout, TimeUnit tu) throws TimeoutException;
+ public byte[] get(long timeout, TimeUnit tu) throws TimeoutException;
/**
* Gets the response waiting for indefinite timeout period.
- * @return response
+ * @return response payload
* @throws InterruptedException if the thread is interrupted before the response arrives.
*/
- public T get() throws InterruptedException;
+ public byte[] get() throws InterruptedException;
/**
* Checks if the response is ready without blocking.
diff --git a/utils/netty/src/main/java/org/onlab/netty/Serializer.java b/utils/netty/src/main/java/org/onlab/netty/Serializer.java
deleted file mode 100644
index ac55f5a..0000000
--- a/utils/netty/src/main/java/org/onlab/netty/Serializer.java
+++ /dev/null
@@ -1,24 +0,0 @@
-package org.onlab.netty;
-
-/**
- * Interface for encoding/decoding message payloads.
- */
-public interface Serializer {
-
- /**
- * Decodes the specified byte array to a POJO.
- *
- * @param data byte array.
- * @return POJO
- */
- Object decode(byte[] data);
-
- /**
- * Encodes the specified POJO into a byte array.
- *
- * @param data POJO to be encoded
- * @return byte array.
- */
- byte[] encode(Object message);
-
-}
diff --git a/utils/netty/src/main/java/org/onlab/netty/SimpleClient.java b/utils/netty/src/main/java/org/onlab/netty/SimpleClient.java
deleted file mode 100644
index 1573780..0000000
--- a/utils/netty/src/main/java/org/onlab/netty/SimpleClient.java
+++ /dev/null
@@ -1,24 +0,0 @@
-package org.onlab.netty;
-
-import java.util.concurrent.TimeUnit;
-
-public final class SimpleClient {
- private SimpleClient() {}
-
- public static void main(String... args) throws Exception {
- NettyMessagingService messaging = new TestNettyMessagingService(9081);
- messaging.activate();
-
- messaging.sendAsync(new Endpoint("localhost", 8080), "simple", "Hello World");
- Response<String> response = messaging.sendAndReceive(new Endpoint("localhost", 8080), "echo", "Hello World");
- System.out.println("Got back:" + response.get(2, TimeUnit.SECONDS));
- }
-
- public static class TestNettyMessagingService extends NettyMessagingService {
- public TestNettyMessagingService(int port) throws Exception {
- super(port);
- Serializer serializer = new KryoSerializer();
- this.serializer = serializer;
- }
- }
-}
diff --git a/utils/netty/src/main/java/org/onlab/netty/SimpleServer.java b/utils/netty/src/main/java/org/onlab/netty/SimpleServer.java
deleted file mode 100644
index 12fa025..0000000
--- a/utils/netty/src/main/java/org/onlab/netty/SimpleServer.java
+++ /dev/null
@@ -1,19 +0,0 @@
-package org.onlab.netty;
-
-public final class SimpleServer {
- private SimpleServer() {}
-
- public static void main(String... args) throws Exception {
- NettyMessagingService server = new TestNettyMessagingService();
- server.activate();
- server.registerHandler("simple", new LoggingHandler());
- server.registerHandler("echo", new EchoHandler());
- }
-
- public static class TestNettyMessagingService extends NettyMessagingService {
- protected TestNettyMessagingService() {
- Serializer serializer = new KryoSerializer();
- this.serializer = serializer;
- }
- }
-}
diff --git a/utils/netty/src/main/java/org/onlab/netty/package-info.java b/utils/netty/src/main/java/org/onlab/netty/package-info.java
index b1b90a3..fee7b04 100644
--- a/utils/netty/src/main/java/org/onlab/netty/package-info.java
+++ b/utils/netty/src/main/java/org/onlab/netty/package-info.java
@@ -1,4 +1,4 @@
/**
* Asynchronous messaging APIs implemented using the Netty framework.
*/
-package org.onlab.netty;
\ No newline at end of file
+package org.onlab.netty;
diff --git a/utils/netty/src/test/java/org/onlab/netty/PingPongTest.java b/utils/netty/src/test/java/org/onlab/netty/PingPongTest.java
new file mode 100644
index 0000000..36d2a1e
--- /dev/null
+++ b/utils/netty/src/test/java/org/onlab/netty/PingPongTest.java
@@ -0,0 +1,30 @@
+package org.onlab.netty;
+
+import java.util.concurrent.TimeUnit;
+
+import org.apache.commons.lang3.RandomUtils;
+import static org.junit.Assert.*;
+import org.junit.Test;
+
+/**
+ * Simple ping-pong test that exercises NettyMessagingService.
+ */
+public class PingPongTest {
+
+ @Test
+ public void testPingPong() throws Exception {
+ NettyMessagingService pinger = new NettyMessagingService(8085);
+ NettyMessagingService ponger = new NettyMessagingService(9086);
+ try {
+ pinger.activate();
+ ponger.activate();
+ ponger.registerHandler("echo", new EchoHandler());
+ byte[] payload = RandomUtils.nextBytes(100);
+ Response response = pinger.sendAndReceive(new Endpoint("localhost", 9086), "echo", payload);
+ assertArrayEquals(payload, response.get(10000, TimeUnit.MILLISECONDS));
+ } finally {
+ pinger.deactivate();
+ ponger.deactivate();
+ }
+ }
+}