Ayaka Koshibe

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

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

Change-Id: Ia1274657b27e01366a4a87196a13068d7104ee80
Showing 265 changed files with 2480 additions and 572 deletions
......@@ -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>
......
......@@ -11,6 +11,9 @@ import org.onlab.onos.cluster.ClusterService;
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 @@ public class FooComponent {
@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 class FooComponent {
public void deactivate() {
clusterService.removeListener(clusterListener);
deviceService.removeListener(deviceListener);
intentService.removeListener(intentListener);
log.info("Stopped");
}
......@@ -59,6 +68,23 @@ public class FooComponent {
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());
}
}
}
......
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());
}
}
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);
}
}
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);
}
}
}
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);
}
}
}
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());
}
}
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);
}
}
}
......@@ -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>
......
......@@ -26,9 +26,7 @@ import org.onlab.onos.net.packet.InboundPacket;
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 @@ import org.slf4j.Logger;
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 @@ public class ReactiveForwarding {
@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 @@ public class ReactiveForwarding {
@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 @@ public class ReactiveForwarding {
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 @@ public class ReactiveForwarding {
// 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();
TrafficSelector.Builder builder = DefaultTrafficSelector.builder();
builder.matchEthType(inPkt.getEtherType())
.matchEthSrc(inPkt.getSourceMAC())
.matchEthDst(inPkt.getDestinationMAC())
.matchInport(context.inPacket().receivedFrom().port());
TrafficTreatment.Builder treat = new DefaultTrafficTreatment.Builder();
TrafficTreatment.Builder treat = DefaultTrafficTreatment.builder();
treat.setOutput(portNumber);
FlowRule f = new DefaultFlowRule(context.inPacket().receivedFrom().deviceId(),
builder.build(), treat.build(), 0, appId, TIMEOUT);
builder.build(), treat.build(), PRIORITY, appId, TIMEOUT);
flowRuleService.applyFlowRules(f);
}
}
}
......
<?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>
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);
}
}
/**
* Trivial application that provides simple form of reactive forwarding
* using the intent service.
*/
package org.onlab.onos.ifwd;
......@@ -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>
......
<?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>
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);
}
}
}
/**
* Proxy Arp application that handles arp resolution for you.
*/
package org.onlab.onos.proxyarp;
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());
}
}
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);
}
}
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);
}
}
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);
}
}
......@@ -35,7 +35,7 @@ public class DevicesListCommand extends AbstractShellCommand {
* @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;
......
......@@ -5,7 +5,7 @@ import java.util.SortedSet;
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 @@ public class FlowRuleStatusCompleter implements Completer {
// 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());
......
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.cli.Comparators;
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 @@ public class FlowsListCommand extends AbstractShellCommand {
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,12 +58,13 @@ public class FlowsListCommand extends AbstractShellCommand {
* @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() :
Collections.singletonList(deviceService.getDevice(DeviceId.deviceId(uri)));
......@@ -71,7 +73,7 @@ public class FlowsListCommand extends AbstractShellCommand {
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 @@ public class FlowsListCommand extends AbstractShellCommand {
* @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;
}
for (FlowRule f : flows) {
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.lifeMillis(), f.priority());
f.packets(), f.life(), f.priority());
print(SFMT, f.selector().criteria());
print(TFMT, f.treatment().instructions());
}
}
}
}
......
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);
}
}
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);
}
}
}
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);
}
}
}
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.DeviceAdminService;
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);
}
}
}
}
......
......@@ -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>
......
......@@ -8,7 +8,7 @@ import java.util.concurrent.atomic.AtomicInteger;
*/
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 @@ public final class ApplicationId {
* @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());
}
}
......
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();
}
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;
}
}
......@@ -17,6 +17,7 @@ public interface MastershipService {
* 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);
......
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() {}
}
......@@ -51,6 +51,22 @@ public class ConnectPoint {
}
/**
* 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");
}
/**
* Returns the connection port number.
*
* @return port number
......
......@@ -73,31 +73,63 @@ public final class DefaultAnnotations implements SparseAnnotations {
}
/**
* 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,
public static SparseAnnotations union(SparseAnnotations annotations,
SparseAnnotations sparseAnnotations) {
if (sparseAnnotations == null || sparseAnnotations.keys().isEmpty()) {
return annotations;
}
final HashMap<String, String> newMap;
if (annotations instanceof DefaultAnnotations) {
return merge((DefaultAnnotations) annotations, sparseAnnotations);
newMap = copy(((DefaultAnnotations) annotations).map);
} else {
newMap = new HashMap<>(annotations.keys().size() +
sparseAnnotations.keys().size());
putAllSparseAnnotations(newMap, annotations);
}
putAllSparseAnnotations(newMap, sparseAnnotations);
return new DefaultAnnotations(newMap);
}
DefaultAnnotations.Builder builder = DefaultAnnotations.builder();
for (String key : annotations.keys()) {
builder.set(key, annotations.value(key));
// 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);
}
}
return merge(builder.build(), sparseAnnotations);
}
@Override
public Set<String> keys() {
// TODO: unmodifiable to be removed after switching to ImmutableMap;
return Collections.unmodifiableSet(map.keySet());
}
......@@ -115,7 +147,7 @@ public final class DefaultAnnotations implements SparseAnnotations {
@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");
}
......
......@@ -3,6 +3,7 @@ package org.onlab.onos.net;
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,7 +19,7 @@ public class DefaultEdgeLink extends DefaultLink implements EdgeLink {
* @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
*/
......@@ -42,4 +43,24 @@ public class DefaultEdgeLink extends DefaultLink implements EdgeLink {
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);
}
}
......
......@@ -10,6 +10,14 @@ import java.net.URI;
*/
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 @@ public final class HostId extends ElementId {
* @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);
}
/**
......
......@@ -22,6 +22,17 @@ public class HostLocation extends ConnectPoint {
}
/**
* 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.
*
......
......@@ -6,6 +6,7 @@ import com.google.common.base.MoreObjects;
// 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 @@ public class LinkKey {
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);
......
......@@ -9,6 +9,8 @@ import com.google.common.primitives.UnsignedLongs;
*/
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;
......
......@@ -96,4 +96,13 @@ public class DefaultDeviceDescription extends AbstractDescription
.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;
}
}
......
......@@ -48,4 +48,9 @@ public class DefaultPortDescription extends AbstractDescription
return isEnabled;
}
// default constructor for serialization
private DefaultPortDescription() {
this.number = null;
this.isEnabled = false;
}
}
......
package org.onlab.onos.net.flow;
public class CompletedBatchOperation {
}
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();
}
}
......@@ -18,10 +18,6 @@ public class DefaultFlowRule implements FlowRule {
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 @@ public class DefaultFlowRule implements FlowRule {
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 @@ public class DefaultFlowRule implements FlowRule {
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 class DefaultFlowRule implements FlowRule {
}
public int hash() {
return Objects.hash(deviceId, selector, id);
return Objects.hash(deviceId, selector, treatment);
}
@Override
......@@ -179,7 +132,7 @@ public class DefaultFlowRule implements FlowRule {
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 @@ public class DefaultFlowRule implements FlowRule {
@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;
}
}
......
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 @@ public final class DefaultTrafficSelector implements TrafficSelector {
}
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 @@ public final class DefaultTrafficSelector implements TrafficSelector {
@Override
public TrafficSelector build() {
return new DefaultTrafficSelector(selector);
return new DefaultTrafficSelector(ImmutableSet.copyOf(selector.values()));
}
}
......
......@@ -5,6 +5,7 @@ import static org.slf4j.LoggerFactory.getLogger;
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.MacAddress;
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 @@ public final class DefaultTrafficTreatment implements TrafficTreatment {
}
/**
* Builds a list of treatments following the following order.
* Modifications -> Group -> Output (including drop)
* 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);
}
public static class Builder implements TrafficTreatment.Builder {
@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 final class Builder implements TrafficTreatment.Builder {
private final Logger log = getLogger(getClass());
......@@ -47,6 +82,11 @@ public final class DefaultTrafficTreatment implements TrafficTreatment {
// 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;
......
package org.onlab.onos.net.flow;
/**
* Represents a generalized match &amp; 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);
}
......@@ -26,6 +26,9 @@ public final class FlowId {
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);
......
......@@ -2,49 +2,16 @@ package org.onlab.onos.net.flow;
import org.onlab.onos.ApplicationId;
import org.onlab.onos.net.DeviceId;
import org.onlab.onos.net.intent.BatchOperationTarget;
/**
* Represents a generalized match &amp; 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 @@ public interface FlowRule {
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
*/
......
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
}
}
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);
}
}
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 @@ public interface FlowRuleProvider extends Provider {
*/
void removeRulesById(ApplicationId id, FlowRule... flowRules);
Future<Void> executeBatch(BatchOperation<FlowRuleBatchEntry> batch);
}
......
......@@ -14,7 +14,7 @@ public interface FlowRuleProviderService extends ProviderService<FlowRuleProvide
*
* @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 @@ public interface FlowRuleProviderService extends ProviderService<FlowRuleProvide
*
* @param flowRules collection of flow rules
*/
void pushFlowMetrics(DeviceId deviceId, Iterable<FlowRule> flowRules);
void pushFlowMetrics(DeviceId deviceId, Iterable<FlowEntry> flowEntries);
......
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 @@ import org.onlab.onos.net.DeviceId;
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 @@ public interface FlowRuleService {
* @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 @@ public interface FlowRuleService {
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 @@ public interface FlowRuleService {
* @param listener flow rule listener
*/
void removeListener(FlowRuleListener listener);
}
......
......@@ -10,11 +10,19 @@ import org.onlab.onos.store.Store;
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 @@ public interface FlowRuleStore extends Store<FlowRuleEvent, FlowRuleStoreDelegat
* @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 @@ public interface FlowRuleStore extends Store<FlowRuleEvent, FlowRuleStoreDelegat
* @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 @@ public interface FlowRuleStore extends Store<FlowRuleEvent, FlowRuleStoreDelegat
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 @@ public interface FlowRuleStore extends Store<FlowRuleEvent, FlowRuleStoreDelegat
* @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);
}
......
......@@ -3,6 +3,8 @@ package org.onlab.onos.net.flow.instructions;
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 @@ public final class Instructions {
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 @@ public final class Instructions {
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;
}
}
}
......
......@@ -2,6 +2,8 @@ package org.onlab.onos.net.flow.instructions;
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 @@ public abstract class L2ModificationInstruction implements Instruction {
.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 @@ public abstract class L2ModificationInstruction implements Instruction {
.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 @@ public abstract class L2ModificationInstruction implements Instruction {
.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;
}
}
......
......@@ -2,6 +2,8 @@ package org.onlab.onos.net.flow.instructions;
import static com.google.common.base.MoreObjects.toStringHelper;
import java.util.Objects;
import org.onlab.packet.IpPrefix;
/**
......@@ -66,5 +68,23 @@ public abstract class L3ModificationInstruction implements Instruction {
.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;
}
}
}
......
......@@ -24,7 +24,7 @@ public abstract class AbstractIntent implements Intent {
}
@Override
public IntentId getId() {
public IntentId id() {
return id;
}
......
......@@ -3,6 +3,7 @@ package org.onlab.onos.net.intent;
import static com.google.common.base.Preconditions.checkNotNull;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
......@@ -12,10 +13,10 @@ import java.util.List;
*
* @param <T> the enum of operators <br>
* 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 @@ public abstract class BatchOperation<T extends BatchOperationEntry<?, ?>> {
*
* @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 @@ public abstract class BatchOperation<T extends BatchOperationEntry<?, ?>> {
/**
* 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
......
......@@ -15,14 +15,7 @@ public class BatchOperationEntry<T extends Enum<?>, U extends BatchOperationTarg
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.
......
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 @@ public abstract class ConnectivityIntent extends AbstractIntent {
/**
* 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 @@ public abstract class ConnectivityIntent extends AbstractIntent {
*
* @return traffic match
*/
public TrafficSelector getTrafficSelector() {
public TrafficSelector selector() {
return selector;
}
......@@ -62,7 +62,7 @@ public abstract class ConnectivityIntent extends AbstractIntent {
*
* @return applied action
*/
public TrafficTreatment getTrafficTreatment() {
public TrafficTreatment treatment() {
return treatment;
}
......
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();
}
}
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();
}
......
......@@ -2,7 +2,7 @@ package org.onlab.onos.net.intent;
/**
* 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 @@ public interface Intent extends BatchOperationTarget {
*
* @return intent identifier
*/
IntentId getId();
IntentId id();
}
......
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 {
// TODO: determine a suitable parent class; if one does not exist, consider introducing one
private final long time;
private final Intent intent;
private final IntentState state;
private final IntentState previous;
public class IntentEvent extends AbstractEvent<IntentEvent.Type, Intent> {
public enum Type {
/**
* Creates an event describing a state change of an intent.
*
* @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
* Signifies that a new intent has been submitted to the system.
*/
public IntentEvent(Intent intent, IntentState state, IntentState previous, long time) {
this.intent = checkNotNull(intent);
this.state = checkNotNull(state);
this.previous = previous;
this.time = time;
}
SUBMITTED,
/**
* Constructor for serializer.
* Signifies that an intent has been successfully installed.
*/
protected IntentEvent() {
this.intent = null;
this.state = null;
this.previous = null;
this.time = 0;
}
INSTALLED,
/**
* Returns the state of the intent which caused the event.
*
* @return the state of the intent
* Signifies that an intent has failed compilation or installation.
*/
public IntentState getState() {
return state;
}
FAILED,
/**
* Returns the previous state of the intent which caused the event.
*
* @return the previous state of the intent
* Signifies that an intent has been withdrawn from the system.
*/
public IntentState getPreviousState() {
return previous;
WITHDRAWN
}
/**
* Returns the intent associated with the event.
* Creates an event of a given type and for the specified intent and the
* current time.
*
* @return the intent
* @param type event type
* @param intent subject intent
* @param time time the event created in milliseconds since start of epoch
*/
public Intent getIntent() {
return intent;
public IntentEvent(Type type, Intent intent, long time) {
super(type, intent, time);
}
/**
* Returns the time at which the event was created.
* Creates an event of a given type and for the specified intent and the
* current time.
*
* @return the time in milliseconds since start of epoch
* @param type event type
* @param intent subject intent
*/
public long getTime() {
return time;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
public IntentEvent(Type type, Intent intent) {
super(type, intent);
}
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();
}
}
......
......@@ -2,7 +2,7 @@ package org.onlab.onos.net.intent;
/**
* Intent identifier suitable as an external key.
*
* <p/>
* This class is immutable.
*/
public final class IntentId implements BatchOperationTarget {
......
package org.onlab.onos.net.intent;
import org.onlab.onos.event.EventListener;
/**
* Listener for {@link IntentEvent intent events}.
*/
public interface IntentEventListener {
/**
* Processes the specified intent event.
*
* @param event the event to process
*/
void event(IntentEvent event);
public interface IntentListener extends EventListener<IntentEvent> {
}
......
package org.onlab.onos.net.intent;
import java.util.Set;
/**
* Service for application submitting or withdrawing their intents.
......@@ -8,9 +7,9 @@ import java.util.Set;
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 @@ public interface IntentService {
/**
* 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 @@ public interface IntentService {
/**
* Submits a batch of submit &amp; 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 @@ public interface IntentService {
* 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 @@ public interface IntentService {
*
* @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);
}
......
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.
*/
COMPILING,
/**
* 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.
*/
COMPILED,
INSTALLING,
/**
* The intent has been successfully installed.
* 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
}
......
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);
}
package org.onlab.onos.net.intent;
import org.onlab.onos.store.StoreDelegate;
/**
* Intent store delegate abstraction.
*/
public interface IntentStoreDelegate extends StoreDelegate<IntentEvent> {
}
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 @@ public class MultiPointToSinglePointIntent extends ConnectivityIntent {
* @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
* @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(),
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 @@ public class MultiPointToSinglePointIntent extends ConnectivityIntent {
*/
protected MultiPointToSinglePointIntent() {
super();
this.ingressPorts = null;
this.egressPort = null;
this.ingressPoints = null;
this.egressPoint = null;
}
/**
......@@ -62,8 +63,8 @@ public class MultiPointToSinglePointIntent extends ConnectivityIntent {
*
* @return set of ingress ports
*/
public Set<ConnectPoint> getIngressPorts() {
return ingressPorts;
public Set<ConnectPoint> ingressPoints() {
return ingressPoints;
}
/**
......@@ -71,8 +72,8 @@ public class MultiPointToSinglePointIntent extends ConnectivityIntent {
*
* @return egress port
*/
public ConnectPoint getEgressPort() {
return egressPort;
public ConnectPoint egressPoint() {
return egressPoint;
}
@Override
......@@ -88,23 +89,23 @@ public class MultiPointToSinglePointIntent extends ConnectivityIntent {
}
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();
}
}
......
......@@ -3,30 +3,30 @@ package org.onlab.onos.net.intent;
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 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 @@ public class OpticalConnectivityIntent extends AbstractIntent {
*/
protected OpticalConnectivityIntent() {
super();
this.srcConnectPoint = null;
this.dstConnectPoint = null;
this.src = null;
this.dst = null;
}
/**
......@@ -44,7 +44,7 @@ public class OpticalConnectivityIntent extends AbstractIntent {
* @return The source transponder port.
*/
public ConnectPoint getSrcConnectPoint() {
return srcConnectPoint;
return src;
}
/**
......@@ -52,7 +52,7 @@ public class OpticalConnectivityIntent extends AbstractIntent {
*
* @return The source transponder port.
*/
public ConnectPoint getDstConnectPoint() {
return dstConnectPoint;
public ConnectPoint getDst() {
return dst;
}
}
......
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 @@ public class PathIntent extends PointToPointIntent {
*
* @return traversed links
*/
public Path getPath() {
public Path path() {
return path;
}
......@@ -78,12 +79,18 @@ public class PathIntent extends PointToPointIntent {
@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();
}
}
......
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 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 @@ public class PointToPointIntent extends ConnectivityIntent {
*/
protected PointToPointIntent() {
super();
this.ingressPort = null;
this.egressPort = null;
this.ingressPoint = null;
this.egressPoint = null;
}
/**
......@@ -51,8 +52,8 @@ public class PointToPointIntent extends ConnectivityIntent {
*
* @return ingress port
*/
public ConnectPoint getIngressPort() {
return ingressPort;
public ConnectPoint ingressPoint() {
return ingressPoint;
}
/**
......@@ -60,8 +61,8 @@ public class PointToPointIntent extends ConnectivityIntent {
*
* @return egress port
*/
public ConnectPoint getEgressPort() {
return egressPort;
public ConnectPoint egressPoint() {
return egressPoint;
}
@Override
......@@ -77,23 +78,23 @@ public class PointToPointIntent extends ConnectivityIntent {
}
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();
}
......
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
* @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);
checkNotNull(egressPorts);
checkArgument(!egressPorts.isEmpty(),
public SinglePointToMultiPointIntent(IntentId id, TrafficSelector selector,
TrafficTreatment treatment,
ConnectPoint ingressPoint,
Set<ConnectPoint> egressPoints) {
super(id, selector, treatment);
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 @@ public class SinglePointToMultiPointIntent extends ConnectivityIntent {
*/
protected SinglePointToMultiPointIntent() {
super();
this.ingressPort = null;
this.egressPorts = null;
this.ingressPoint = null;
this.egressPoints = null;
}
/**
......@@ -61,8 +61,8 @@ public class SinglePointToMultiPointIntent extends ConnectivityIntent {
*
* @return ingress port
*/
public ConnectPoint getIngressPort() {
return ingressPort;
public ConnectPoint ingressPoint() {
return ingressPoint;
}
/**
......@@ -70,8 +70,8 @@ public class SinglePointToMultiPointIntent extends ConnectivityIntent {
*
* @return set of egress ports
*/
public Set<ConnectPoint> getEgressPorts() {
return egressPorts;
public Set<ConnectPoint> egressPoints() {
return egressPoints;
}
@Override
......@@ -87,23 +87,23 @@ public class SinglePointToMultiPointIntent extends ConnectivityIntent {
}
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();
}
......
/**
* 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
......
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.
......
......@@ -24,7 +24,7 @@ public abstract class DefaultPacketContext implements PacketContext {
this.inPkt = inPkt;
this.outPkt = outPkt;
this.block = new AtomicBoolean(block);
this.builder = new DefaultTrafficTreatment.Builder();
this.builder = DefaultTrafficTreatment.builder();
}
@Override
......
......@@ -3,6 +3,7 @@ package org.onlab.onos.net.provider;
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 @@ import static com.google.common.base.MoreObjects.toStringHelper;
*/
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 @@ public class ProviderId {
* @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;
}
......
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 @@ public interface ProxyArpService {
*/
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);
}
......
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 @@ public class TopologyEvent extends AbstractEvent<TopologyEvent.Type, Topology> {
*
* @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 @@ public class TopologyEvent extends AbstractEvent<TopologyEvent.Type, Topology> {
*
* @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;
}
}
......
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);
}
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 @@ public interface ClockService {
* @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);
}
......
......@@ -2,7 +2,15 @@ package org.onlab.onos.store;
/**
* 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);
}
......
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
......@@ -36,6 +36,23 @@ public class DefaultAnnotationsTest {
}
@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());
......
......@@ -58,7 +58,5 @@ public class DefaultDeviceTest {
assertEquals("incorrect hw", HW, device.hwVersion());
assertEquals("incorrect sw", SW, device.swVersion());
assertEquals("incorrect serial", SN1, device.serialNumber());
assertEquals("incorrect serial", SN1, device.serialNumber());
}
}
......
......@@ -5,6 +5,7 @@ import org.junit.Test;
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 @@ public class DefaultEdgeLinkTest {
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());
}
}
......
......@@ -39,7 +39,7 @@ public class DeviceEventTest extends AbstractEventTest {
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);
}
......
......@@ -16,8 +16,8 @@ import org.onlab.onos.net.flow.TrafficTreatment;
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));
......
......@@ -11,19 +11,19 @@ import java.util.concurrent.ExecutorService;
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 @@ public class FakeIntentManager implements TestableIntentService {
@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 @@ public class FakeIntentManager implements TestableIntentService {
@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 @@ public class FakeIntentManager implements TestableIntentService {
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 @@ public class FakeIntentManager implements TestableIntentService {
@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 @@ public class FakeIntentManager implements TestableIntentService {
}
@Override
public long getIntentCount() {
return intents.size();
}
@Override
public Intent getIntent(IntentId id) {
return intents.get(id);
}
......@@ -185,23 +196,24 @@ public class FakeIntentManager implements TestableIntentService {
}
@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 @@ public class FakeIntentManager implements TestableIntentService {
}
@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);
}
......@@ -252,7 +265,8 @@ public class FakeIntentManager implements TestableIntentService {
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) {
......
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.