Ayaka Koshibe

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

Showing 147 changed files with 1934 additions and 825 deletions
......@@ -18,18 +18,20 @@
<dependencies>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-core-asl</artifactId>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-core-asl</artifactId>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.4.2</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.onlab.onos</groupId>
<artifactId>onlab-misc</artifactId>
</dependency>
</dependencies>
......
......@@ -41,5 +41,17 @@
<groupId>org.apache.karaf.shell</groupId>
<artifactId>org.apache.karaf.shell.console</artifactId>
</dependency>
<dependency>
<groupId>org.onlab.onos</groupId>
<artifactId>onlab-misc</artifactId>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>
<dependency>
<groupId>org.osgi</groupId>
<artifactId>org.osgi.core</artifactId>
</dependency>
</dependencies>
</project>
......
......@@ -4,8 +4,6 @@ import java.io.IOException;
import org.onlab.netty.Message;
import org.onlab.netty.MessageHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
......@@ -13,11 +11,8 @@ import org.slf4j.LoggerFactory;
*/
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());
}
}
......
......@@ -8,12 +8,12 @@ import org.slf4j.LoggerFactory;
/**
* A MessageHandler that simply logs the information.
*/
public class NettyLoggingHandler implements MessageHandler {
public class NettyNothingHandler 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);
// Do nothing
}
}
......
package org.onlab.onos.foo;
import static java.lang.Thread.sleep;
import java.io.IOException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.onlab.metrics.MetricsComponent;
......@@ -15,14 +18,29 @@ import org.slf4j.LoggerFactory;
import com.codahale.metrics.Timer;
/**
* The Simple netty client test.
*/
// FIXME: Should be move out to test or app
public final class SimpleNettyClient {
private static Logger log = LoggerFactory.getLogger(SimpleNettyClient.class);
static NettyMessagingService messaging;
static MetricsManager metrics;
private SimpleNettyClient() {
}
/**
* The entry point of application.
*
* @param args the input arguments
* @throws IOException the iO exception
* @throws InterruptedException the interrupted exception
* @throws ExecutionException the execution exception
* @throws TimeoutException the timeout exception
*/
public static void main(String[] args)
throws IOException, InterruptedException, ExecutionException,
TimeoutException {
......@@ -34,48 +52,87 @@ private static Logger log = LoggerFactory.getLogger(SimpleNettyClient.class);
System.exit(0);
}
public static void startStandalone(String... args) throws Exception {
/**
* Start standalone.
*
* @param args the args
* @throws Exception the exception
*/
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 = new TestNettyMessagingService(9081);
metrics = new MetricsManager();
Endpoint endpoint = new Endpoint(host, port);
messaging.activate();
metrics.activate();
MetricsFeature feature = new MetricsFeature("latency");
MetricsComponent component = metrics.registerComponent("NettyMessaging");
log.info("warmup....");
log.info("connecting " + host + ":" + port + " warmup:" + warmup + " iterations:" + iterations);
for (int i = 0; i < warmup; i++) {
messaging.sendAsync(endpoint, "simple", "Hello World".getBytes());
Response response = messaging
.sendAndReceive(endpoint, "echo",
"Hello World".getBytes());
response.get(100000, TimeUnit.MILLISECONDS);
}
log.info("measuring round-trip send & receive");
Timer sendAndReceiveTimer = metrics.createTimer(component, feature, "SendAndReceive");
int timeouts = 0;
for (int i = 0; i < iterations; i++) {
Response response;
Timer.Context context = sendAndReceiveTimer.time();
try {
response = messaging
.sendAndReceive(endpoint, "echo",
"Hello World".getBytes());
response.get(10000, TimeUnit.MILLISECONDS);
} catch (TimeoutException e) {
timeouts++;
log.info("timeout:" + timeouts + " at iteration:" + i);
} finally {
context.stop();
}
// System.out.println("Got back:" + new String(response.get(2, TimeUnit.SECONDS)));
}
//sleep(1000);
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(endpoint, "simple", "Hello World".getBytes());
context.stop();
Timer.Context context = sendAsyncTimer.time();
messaging.sendAsync(endpoint, "simple", "Hello World".getBytes());
context.stop();
}
sleep(10000);
}
Timer sendAndReceiveTimer = metrics.createTimer(component, feature, "SendAndReceive");
for (int i = 0; i < iterations; i++) {
Timer.Context context = sendAndReceiveTimer.time();
Response response = messaging
.sendAndReceive(endpoint, "echo",
"Hello World".getBytes());
// System.out.println("Got back:" + new String(response.get(2, TimeUnit.SECONDS)));
context.stop();
public static void stop() {
try {
messaging.deactivate();
metrics.deactivate();
} catch (Exception e) {
log.info("Unable to stop client %s", e);
}
}
/**
* The type Test netty messaging service.
*/
public static class TestNettyMessagingService extends NettyMessagingService {
/**
* Instantiates a new Test netty messaging service.
*
* @param port the port
* @throws Exception the exception
*/
public TestNettyMessagingService(int port) throws Exception {
super(port);
}
......
package org.onlab.onos.foo;
import static org.onlab.onos.foo.SimpleNettyClient.startStandalone;
import static org.onlab.onos.foo.SimpleNettyClient.stop;
import org.apache.karaf.shell.commands.Argument;
import org.apache.karaf.shell.commands.Command;
......@@ -10,7 +11,7 @@ import org.onlab.onos.cli.AbstractShellCommand;
* Test Netty client performance.
*/
@Command(scope = "onos", name = "simple-netty-client",
description = "Starts the simple Netty client")
description = "Starts simple Netty client")
public class SimpleNettyClientCommand extends AbstractShellCommand {
//FIXME: replace these arguments with proper ones needed for the test.
......@@ -18,17 +19,17 @@ public class SimpleNettyClientCommand extends AbstractShellCommand {
required = false, multiValued = false)
String hostname = "localhost";
@Argument(index = 3, name = "port", description = "Port",
@Argument(index = 1, name = "port", description = "Port",
required = false, multiValued = false)
String port = "8081";
@Argument(index = 1, name = "warmupCount", description = "Warm-up count",
@Argument(index = 2, name = "warmupCount", description = "Warm-up count",
required = false, multiValued = false)
String warmupCount = "1000";
@Argument(index = 2, name = "messageCount", description = "Message count",
@Argument(index = 3, name = "messageCount", description = "Message count",
required = false, multiValued = false)
String messageCount = "100000";
String messageCount = "1000000";
@Override
protected void execute() {
......@@ -37,5 +38,6 @@ public class SimpleNettyClientCommand extends AbstractShellCommand {
} catch (Exception e) {
error("Unable to start client %s", e);
}
stop();
}
}
......
......@@ -12,16 +12,30 @@ import org.slf4j.LoggerFactory;
private SimpleNettyServer() {}
public static void main(String... args) throws Exception {
/**
* The entry point of application.
*
* @param args the input arguments
* @throws Exception the exception
*/
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);
/**
* Start standalone server.
*
* @param args the args
* @throws Exception the exception
*/
public static void startStandalone(String[] args) throws Exception {
int port = args.length > 0 ? Integer.parseInt(args[0]) : 8081;
NettyMessagingService server = new NettyMessagingService(port);
server.activate();
server.registerHandler("simple", new NettyLoggingHandler());
server.registerHandler("simple", new NettyNothingHandler());
server.registerHandler("echo", new NettyEchoHandler());
log.info("Netty Server server on port " + port);
}
}
......
......@@ -10,26 +10,18 @@ import org.onlab.onos.cli.AbstractShellCommand;
* Starts the Simple Netty server.
*/
@Command(scope = "onos", name = "simple-netty-server",
description = "Starts the simple netty server")
description = "Starts simple Netty server")
public class SimpleNettyServerCommand extends AbstractShellCommand {
//FIXME: Replace these with parameters for
@Argument(index = 0, name = "serverIp", description = "Server IP address",
@Argument(index = 0, name = "port", description = "listen port",
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";
String port = "8081";
@Override
protected void execute() {
try {
startStandalone(new String[]{serverIp, workers, messageLength});
startStandalone(new String[]{port});
} catch (Exception e) {
error("Unable to start server %s", e);
}
......
......@@ -10,6 +10,7 @@ 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.CoreService;
import org.onlab.onos.net.Host;
import org.onlab.onos.net.HostId;
import org.onlab.onos.net.Path;
......@@ -53,13 +54,16 @@ public class ReactiveForwarding {
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected FlowRuleService flowRuleService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected CoreService coreService;
private ReactivePacketProcessor processor = new ReactivePacketProcessor();
private ApplicationId appId;
@Activate
public void activate() {
appId = ApplicationId.getAppId();
appId = coreService.registerApplication("org.onlab.onos.fwd");
packetService.addProcessor(processor, PacketProcessor.ADVISOR_MAX + 2);
log.info("Started with Application ID {}", appId.id());
}
......@@ -166,8 +170,6 @@ public class ReactiveForwarding {
// We don't yet support bufferids in the flowservice so packet out first.
packetOut(context, portNumber);
// Install the flow rule to handle this type of message from now on.
Ethernet inPkt = context.inPacket().parsed();
TrafficSelector.Builder builder = DefaultTrafficSelector.builder();
......
......@@ -16,4 +16,14 @@
<description>ONOS simple Mobility app</description>
<dependencies>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>
<dependency>
<groupId>org.onlab.onos</groupId>
<artifactId>onlab-misc</artifactId>
</dependency>
</dependencies>
</project>
......
......@@ -10,6 +10,7 @@ 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.CoreService;
import org.onlab.onos.net.Device;
import org.onlab.onos.net.Host;
import org.onlab.onos.net.device.DeviceService;
......@@ -44,11 +45,14 @@ public class HostMobility {
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected DeviceService deviceService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected CoreService coreService;
private ApplicationId appId;
@Activate
public void activate() {
appId = ApplicationId.getAppId();
appId = coreService.registerApplication("org.onlab.onos.mobility");
hostService.addListener(new InternalHostListener());
log.info("Started with Application ID {}", appId.id());
}
......
......@@ -23,7 +23,8 @@
<module>foo</module>
<module>mobility</module>
<module>proxyarp</module>
<module>config</module>
<module>config</module>
<module>sdnip</module>
</modules>
<properties>
......
......@@ -8,6 +8,7 @@ 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.CoreService;
import org.onlab.onos.net.packet.PacketContext;
import org.onlab.onos.net.packet.PacketProcessor;
import org.onlab.onos.net.packet.PacketService;
......@@ -31,11 +32,14 @@ public class ProxyArp {
private ProxyArpProcessor processor = new ProxyArpProcessor();
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected CoreService coreService;
private ApplicationId appId;
@Activate
public void activate() {
appId = ApplicationId.getAppId();
appId = coreService.registerApplication("org.onlab.onos.proxyarp");
packetService.addProcessor(processor, PacketProcessor.ADVISOR_MAX + 1);
log.info("Started with Application ID {}", appId.id());
}
......
<?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-sdnip</artifactId>
<packaging>bundle</packaging>
<description>SDN-IP peering application</description>
<dependencies>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-core-asl</artifactId>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.4.2</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>
package org.onlab.onos.sdnip;
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.slf4j.Logger;
/**
* Placeholder SDN-IP component.
*/
@Component(immediate = true)
public class SdnIp {
private final Logger log = getLogger(getClass());
@Activate
protected void activate() {
log.debug("SDN-IP started");
}
@Deactivate
protected void deactivate() {
log.info("Stopped");
}
}
/**
* SDN-IP peering application.
*/
package org.onlab.onos.sdnip;
\ No newline at end of file
......@@ -141,7 +141,7 @@ public class TopologyResource extends BaseResource {
private ObjectNode json(ObjectMapper mapper, ElementId id, int group,
String label, boolean isOnline) {
return mapper.createObjectNode()
.put("name", id.uri().toString())
.put("name", id.toString())
.put("label", label)
.put("group", group)
.put("online", isOnline);
......@@ -202,7 +202,7 @@ public class TopologyResource extends BaseResource {
// Returns a formatted string for the element associated with the given
// connection point.
private static String id(ConnectPoint cp) {
return cp.elementId().uri().toString();
return cp.elementId().toString();
}
}
......
/**
* REST resources for the sample topology viewer application.
* Sample topology viewer application.
*/
package org.onlab.onos.tvue;
......
......@@ -21,14 +21,14 @@ public final class Comparators {
public static final Comparator<ElementId> ELEMENT_ID_COMPARATOR = new Comparator<ElementId>() {
@Override
public int compare(ElementId id1, ElementId id2) {
return id1.uri().toString().compareTo(id2.uri().toString());
return id1.toString().compareTo(id2.toString());
}
};
public static final Comparator<Element> ELEMENT_COMPARATOR = new Comparator<Element>() {
@Override
public int compare(Element e1, Element e2) {
return e1.id().uri().toString().compareTo(e2.id().uri().toString());
return e1.id().toString().compareTo(e2.id().toString());
}
};
......
......@@ -22,8 +22,10 @@ public class SummaryCommand extends AbstractShellCommand {
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(),
print("node=%s, version=%s",
get(ClusterService.class).getLocalNode().ip(),
get(CoreService.class).version().toString());
print("nodes=%d, devices=%d, links=%d, hosts=%d, clusters=%s, paths=%d, flows=%d, intents=%d",
get(ClusterService.class).getNodes().size(),
get(DeviceService.class).getDeviceCount(),
get(LinkService.class).getLinkCount(),
......
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;
import java.util.Map;
import com.google.common.collect.Maps;
import org.apache.karaf.shell.commands.Argument;
import org.apache.karaf.shell.commands.Command;
import org.onlab.onos.CoreService;
import org.onlab.onos.cli.AbstractShellCommand;
import org.onlab.onos.cli.Comparators;
import org.onlab.onos.net.Device;
......@@ -18,37 +13,43 @@ 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;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import static com.google.common.collect.Lists.newArrayList;
import static org.onlab.onos.cli.net.DevicesListCommand.getSortedDevices;
/**
* Lists all currently-known hosts.
*/
@Command(scope = "onos", name = "flows",
description = "Lists all currently-known flows.")
description = "Lists all currently-known flows.")
public class FlowsListCommand extends AbstractShellCommand {
public static final String ANY = "any";
private static final String FMT =
" id=%s, state=%s, bytes=%s, packets=%s, duration=%s, priority=%s";
" id=%s, state=%s, bytes=%s, packets=%s, duration=%s, priority=%s, appId=%s";
private static final String TFMT = " treatment=%s";
private static final String SFMT = " selector=%s";
@Argument(index = 1, name = "uri", description = "Device ID",
required = false, multiValued = false)
required = false, multiValued = false)
String uri = null;
@Argument(index = 0, name = "state", description = "Flow Rule state",
required = false, multiValued = false)
required = false, multiValued = false)
String state = null;
@Override
protected void execute() {
CoreService coreService = get(CoreService.class);
DeviceService deviceService = get(DeviceService.class);
FlowRuleService service = get(FlowRuleService.class);
Map<Device, List<FlowEntry>> flows = getSortedFlows(deviceService, service);
for (Device d : getSortedDevices(deviceService)) {
printFlows(d, flows.get(d));
printFlows(d, flows.get(d), coreService);
}
}
......@@ -67,7 +68,7 @@ public class FlowsListCommand extends AbstractShellCommand {
s = FlowEntryState.valueOf(state.toUpperCase());
}
Iterable<Device> devices = uri == null ? deviceService.getDevices() :
Collections.singletonList(deviceService.getDevice(DeviceId.deviceId(uri)));
Collections.singletonList(deviceService.getDevice(DeviceId.deviceId(uri)));
for (Device d : devices) {
if (s == null) {
rules = newArrayList(service.getFlowEntries(d.id()));
......@@ -87,16 +88,19 @@ public class FlowsListCommand extends AbstractShellCommand {
/**
* Prints flows.
* @param d the device
*
* @param d the device
* @param flows the set of flows for that device.
*/
protected void printFlows(Device d, List<FlowEntry> flows) {
protected void printFlows(Device d, List<FlowEntry> flows,
CoreService coreService) {
boolean empty = flows == null || flows.isEmpty();
print("deviceId=%s, flowRuleCount=%d", d.id(), empty ? 0 : flows.size());
if (!empty) {
for (FlowEntry f : flows) {
print(FMT, Long.toHexString(f.id().value()), f.state(), f.bytes(),
f.packets(), f.life(), f.priority());
print(FMT, Long.toHexString(f.id().value()), f.state(),
f.bytes(), f.packets(), f.life(), f.priority(),
coreService.getAppId(f.appId()).name());
print(SFMT, f.selector().criteria());
print(TFMT, f.treatment().instructions());
}
......
package org.onlab.onos.cli.net;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
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.IntentEvent;
import org.onlab.onos.net.intent.IntentEvent.Type;
import org.onlab.onos.net.intent.IntentId;
import org.onlab.onos.net.intent.IntentListener;
import org.onlab.onos.net.intent.IntentService;
import org.onlab.onos.net.intent.PointToPointIntent;
import org.onlab.packet.Ethernet;
import org.onlab.packet.MacAddress;
/**
* Installs point-to-point connectivity intents.
*/
@Command(scope = "onos", name = "push-test-intents",
description = "Installs random intents to test throughput")
public class IntentPushTestCommand extends AbstractShellCommand
implements IntentListener {
@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;
@Argument(index = 2, name = "count",
description = "Number of intents to push",
required = true, multiValued = false)
String countString = null;
private static long id = 0x7870001;
private IntentService service;
private CountDownLatch latch;
private long start, end;
@Override
protected void execute() {
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.Builder selector = DefaultTrafficSelector.builder()
.matchEthType(Ethernet.TYPE_IPV4);
TrafficTreatment treatment = DefaultTrafficTreatment.builder().build();
int count = Integer.parseInt(countString);
service.addListener(this);
latch = new CountDownLatch(count);
start = System.currentTimeMillis();
for (int i = 0; i < count; i++) {
TrafficSelector s = selector
.matchEthSrc(MacAddress.valueOf(i))
.build();
Intent intent =
new PointToPointIntent(new IntentId(id++),
s,
treatment,
ingress,
egress);
service.submit(intent);
}
try {
latch.await(5, TimeUnit.SECONDS);
printResults(count);
} catch (InterruptedException e) {
print(e.toString());
}
service.removeListener(this);
}
private void printResults(int count) {
long delta = end - start;
print("Time to install %d intents: %d ms", count, delta);
}
/**
* 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);
}
@Override
public void event(IntentEvent event) {
if (event.type() == Type.INSTALLED) {
end = event.time();
if (latch != null) {
latch.countDown();
} else {
log.warn("install event latch is null");
}
}
}
}
......@@ -19,17 +19,16 @@ import org.onlab.onos.net.intent.IntentState;
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.";
private static final String PLEASE = "please";
@Argument(index = 0, name = "disclaimer", description = "Device ID",
@Argument(index = 0, name = "please", description = "Confirmation phrase",
required = false, multiValued = false)
String disclaimer = null;
String please = 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);
if (please == null || !please.equals(PLEASE)) {
print("I'm afraid I can't do that!\nSay: %s", PLEASE);
return;
}
......
......@@ -82,6 +82,13 @@
<ref component-id="connectPointCompleter"/>
</completers>
</command>
<command>
<action class="org.onlab.onos.cli.net.IntentPushTestCommand"/>
<completers>
<ref component-id="connectPointCompleter"/>
<ref component-id="connectPointCompleter"/>
</completers>
</command>
<command>
<action class="org.onlab.onos.cli.net.ClustersListCommand"/>
......
package org.onlab.onos;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicInteger;
/**
* Application id generator class.
* Application identifier.
*/
public final class ApplicationId {
public interface ApplicationId {
private static final AtomicInteger ID_DISPENCER = new AtomicInteger(1);
private final Integer id;
// Ban public construction
private ApplicationId(Integer id) {
this.id = id;
}
public Integer id() {
return id;
}
public static ApplicationId valueOf(Integer id) {
return new ApplicationId(id);
}
@Override
public int hashCode() {
return Objects.hash(id);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (!(obj instanceof ApplicationId)) {
return false;
}
ApplicationId other = (ApplicationId) obj;
return Objects.equals(this.id, other.id);
}
/**
* Returns the application id.
* @return a short value
*/
short id();
/**
* Returns a new application id.
*
* @return app id
* Returns the applications supplied identifier.
* @return a string identifier
*/
public static ApplicationId getAppId() {
return new ApplicationId(ApplicationId.ID_DISPENCER.getAndIncrement());
}
String name();
}
......
......@@ -12,4 +12,21 @@ public interface CoreService {
*/
Version version();
/**
* Registers a new application by its name, which is expected
* to follow the reverse DNS convention, e.g.
* {@code org.flying.circus.app}
*
* @param identifier string identifier
* @return the application id
*/
ApplicationId registerApplication(String identifier);
/**
* Returns an existing application id from a given id.
* @param id the short value of the id
* @return an application id
*/
ApplicationId getAppId(Short id);
}
......
package org.onlab.onos.cluster;
import com.google.common.base.Function;
/**
* Function to convert ControllerNode to NodeId.
*/
public final class ControllerNodeToNodeId
implements Function<ControllerNode, NodeId> {
private static final ControllerNodeToNodeId INSTANCE = new ControllerNodeToNodeId();
@Override
public NodeId apply(ControllerNode input) {
return input.id();
}
/**
* Returns a Function to convert ControllerNode to NodeId.
*
* @return ControllerNodeToNodeId instance.
*/
public static ControllerNodeToNodeId toNodeId() {
return INSTANCE;
}
}
package org.onlab.onos.net;
import java.net.URI;
import java.util.Objects;
/**
* Immutable representation of a device identity.
*/
public final class DeviceId extends ElementId {
/**
* Represents either no device, or an unspecified device.
*/
public static final DeviceId NONE = deviceId("none:none");
private final URI uri;
private final String str;
// Public construction is prohibited
private DeviceId(URI uri) {
super(uri);
this.uri = uri;
this.str = uri.toString();
}
// Default constructor for serialization
protected DeviceId() {
this.uri = null;
this.str = null;
}
/**
......@@ -30,4 +47,36 @@ public final class DeviceId extends ElementId {
return deviceId(URI.create(string));
}
/**
* Returns the backing URI.
*
* @return backing URI
*/
public URI uri() {
return uri;
}
@Override
public int hashCode() {
return Objects.hash(str);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj instanceof DeviceId) {
final DeviceId that = (DeviceId) obj;
return this.getClass() == that.getClass() &&
Objects.equals(this.str, that.str);
}
return false;
}
@Override
public String toString() {
return str;
}
}
......
package org.onlab.onos.net;
import java.net.URI;
import java.util.Objects;
/**
* Immutable representation of a network element identity.
*/
public abstract class ElementId {
private final URI uri;
// Default constructor for serialization
protected ElementId() {
this.uri = null;
}
/**
* Creates an element identifier using the supplied URI.
*
* @param uri backing URI
*/
protected ElementId(URI uri) {
this.uri = uri;
}
/**
* Returns the backing URI.
*
* @return backing URI
*/
public URI uri() {
return uri;
}
@Override
public int hashCode() {
return Objects.hash(uri);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj instanceof ElementId) {
final ElementId that = (ElementId) obj;
return this.getClass() == that.getClass() &&
Objects.equals(this.uri, that.uri);
}
return false;
}
@Override
public String toString() {
return uri.toString();
}
}
......
......@@ -3,44 +3,69 @@ package org.onlab.onos.net;
import org.onlab.packet.MacAddress;
import org.onlab.packet.VlanId;
import java.net.URI;
import java.util.Objects;
import static com.google.common.base.Preconditions.checkArgument;
/**
* Immutable representation of a host identity.
*/
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 static final HostId NONE = new HostId(MacAddress.ZERO, VlanId.NONE);
private static final int MAC_LENGTH = 17;
private static final int MIN_ID_LENGTH = 19;
private final MacAddress mac;
private final VlanId vlanId;
// Public construction is prohibited
private HostId(URI uri) {
super(uri);
private HostId(MacAddress mac, VlanId vlanId) {
this.mac = mac;
this.vlanId = vlanId;
}
// Default constructor for serialization
private HostId() {
this.mac = null;
this.vlanId = null;
}
/**
* Creates a device id using the supplied URI.
* Returns the host MAC address.
*
* @param uri device URI
* @return host identifier
* @return MAC address
*/
public MacAddress mac() {
return mac;
}
/**
* Returns the host MAC address.
*
* @return MAC address
*/
public static HostId hostId(URI uri) {
return new HostId(uri);
public VlanId vlanId() {
return vlanId;
}
/**
* Creates a device id using the supplied URI string.
* Creates a device id using the supplied ID string.
*
* @param string device URI string
* @return host identifier
*/
public static HostId hostId(String string) {
return hostId(URI.create(string));
checkArgument(string.length() >= MIN_ID_LENGTH,
"Host ID must be at least %s characters", MIN_ID_LENGTH);
MacAddress mac = MacAddress.valueOf(string.substring(0, MAC_LENGTH));
VlanId vlanId = VlanId.vlanId(Short.parseShort(string.substring(MAC_LENGTH + 1)));
return new HostId(mac, vlanId);
}
/**
......@@ -51,7 +76,7 @@ public final class HostId extends ElementId {
* @return host identifier
*/
public static HostId hostId(MacAddress mac, VlanId vlanId) {
return hostId(NIC + ":" + mac + "-" + vlanId);
return new HostId(mac, vlanId);
}
/**
......@@ -64,4 +89,26 @@ public final class HostId extends ElementId {
return hostId(mac, VlanId.vlanId(VlanId.UNTAGGED));
}
public String toString() {
return mac + "/" + vlanId;
}
@Override
public int hashCode() {
return Objects.hash(mac, vlanId);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj instanceof HostId) {
final HostId other = (HostId) obj;
return Objects.equals(this.mac, other.mac) &&
Objects.equals(this.vlanId, other.vlanId);
}
return false;
}
}
......
package org.onlab.onos.net;
import static org.onlab.onos.net.PortNumber.P0;
/**
* Representation of a network edge location where an end-station host is
* connected.
*/
public class HostLocation extends ConnectPoint {
/**
* Represents a no location or an unknown location.
*/
public static final HostLocation NONE = new HostLocation(DeviceId.NONE, P0, 0L);
// Note that time is explicitly excluded from the notion of equality.
private final long time;
......
package org.onlab.onos.net;
/**
* Representation of a network resource.
*/
public interface NetworkResource {
}
......@@ -4,6 +4,8 @@ import org.onlab.onos.net.AbstractDescription;
import org.onlab.onos.net.PortNumber;
import org.onlab.onos.net.SparseAnnotations;
import com.google.common.base.MoreObjects;
/**
* Default implementation of immutable port description.
*/
......@@ -48,6 +50,15 @@ public class DefaultPortDescription extends AbstractDescription
return isEnabled;
}
@Override
public String toString() {
return MoreObjects.toStringHelper(getClass())
.add("number", number)
.add("isEnabled", isEnabled)
.add("annotations", annotations())
.toString();
}
// default constructor for serialization
private DefaultPortDescription() {
this.number = null;
......
package org.onlab.onos.net.flow;
import java.util.List;
/**
* Interface capturing the result of a batch operation.
*
*/
public interface BatchOperationResult<T> {
/**
* Returns whether the operation was successful.
* @return true if successful, false otherwise
*/
boolean isSuccess();
/**
* Obtains a list of items which failed.
* @return a list of failures
*/
List<T> failedItems();
}
package org.onlab.onos.net.flow;
public class CompletedBatchOperation {
import java.util.List;
import com.google.common.collect.ImmutableList;
public class CompletedBatchOperation implements BatchOperationResult<FlowEntry> {
private final boolean success;
private final List<FlowEntry> failures;
public CompletedBatchOperation(boolean success, List<FlowEntry> failures) {
this.success = success;
this.failures = ImmutableList.copyOf(failures);
}
@Override
public boolean isSuccess() {
return success;
}
@Override
public List<FlowEntry> failedItems() {
return failures;
}
}
......
......@@ -17,6 +17,10 @@ public class DefaultFlowEntry extends DefaultFlowRule implements FlowEntry {
private long lastSeen = -1;
private final int errType;
private final int errCode;
public DefaultFlowEntry(DeviceId deviceId, TrafficSelector selector,
TrafficTreatment treatment, int priority, FlowEntryState state,
......@@ -27,6 +31,8 @@ public class DefaultFlowEntry extends DefaultFlowRule implements FlowEntry {
this.life = life;
this.packets = packets;
this.bytes = bytes;
this.errCode = -1;
this.errType = -1;
this.lastSeen = System.currentTimeMillis();
}
......@@ -37,6 +43,8 @@ public class DefaultFlowEntry extends DefaultFlowRule implements FlowEntry {
this.life = life;
this.packets = packets;
this.bytes = bytes;
this.errCode = -1;
this.errType = -1;
this.lastSeen = System.currentTimeMillis();
}
......@@ -46,9 +54,18 @@ public class DefaultFlowEntry extends DefaultFlowRule implements FlowEntry {
this.life = 0;
this.packets = 0;
this.bytes = 0;
this.errCode = -1;
this.errType = -1;
this.lastSeen = System.currentTimeMillis();
}
public DefaultFlowEntry(FlowRule rule, int errType, int errCode) {
super(rule);
this.state = FlowEntryState.FAILED;
this.errType = errType;
this.errCode = errCode;
}
@Override
public long life() {
return life;
......@@ -100,6 +117,16 @@ public class DefaultFlowEntry extends DefaultFlowRule implements FlowEntry {
}
@Override
public int errType() {
return this.errType;
}
@Override
public int errCode() {
return this.errCode;
}
@Override
public String toString() {
return toStringHelper(this)
.add("rule", super.toString())
......@@ -108,4 +135,6 @@ public class DefaultFlowEntry extends DefaultFlowRule implements FlowEntry {
}
}
......
......@@ -21,7 +21,7 @@ public class DefaultFlowRule implements FlowRule {
private final FlowId id;
private final ApplicationId appId;
private final short appId;
private final int timeout;
......@@ -36,7 +36,7 @@ public class DefaultFlowRule implements FlowRule {
this.timeout = timeout;
this.created = System.currentTimeMillis();
this.appId = ApplicationId.valueOf((int) (flowId >> 32));
this.appId = (short) (flowId >>> 48);
this.id = FlowId.valueOf(flowId);
}
......@@ -52,11 +52,11 @@ public class DefaultFlowRule implements FlowRule {
this.priority = priority;
this.selector = selector;
this.treatment = treatement;
this.appId = appId;
this.appId = appId.id();
this.timeout = timeout;
this.created = System.currentTimeMillis();
this.id = FlowId.valueOf((((long) appId().id()) << 32) | (this.hash() & 0xffffffffL));
this.id = FlowId.valueOf((((long) this.appId) << 48) | (this.hash() & 0x0000ffffffffL));
}
public DefaultFlowRule(FlowRule rule) {
......@@ -78,7 +78,7 @@ public class DefaultFlowRule implements FlowRule {
}
@Override
public ApplicationId appId() {
public short appId() {
return appId;
}
......
......@@ -140,6 +140,16 @@ public final class DefaultTrafficSelector implements TrafficSelector {
}
@Override
public Builder matchTcpSrc(Short tcpPort) {
return add(Criteria.matchTcpSrc(tcpPort));
}
@Override
public Builder matchTcpDst(Short tcpPort) {
return add(Criteria.matchTcpDst(tcpPort));
}
@Override
public TrafficSelector build() {
return new DefaultTrafficSelector(ImmutableSet.copyOf(selector.values()));
}
......
package org.onlab.onos.net.flow;
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;
import org.onlab.onos.net.flow.instructions.Instructions;
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.LinkedList;
import java.util.List;
import java.util.Objects;
/**
* Default traffic treatment implementation.
......@@ -58,7 +55,7 @@ public final class DefaultTrafficTreatment implements TrafficTreatment {
}
if (obj instanceof DefaultTrafficTreatment) {
DefaultTrafficTreatment that = (DefaultTrafficTreatment) obj;
return Objects.equals(instructions, that.instructions);
return Objects.equals(instructions, that.instructions);
}
return false;
......@@ -70,8 +67,6 @@ public final class DefaultTrafficTreatment implements TrafficTreatment {
*/
public static final class Builder implements TrafficTreatment.Builder {
private final Logger log = getLogger(getClass());
boolean drop = false;
List<Instruction> outputs = new LinkedList<>();
......@@ -107,7 +102,8 @@ public final class DefaultTrafficTreatment implements TrafficTreatment {
groups.add(instruction);
break;
default:
log.warn("Unknown instruction type {}", instruction.type());
throw new IllegalArgumentException("Unknown instruction type: " +
instruction.type());
}
return this;
}
......
......@@ -29,7 +29,12 @@ public interface FlowEntry extends FlowRule {
/**
* Flow has been removed from flow table and can be purged.
*/
REMOVED
REMOVED,
/**
* Indicates that the installation of this flow has failed.
*/
FAILED
}
/**
......@@ -95,4 +100,16 @@ public interface FlowEntry extends FlowRule {
*/
void setBytes(long bytes);
/**
* Indicates the error type.
* @return an integer value of the error
*/
int errType();
/**
* Indicates the error code.
* @return an integer value of the error
*/
int errCode();
}
......
package org.onlab.onos.net.flow;
import org.onlab.onos.ApplicationId;
import org.onlab.onos.net.DeviceId;
import org.onlab.onos.net.intent.BatchOperationTarget;
......@@ -26,7 +25,7 @@ public interface FlowRule extends BatchOperationTarget {
*
* @return an applicationId
*/
ApplicationId appId();
short appId();
/**
* Returns the flow rule priority given in natural order; higher numbers
......
......@@ -37,6 +37,12 @@ public interface FlowRuleProvider extends Provider {
*/
void removeRulesById(ApplicationId id, FlowRule... flowRules);
Future<Void> executeBatch(BatchOperation<FlowRuleBatchEntry> batch);
/**
* Installs a batch of flow rules. Each flowrule is associated to an
* operation which results in either addition, removal or modification.
* @param batch a batch of flow rules
* @return a future indicating the status of this execution
*/
Future<CompletedBatchOperation> executeBatch(BatchOperation<FlowRuleBatchEntry> batch);
}
......
......@@ -98,6 +98,20 @@ public interface TrafficSelector {
public Builder matchIPDst(IpPrefix ip);
/**
* Matches a TCP source port number.
* @param tcpPort a TCP source port number
* @return a selection builder
*/
public Builder matchTcpSrc(Short tcpPort);
/**
* Matches a TCP destination port number.
* @param tcpPort a TCP destination port number
* @return a selection builder
*/
public Builder matchTcpDst(Short tcpPort);
/**
* Builds an immutable traffic selector.
*
* @return traffic selector
......
......@@ -113,6 +113,25 @@ public final class Criteria {
return new IPCriterion(ip, Type.IPV4_DST);
}
/**
* Creates a match on TCP source port field using the specified value.
*
* @param tcpPort
* @return match criterion
*/
public static Criterion matchTcpSrc(Short tcpPort) {
return new TcpPortCriterion(tcpPort, Type.TCP_SRC);
}
/**
* Creates a match on TCP destination port field using the specified value.
*
* @param tcpPort
* @return match criterion
*/
public static Criterion matchTcpDst(Short tcpPort) {
return new TcpPortCriterion(tcpPort, Type.TCP_DST);
}
/*
* Implementations of criteria.
......@@ -437,4 +456,49 @@ public final class Criteria {
}
public static final class TcpPortCriterion implements Criterion {
private final Short tcpPort;
private final Type type;
public TcpPortCriterion(Short tcpPort, Type type) {
this.tcpPort = tcpPort;
this.type = type;
}
@Override
public Type type() {
return this.type;
}
public Short tcpPort() {
return this.tcpPort;
}
@Override
public String toString() {
return toStringHelper(type().toString())
.add("tcpPort", tcpPort).toString();
}
@Override
public int hashCode() {
return Objects.hash(tcpPort, type);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj instanceof TcpPortCriterion) {
TcpPortCriterion that = (TcpPortCriterion) obj;
return Objects.equals(tcpPort, that.tcpPort) &&
Objects.equals(type, that.type);
}
return false;
}
}
}
......
package org.onlab.onos.net.host;
import com.google.common.collect.ImmutableSet;
import org.onlab.onos.net.AbstractDescription;
import org.onlab.onos.net.HostLocation;
import org.onlab.onos.net.SparseAnnotations;
......@@ -8,9 +7,6 @@ import org.onlab.packet.IpPrefix;
import org.onlab.packet.MacAddress;
import org.onlab.packet.VlanId;
import java.util.HashSet;
import java.util.Set;
import static com.google.common.base.MoreObjects.toStringHelper;
/**
......@@ -22,7 +18,7 @@ public class DefaultHostDescription extends AbstractDescription
private final MacAddress mac;
private final VlanId vlan;
private final HostLocation location;
private final Set<IpPrefix> ips;
private final IpPrefix ip;
/**
* Creates a host description using the supplied information.
......@@ -35,7 +31,7 @@ public class DefaultHostDescription extends AbstractDescription
public DefaultHostDescription(MacAddress mac, VlanId vlan,
HostLocation location,
SparseAnnotations... annotations) {
this(mac, vlan, location, new HashSet<IpPrefix>(), annotations);
this(mac, vlan, location, null, annotations);
}
/**
......@@ -44,17 +40,17 @@ public class DefaultHostDescription extends AbstractDescription
* @param mac host MAC address
* @param vlan host VLAN identifier
* @param location host location
* @param ips of host IP addresses
* @param ip host IP address
* @param annotations optional key/value annotations map
*/
public DefaultHostDescription(MacAddress mac, VlanId vlan,
HostLocation location, Set<IpPrefix> ips,
HostLocation location, IpPrefix ip,
SparseAnnotations... annotations) {
super(annotations);
this.mac = mac;
this.vlan = vlan;
this.location = location;
this.ips = new HashSet<>(ips);
this.ip = ip;
}
@Override
......@@ -73,8 +69,8 @@ public class DefaultHostDescription extends AbstractDescription
}
@Override
public Set<IpPrefix> ipAddresses() {
return ImmutableSet.copyOf(ips);
public IpPrefix ipAddress() {
return ip;
}
@Override
......@@ -83,7 +79,7 @@ public class DefaultHostDescription extends AbstractDescription
.add("mac", mac)
.add("vlan", vlan)
.add("location", location)
.add("ipAddresses", ips)
.add("ipAddress", ip)
.toString();
}
......
package org.onlab.onos.net.host;
import java.util.Set;
import org.onlab.onos.net.Description;
import org.onlab.onos.net.HostLocation;
import org.onlab.packet.IpPrefix;
......@@ -35,10 +33,10 @@ public interface HostDescription extends Description {
HostLocation location();
/**
* Returns zero or more IP address(es) associated with this host's MAC.
* Returns the IP address associated with this host's MAC.
*
* @return a set of IP addresses.
* @return host IP address
*/
Set<IpPrefix> ipAddresses();
IpPrefix ipAddress();
}
......
package org.onlab.onos.net.intent;
import java.util.concurrent.Future;
import org.onlab.onos.net.flow.CompletedBatchOperation;
/**
* Abstraction of entity capable of installing intents to the environment.
*/
......@@ -10,7 +14,7 @@ public interface IntentInstaller<T extends InstallableIntent> {
* @param intent intent to be installed
* @throws IntentException if issues are encountered while installing the intent
*/
void install(T intent);
Future<CompletedBatchOperation> install(T intent);
/**
* Uninstalls the specified intent from the environment.
......@@ -18,5 +22,5 @@ public interface IntentInstaller<T extends InstallableIntent> {
* @param intent intent to be uninstalled
* @throws IntentException if issues are encountered while uninstalling the intent
*/
void uninstall(T intent);
Future<CompletedBatchOperation> uninstall(T intent);
}
......
......@@ -33,6 +33,8 @@ public interface IntentStore extends Store<IntentEvent, IntentStoreDelegate> {
/**
* Returns the number of intents in the store.
*
* @return the number of intents in the store
*/
long getIntentCount();
......@@ -44,7 +46,7 @@ public interface IntentStore extends Store<IntentEvent, IntentStoreDelegate> {
Iterable<Intent> getIntents();
/**
* Returns the intent with the specified identifer.
* Returns the intent with the specified identifier.
*
* @param intentId intent identification
* @return intent or null if not found
......@@ -94,7 +96,6 @@ public interface IntentStore extends Store<IntentEvent, IntentStoreDelegate> {
* specified original intent.
*
* @param intentId original intent identifier
* @return compiled state transition event
*/
void removeInstalledIntents(IntentId intentId);
......
......@@ -53,4 +53,4 @@
* 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.intent;
......
package org.onlab.onos.store;
import java.util.List;
import org.onlab.onos.event.Event;
import static com.google.common.base.Preconditions.checkState;
......@@ -41,4 +43,15 @@ public class AbstractStore<E extends Event, D extends StoreDelegate<E>>
delegate.notify(event);
}
}
/**
* Notifies the delegate with the specified list of events.
*
* @param events list of events to delegate
*/
protected void notifyDelegate(List<E> events) {
for (E event: events) {
notifyDelegate(event);
}
}
}
......
package org.onlab.onos;
import java.util.Objects;
/**
* Test application ID.
*/
public class TestApplicationId implements ApplicationId {
private final String name;
private final short id;
public TestApplicationId(String name) {
this.name = name;
this.id = (short) Objects.hash(name);
}
public static ApplicationId create(String name) {
return new TestApplicationId(name);
}
@Override
public short id() {
return id;
}
@Override
public String name() {
return name;
}
}
package org.onlab.onos.cluster;
import static org.junit.Assert.*;
import static org.onlab.onos.cluster.ControllerNodeToNodeId.toNodeId;
import java.util.Arrays;
import java.util.List;
import org.junit.Test;
import org.onlab.packet.IpPrefix;
import com.google.common.collect.FluentIterable;
public class ControllerNodeToNodeIdTest {
private static final NodeId NID1 = new NodeId("foo");
private static final NodeId NID2 = new NodeId("bar");
private static final NodeId NID3 = new NodeId("buz");
private static final IpPrefix IP1 = IpPrefix.valueOf("127.0.0.1");
private static final IpPrefix IP2 = IpPrefix.valueOf("127.0.0.2");
private static final IpPrefix IP3 = IpPrefix.valueOf("127.0.0.3");
private static final ControllerNode CN1 = new DefaultControllerNode(NID1, IP1);
private static final ControllerNode CN2 = new DefaultControllerNode(NID2, IP2);
private static final ControllerNode CN3 = new DefaultControllerNode(NID3, IP3);
@Test
public final void testToNodeId() {
final Iterable<ControllerNode> nodes = Arrays.asList(CN1, CN2, CN3);
final List<NodeId> nodeIds = Arrays.asList(NID1, NID2, NID3);
assertEquals(nodeIds,
FluentIterable.from(nodes)
.transform(toNodeId())
.toList());
}
}
......@@ -9,6 +9,7 @@ import static org.onlab.onos.event.TestEvent.Type.FOO;
import java.util.List;
import java.util.Timer;
import org.junit.Ignore;
import org.junit.Test;
/**
......@@ -41,19 +42,23 @@ public class AbstractEventAccumulatorTest {
assertEquals("incorrect batch", "abcde", accumulator.batch);
}
@Ignore("FIXME: timing sensitive test failing randomly.")
@Test
public void timeTrigger() {
TestAccumulator accumulator = new TestAccumulator();
accumulator.add(new TestEvent(FOO, "a"));
delay(40);
delay(30);
assertTrue("should not have fired yet", accumulator.batch.isEmpty());
accumulator.add(new TestEvent(FOO, "b"));
delay(40);
delay(30);
assertTrue("should not have fired yet", accumulator.batch.isEmpty());
accumulator.add(new TestEvent(FOO, "c"));
delay(40);
delay(30);
assertTrue("should not have fired yet", accumulator.batch.isEmpty());
accumulator.add(new TestEvent(FOO, "d"));
delay(30);
assertFalse("should have fired", accumulator.batch.isEmpty());
assertEquals("incorrect batch", "abc", accumulator.batch);
assertEquals("incorrect batch", "abcd", accumulator.batch);
}
@Test
......
......@@ -18,8 +18,8 @@ public class DefaultEdgeLinkTest {
private static final ProviderId PID = new ProviderId("of", "foo");
private static final DeviceId DID1 = deviceId("of:foo");
private static final HostId HID1 = hostId("nic:foobar");
private static final HostId HID2 = hostId("nic:barfoo");
private static final HostId HID1 = hostId("00:00:00:00:00:01/-1");
private static final HostId HID2 = hostId("00:00:00:00:00:01/-1");
private static final PortNumber P0 = portNumber(0);
private static final PortNumber P1 = portNumber(1);
......@@ -35,12 +35,8 @@ public class DefaultEdgeLinkTest {
EdgeLink l4 = new DefaultEdgeLink(PID, cp(HID2, P0),
new HostLocation(DID1, P1, 123L), false);
EdgeLink l5 = new DefaultEdgeLink(PID, cp(HID1, P0),
new HostLocation(DID1, P1, 123L), false);
new EqualsTester().addEqualityGroup(l1, l2)
.addEqualityGroup(l3, l4)
.addEqualityGroup(l5)
.testEquals();
}
......
......@@ -8,7 +8,7 @@ import static org.onlab.onos.net.DeviceId.deviceId;
/**
* Test of the device identifier.
*/
public class DeviceIdTest extends ElementIdTest {
public class DeviceIdTest {
@Test
public void basics() {
......
package org.onlab.onos.net;
import com.google.common.testing.EqualsTester;
import org.junit.Test;
import java.net.URI;
import static org.junit.Assert.assertEquals;
/**
* Test of the network element identifier.
*/
public class ElementIdTest {
private static class FooId extends ElementId {
public FooId(URI uri) {
super(uri);
}
}
public static URI uri(String str) {
return URI.create(str);
}
@Test
public void basics() {
new EqualsTester()
.addEqualityGroup(new FooId(uri("of:foo")),
new FooId(uri("of:foo")))
.addEqualityGroup(new FooId(uri("of:bar")))
.testEquals();
assertEquals("wrong uri", uri("ofcfg:foo"),
new FooId(uri("ofcfg:foo")).uri());
}
}
......@@ -11,20 +11,18 @@ import static org.onlab.onos.net.HostId.hostId;
/**
* Test for the host identifier.
*/
public class HostIdTest extends ElementIdTest {
public class HostIdTest {
private static final MacAddress MAC1 = MacAddress.valueOf("00:11:00:00:00:01");
private static final MacAddress MAC2 = MacAddress.valueOf("00:22:00:00:00:02");
private static final VlanId VLAN1 = VlanId.vlanId((short) 11);
private static final VlanId VLAN2 = VlanId.vlanId((short) 22);
@Override
@Test
public void basics() {
new EqualsTester()
.addEqualityGroup(hostId("nic:00:11:00:00:00:01-11"),
hostId(MAC1, VLAN1))
.addEqualityGroup(hostId(MAC2, VLAN2))
.addEqualityGroup(hostId(MAC1, VLAN1), hostId(MAC1, VLAN1))
.addEqualityGroup(hostId(MAC2, VLAN2), hostId(MAC2, VLAN2))
.testEquals();
}
......
......@@ -31,7 +31,7 @@ public final class NetTestTools {
// Short-hand for producing a host id from a string
public static HostId hid(String id) {
return hostId("nic:" + id);
return hostId(id);
}
// Crates a new device with the specified id
......
......@@ -10,9 +10,8 @@ import com.google.common.testing.EqualsTester;
/**
* Test of the port number.
*/
public class PortNumberTest extends ElementIdTest {
public class PortNumberTest {
@Override
@Test
public void basics() {
new EqualsTester()
......
package org.onlab.onos.net.host;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.util.Set;
import org.junit.Test;
import org.onlab.onos.net.DeviceId;
import org.onlab.onos.net.HostLocation;
......@@ -13,7 +8,8 @@ import org.onlab.packet.IpPrefix;
import org.onlab.packet.MacAddress;
import org.onlab.packet.VlanId;
import com.google.common.collect.Sets;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
/**
* Test for the default host description.
......@@ -22,24 +18,22 @@ public class DefualtHostDecriptionTest {
private static final MacAddress MAC = MacAddress.valueOf("00:00:11:00:00:01");
private static final VlanId VLAN = VlanId.vlanId((short) 10);
private static final IpPrefix IP = IpPrefix.valueOf("10.0.0.1");
private static final HostLocation LOC = new HostLocation(
DeviceId.deviceId("of:foo"),
PortNumber.portNumber(100),
123L
);
private static final Set<IpPrefix> IPS = Sets.newHashSet(
IpPrefix.valueOf("10.0.0.1"),
IpPrefix.valueOf("10.0.0.2")
);
DeviceId.deviceId("of:foo"),
PortNumber.portNumber(100),
123L
);
@Test
public void basics() {
HostDescription host =
new DefaultHostDescription(MAC, VLAN, LOC, IPS);
new DefaultHostDescription(MAC, VLAN, LOC, IP);
assertEquals("incorrect mac", MAC, host.hwAddress());
assertEquals("incorrect vlan", VLAN, host.vlan());
assertEquals("incorrect location", LOC, host.location());
assertTrue("incorrect ip's", IPS.equals(host.ipAddresses()));
assertEquals("incorrect ip's", IP, host.ipAddress());
assertTrue("incorrect toString", host.toString().contains("vlan=10"));
}
......
package org.onlab.onos.net.intent;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.fail;
import static org.onlab.onos.net.intent.IntentEvent.Type.FAILED;
import static org.onlab.onos.net.intent.IntentEvent.Type.INSTALLED;
import static org.onlab.onos.net.intent.IntentEvent.Type.SUBMITTED;
import static org.onlab.onos.net.intent.IntentEvent.Type.WITHDRAWN;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.Future;
import static org.junit.Assert.*;
import static org.onlab.onos.net.intent.IntentEvent.Type.*;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.onlab.onos.net.flow.CompletedBatchOperation;
/**
* Suite of tests for the intent service contract.
......@@ -290,17 +298,19 @@ public class IntentServiceTest {
}
@Override
public void install(TestInstallableIntent intent) {
public Future<CompletedBatchOperation> install(TestInstallableIntent intent) {
if (fail) {
throw new IntentException("install failed by design");
}
return null;
}
@Override
public void uninstall(TestInstallableIntent intent) {
public Future<CompletedBatchOperation> uninstall(TestInstallableIntent intent) {
if (fail) {
throw new IntentException("remove failed by design");
}
return null;
}
}
......
package org.onlab.onos.cluster.impl;
package org.onlab.onos.impl;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Service;
import org.onlab.onos.ApplicationId;
import org.onlab.onos.CoreService;
import org.onlab.onos.Version;
import org.onlab.util.Tools;
import java.io.File;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
/**
* Core service implementation.
......@@ -17,9 +21,13 @@ import java.util.List;
@Service
public class CoreManager implements CoreService {
private static final AtomicInteger ID_DISPENSER = new AtomicInteger(1);
private static final File VERSION_FILE = new File("../VERSION");
private static Version version = Version.version("1.0.0-SNAPSHOT");
private final Map<Short, DefaultApplicationId> appIds = new ConcurrentHashMap<>();
// TODO: work in progress
@Activate
......@@ -35,4 +43,17 @@ public class CoreManager implements CoreService {
return version;
}
@Override
public ApplicationId getAppId(Short id) {
return appIds.get(id);
}
@Override
public ApplicationId registerApplication(String name) {
short id = (short) ID_DISPENSER.getAndIncrement();
DefaultApplicationId appId = new DefaultApplicationId(id, name);
appIds.put(id, appId);
return appId;
}
}
......
package org.onlab.onos.impl;
import org.onlab.onos.ApplicationId;
import java.util.Objects;
import static com.google.common.base.MoreObjects.toStringHelper;
/**
* Application id generator class.
*/
public class DefaultApplicationId implements ApplicationId {
private final short id;
private final String name;
// Ban public construction
protected DefaultApplicationId(Short id, String identifier) {
this.id = id;
this.name = identifier;
}
@Override
public short id() {
return id;
}
@Override
public String name() {
return name;
}
@Override
public int hashCode() {
return Objects.hash(id);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj instanceof DefaultApplicationId) {
DefaultApplicationId other = (DefaultApplicationId) obj;
return Objects.equals(this.id, other.id);
}
return false;
}
@Override
public String toString() {
return toStringHelper(this).add("id", id).add("name", name).toString();
}
}
/**
*
*/
package org.onlab.onos.impl;
\ No newline at end of file
......@@ -5,10 +5,12 @@ import static org.slf4j.LoggerFactory.getLogger;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicReference;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
......@@ -26,6 +28,7 @@ import org.onlab.onos.net.flow.CompletedBatchOperation;
import org.onlab.onos.net.flow.FlowEntry;
import org.onlab.onos.net.flow.FlowRule;
import org.onlab.onos.net.flow.FlowRuleBatchEntry;
import org.onlab.onos.net.flow.FlowRuleBatchEntry.FlowRuleOperation;
import org.onlab.onos.net.flow.FlowRuleBatchOperation;
import org.onlab.onos.net.flow.FlowRuleEvent;
import org.onlab.onos.net.flow.FlowRuleListener;
......@@ -52,6 +55,8 @@ public class FlowRuleManager
extends AbstractProviderRegistry<FlowRuleProvider, FlowRuleProviderService>
implements FlowRuleService, FlowRuleProviderRegistry {
enum BatchState { STARTED, FINISHED, CANCELLED };
public static final String FLOW_RULE_NULL = "FlowRule cannot be null";
private final Logger log = getLogger(getClass());
......@@ -144,7 +149,7 @@ public class FlowRuleManager
FlowRuleBatchOperation batch) {
Multimap<FlowRuleProvider, FlowRuleBatchEntry> batches =
ArrayListMultimap.create();
List<Future<Void>> futures = Lists.newArrayList();
List<Future<CompletedBatchOperation>> futures = Lists.newArrayList();
for (FlowRuleBatchEntry fbe : batch.getOperations()) {
final FlowRule f = fbe.getTarget();
final Device device = deviceService.getDevice(f.deviceId());
......@@ -165,10 +170,10 @@ public class FlowRuleManager
for (FlowRuleProvider provider : batches.keySet()) {
FlowRuleBatchOperation b =
new FlowRuleBatchOperation(batches.get(provider));
Future<Void> future = provider.executeBatch(b);
Future<CompletedBatchOperation> future = provider.executeBatch(b);
futures.add(future);
}
return new FlowRuleBatchFuture(futures);
return new FlowRuleBatchFuture(futures, batches);
}
@Override
......@@ -341,59 +346,140 @@ public class FlowRuleManager
private class FlowRuleBatchFuture
implements Future<CompletedBatchOperation> {
private final List<Future<Void>> futures;
private final List<Future<CompletedBatchOperation>> futures;
private final Multimap<FlowRuleProvider, FlowRuleBatchEntry> batches;
private final AtomicReference<BatchState> state;
private CompletedBatchOperation overall;
public FlowRuleBatchFuture(List<Future<Void>> futures) {
public FlowRuleBatchFuture(List<Future<CompletedBatchOperation>> futures,
Multimap<FlowRuleProvider, FlowRuleBatchEntry> batches) {
this.futures = futures;
this.batches = batches;
state = new AtomicReference<FlowRuleManager.BatchState>();
state.set(BatchState.STARTED);
}
@Override
public boolean cancel(boolean mayInterruptIfRunning) {
// TODO Auto-generated method stub
return false;
if (state.get() == BatchState.FINISHED) {
return false;
}
if (!state.compareAndSet(BatchState.STARTED, BatchState.CANCELLED)) {
return false;
}
cleanUpBatch();
for (Future<CompletedBatchOperation> f : futures) {
f.cancel(mayInterruptIfRunning);
}
return true;
}
@Override
public boolean isCancelled() {
// TODO Auto-generated method stub
return false;
return state.get() == BatchState.CANCELLED;
}
@Override
public boolean isDone() {
boolean isDone = true;
for (Future<Void> future : futures) {
isDone &= future.isDone();
}
return isDone;
return state.get() == BatchState.FINISHED;
}
@Override
public CompletedBatchOperation get() throws InterruptedException,
ExecutionException {
// TODO Auto-generated method stub
for (Future<Void> future : futures) {
future.get();
ExecutionException {
if (isDone()) {
return overall;
}
boolean success = true;
List<FlowEntry> failed = Lists.newLinkedList();
CompletedBatchOperation completed;
for (Future<CompletedBatchOperation> future : futures) {
completed = future.get();
success = validateBatchOperation(failed, completed, future);
}
return new CompletedBatchOperation();
return finalizeBatchOperation(success, failed);
}
@Override
public CompletedBatchOperation get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException,
TimeoutException {
// TODO we should decrement the timeout
if (isDone()) {
return overall;
}
boolean success = true;
List<FlowEntry> failed = Lists.newLinkedList();
CompletedBatchOperation completed;
long start = System.nanoTime();
long end = start + unit.toNanos(timeout);
for (Future<Void> future : futures) {
for (Future<CompletedBatchOperation> future : futures) {
long now = System.nanoTime();
long thisTimeout = end - now;
future.get(thisTimeout, TimeUnit.NANOSECONDS);
completed = future.get(thisTimeout, TimeUnit.NANOSECONDS);
success = validateBatchOperation(failed, completed, future);
}
return new CompletedBatchOperation();
return finalizeBatchOperation(success, failed);
}
private boolean validateBatchOperation(List<FlowEntry> failed,
CompletedBatchOperation completed,
Future<CompletedBatchOperation> future) {
if (isCancelled()) {
throw new CancellationException();
}
if (!completed.isSuccess()) {
failed.addAll(completed.failedItems());
cleanUpBatch();
cancelAllSubBatches();
return false;
}
return true;
}
private void cancelAllSubBatches() {
for (Future<CompletedBatchOperation> f : futures) {
f.cancel(true);
}
}
private CompletedBatchOperation finalizeBatchOperation(boolean success,
List<FlowEntry> failed) {
synchronized (this) {
if (!state.compareAndSet(BatchState.STARTED, BatchState.FINISHED)) {
if (state.get() == BatchState.FINISHED) {
return overall;
}
throw new CancellationException();
}
overall = new CompletedBatchOperation(success, failed);
return overall;
}
}
private void cleanUpBatch() {
for (FlowRuleBatchEntry fbe : batches.values()) {
if (fbe.getOperator() == FlowRuleOperation.ADD ||
fbe.getOperator() == FlowRuleOperation.MODIFY) {
store.deleteFlowRule(fbe.getTarget());
} else if (fbe.getOperator() == FlowRuleOperation.REMOVE) {
store.storeFlowRule(fbe.getTarget());
}
}
}
}
}
......
......@@ -168,7 +168,6 @@ public class HostManager
checkNotNull(hostId, HOST_ID_NULL);
HostEvent event = store.removeHost(hostId);
if (event != null) {
log.info("Host {} administratively removed", hostId);
post(event);
}
}
......@@ -214,7 +213,6 @@ public class HostManager
HostEvent event = store.createOrUpdateHost(provider().id(), hostId,
hostDescription);
if (event != null) {
log.debug("Host {} detected", hostId);
post(event);
}
}
......@@ -225,7 +223,6 @@ public class HostManager
checkValidity();
HostEvent event = store.removeHost(hostId);
if (event != null) {
log.debug("Host {} vanished", hostId);
post(event);
}
}
......
......@@ -13,12 +13,17 @@ import static org.onlab.util.Tools.namedThreads;
import static org.slf4j.LoggerFactory.getLogger;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
......@@ -28,6 +33,7 @@ import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.apache.felix.scr.annotations.Service;
import org.onlab.onos.event.AbstractListenerRegistry;
import org.onlab.onos.event.EventDeliveryService;
import org.onlab.onos.net.flow.CompletedBatchOperation;
import org.onlab.onos.net.intent.InstallableIntent;
import org.onlab.onos.net.intent.Intent;
import org.onlab.onos.net.intent.IntentCompiler;
......@@ -44,7 +50,9 @@ import org.onlab.onos.net.intent.IntentStore;
import org.onlab.onos.net.intent.IntentStoreDelegate;
import org.slf4j.Logger;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
/**
* An implementation of Intent Manager.
......@@ -67,7 +75,8 @@ public class IntentManager
private final AbstractListenerRegistry<IntentEvent, IntentListener>
listenerRegistry = new AbstractListenerRegistry<>();
private final ExecutorService executor = newSingleThreadExecutor(namedThreads("onos-intents"));
private ExecutorService executor;
private ExecutorService monitorExecutor;
private final IntentStoreDelegate delegate = new InternalStoreDelegate();
private final TopologyChangeDelegate topoDelegate = new InternalTopoChangeDelegate();
......@@ -86,6 +95,8 @@ public class IntentManager
store.setDelegate(delegate);
trackerService.setDelegate(topoDelegate);
eventDispatcher.addSink(IntentEvent.class, listenerRegistry);
executor = newSingleThreadExecutor(namedThreads("onos-intents"));
monitorExecutor = newSingleThreadExecutor(namedThreads("onos-intent-monitor"));
log.info("Started");
}
......@@ -94,6 +105,8 @@ public class IntentManager
store.unsetDelegate(delegate);
trackerService.unsetDelegate(topoDelegate);
eventDispatcher.removeSink(IntentEvent.class);
executor.shutdown();
monitorExecutor.shutdown();
log.info("Stopped");
}
......@@ -240,14 +253,23 @@ public class IntentManager
}
}
// FIXME: To make SDN-IP workable ASAP, only single level compilation is implemented
// TODO: implement compilation traversing tree structure
/**
* Compiles an intent recursively.
*
* @param intent intent
* @return result of compilation
*/
private List<InstallableIntent> compileIntent(Intent intent) {
if (intent instanceof InstallableIntent) {
return ImmutableList.of((InstallableIntent) intent);
}
List<InstallableIntent> installable = new ArrayList<>();
// TODO do we need to registerSubclassCompiler?
for (Intent compiled : getCompiler(intent).compile(intent)) {
InstallableIntent installableIntent = (InstallableIntent) compiled;
installable.add(installableIntent);
installable.addAll(compileIntent(compiled));
}
return installable;
}
......@@ -261,6 +283,7 @@ public class IntentManager
// Indicate that the intent is entering the installing phase.
store.setState(intent, INSTALLING);
List<Future<CompletedBatchOperation>> installFutures = Lists.newArrayList();
try {
List<InstallableIntent> installables = store.getInstallableIntents(intent.id());
if (installables != null) {
......@@ -268,17 +291,20 @@ public class IntentManager
registerSubclassInstallerIfNeeded(installable);
trackerService.addTrackedResources(intent.id(),
installable.requiredLinks());
getInstaller(installable).install(installable);
Future<CompletedBatchOperation> future = getInstaller(installable).install(installable);
installFutures.add(future);
}
}
eventDispatcher.post(store.setState(intent, INSTALLED));
// FIXME we have to wait for the installable intents
//eventDispatcher.post(store.setState(intent, INSTALLED));
monitorExecutor.execute(new IntentInstallMonitor(intent, installFutures, INSTALLED));
} catch (Exception e) {
log.warn("Unable to install intent {} due to: {}", intent.id(), e);
uninstallIntent(intent);
uninstallIntent(intent, RECOMPILING);
// If compilation failed, kick off the recompiling phase.
executeRecompilingPhase(intent);
// FIXME
//executeRecompilingPhase(intent);
}
}
......@@ -327,12 +353,14 @@ public class IntentManager
private void executeWithdrawingPhase(Intent intent) {
// Indicate that the intent is being withdrawn.
store.setState(intent, WITHDRAWING);
uninstallIntent(intent);
uninstallIntent(intent, WITHDRAWN);
// If all went well, disassociate the top-level intent with its
// installable derivatives and mark it as withdrawn.
store.removeInstalledIntents(intent.id());
eventDispatcher.post(store.setState(intent, WITHDRAWN));
// FIXME need to clean up
//store.removeInstalledIntents(intent.id());
// FIXME
//eventDispatcher.post(store.setState(intent, WITHDRAWN));
}
/**
......@@ -340,14 +368,17 @@ public class IntentManager
*
* @param intent intent to be uninstalled
*/
private void uninstallIntent(Intent intent) {
private void uninstallIntent(Intent intent, IntentState nextState) {
List<Future<CompletedBatchOperation>> uninstallFutures = Lists.newArrayList();
try {
List<InstallableIntent> installables = store.getInstallableIntents(intent.id());
if (installables != null) {
for (InstallableIntent installable : installables) {
getInstaller(installable).uninstall(installable);
Future<CompletedBatchOperation> future = getInstaller(installable).uninstall(installable);
uninstallFutures.add(future);
}
}
monitorExecutor.execute(new IntentInstallMonitor(intent, uninstallFutures, nextState));
} catch (IntentException e) {
log.warn("Unable to uninstall intent {} due to: {}", intent.id(), e);
}
......@@ -422,9 +453,10 @@ public class IntentManager
// Attempt recompilation of the specified intents first.
for (IntentId intentId : intentIds) {
Intent intent = getIntent(intentId);
uninstallIntent(intent);
uninstallIntent(intent, RECOMPILING);
executeRecompilingPhase(intent);
//FIXME
//executeRecompilingPhase(intent);
}
if (compileAllFailed) {
......@@ -460,4 +492,49 @@ public class IntentManager
}
}
private class IntentInstallMonitor implements Runnable {
private final Intent intent;
private final List<Future<CompletedBatchOperation>> futures;
private final IntentState nextState;
public IntentInstallMonitor(Intent intent,
List<Future<CompletedBatchOperation>> futures, IntentState nextState) {
this.intent = intent;
this.futures = futures;
this.nextState = nextState;
}
private void updateIntent(Intent intent) {
if (nextState == RECOMPILING) {
executor.execute(new IntentTask(nextState, intent));
} else if (nextState == INSTALLED || nextState == WITHDRAWN) {
eventDispatcher.post(store.setState(intent, nextState));
} else {
log.warn("Invalid next intent state {} for intent {}", nextState, intent);
}
}
@Override
public void run() {
for (Iterator<Future<CompletedBatchOperation>> i = futures.iterator(); i.hasNext();) {
Future<CompletedBatchOperation> future = i.next();
try {
// TODO: we may want to get the future here and go back to the future.
CompletedBatchOperation completed = future.get(100, TimeUnit.NANOSECONDS);
// TODO check if future succeeded and if not report fail items
i.remove();
} catch (TimeoutException | InterruptedException | ExecutionException te) {
log.debug("Intallations of intent {} is still pending", intent);
}
}
if (futures.isEmpty()) {
updateIntent(intent);
} else {
// resubmit ourselves if we are not done yet
monitorExecutor.submit(this);
}
}
}
}
......
......@@ -5,7 +5,7 @@ import static org.slf4j.LoggerFactory.getLogger;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
......@@ -13,8 +13,10 @@ 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.CoreService;
import org.onlab.onos.net.ConnectPoint;
import org.onlab.onos.net.Link;
import org.onlab.onos.net.flow.CompletedBatchOperation;
import org.onlab.onos.net.flow.DefaultFlowRule;
import org.onlab.onos.net.flow.DefaultTrafficSelector;
import org.onlab.onos.net.flow.FlowRule;
......@@ -45,10 +47,14 @@ public class PathIntentInstaller implements IntentInstaller<PathIntent> {
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected FlowRuleService flowRuleService;
private final ApplicationId appId = ApplicationId.getAppId();
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected CoreService coreService;
private ApplicationId appId;
@Activate
public void activate() {
appId = coreService.registerApplication("org.onlab.onos.net.intent");
intentManager.registerInstaller(PathIntent.class, this);
}
......@@ -57,8 +63,26 @@ public class PathIntentInstaller implements IntentInstaller<PathIntent> {
intentManager.unregisterInstaller(PathIntent.class);
}
/**
* Apply a list of FlowRules.
*
* @param rules rules to apply
*/
private Future<CompletedBatchOperation> applyBatch(List<FlowRuleBatchEntry> rules) {
FlowRuleBatchOperation batch = new FlowRuleBatchOperation(rules);
Future<CompletedBatchOperation> future = flowRuleService.applyBatch(batch);
return future;
// try {
// //FIXME don't do this here
// future.get();
// } catch (InterruptedException | ExecutionException e) {
// // TODO Auto-generated catch block
// e.printStackTrace();
// }
}
@Override
public void install(PathIntent intent) {
public Future<CompletedBatchOperation> install(PathIntent intent) {
TrafficSelector.Builder builder =
DefaultTrafficSelector.builder(intent.selector());
Iterator<Link> links = intent.path().links().iterator();
......@@ -74,20 +98,14 @@ public class PathIntentInstaller implements IntentInstaller<PathIntent> {
builder.build(), treatment,
123, appId, 600);
rules.add(new FlowRuleBatchEntry(FlowRuleOperation.ADD, rule));
//flowRuleService.applyFlowRules(rule);
prev = link.dst();
}
FlowRuleBatchOperation batch = new FlowRuleBatchOperation(rules);
try {
flowRuleService.applyBatch(batch).get();
} catch (InterruptedException | ExecutionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return applyBatch(rules);
}
@Override
public void uninstall(PathIntent intent) {
public Future<CompletedBatchOperation> uninstall(PathIntent intent) {
TrafficSelector.Builder builder =
DefaultTrafficSelector.builder(intent.selector());
Iterator<Link> links = intent.path().links().iterator();
......@@ -103,15 +121,131 @@ public class PathIntentInstaller implements IntentInstaller<PathIntent> {
builder.build(), treatment,
123, appId, 600);
rules.add(new FlowRuleBatchEntry(FlowRuleOperation.REMOVE, rule));
//flowRuleService.removeFlowRules(rule);
prev = link.dst();
}
FlowRuleBatchOperation batch = new FlowRuleBatchOperation(rules);
try {
flowRuleService.applyBatch(batch).get();
} catch (InterruptedException | ExecutionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return applyBatch(rules);
}
// TODO refactor below this line... ----------------------------
/**
* Generates the series of MatchActionOperations from the
* {@link FlowBatchOperation}.
* <p>
* FIXME: Currently supporting PacketPathFlow and SingleDstTreeFlow only.
* <p>
* FIXME: MatchActionOperations should have dependency field to the other
* match action operations, and this method should use this.
*
* @param op the {@link FlowBatchOperation} object
* @return the list of {@link MatchActionOperations} objects
*/
/*
private List<MatchActionOperations>
generateMatchActionOperationsList(FlowBatchOperation op) {
// MatchAction operations at head (ingress) switches.
MatchActionOperations headOps = matchActionService.createOperationsList();
// MatchAction operations at rest of the switches.
MatchActionOperations tailOps = matchActionService.createOperationsList();
MatchActionOperations removeOps = matchActionService.createOperationsList();
for (BatchOperationEntry<Operator, ?> e : op.getOperations()) {
if (e.getOperator() == FlowBatchOperation.Operator.ADD) {
generateInstallMatchActionOperations(e, tailOps, headOps);
} else if (e.getOperator() == FlowBatchOperation.Operator.REMOVE) {
generateRemoveMatchActionOperations(e, removeOps);
} else {
throw new UnsupportedOperationException(
"FlowManager supports ADD and REMOVE operations only.");
}
}
return Arrays.asList(tailOps, headOps, removeOps);
}
*/
/**
* Generates MatchActionOperations for an INSTALL FlowBatchOperation.
* <p/>
* FIXME: Currently only supports flows that generate exactly two match
* action operation sets.
*
* @param e Flow BatchOperationEntry
* @param tailOps MatchActionOperation set that the tail
* MatchActionOperations will be placed in
* @param headOps MatchActionOperation set that the head
* MatchActionOperations will be placed in
*/
/*
private void generateInstallMatchActionOperations(
BatchOperationEntry<Operator, ?> e,
MatchActionOperations tailOps,
MatchActionOperations headOps) {
if (!(e.getTarget() instanceof Flow)) {
throw new IllegalStateException(
"The target is not Flow object: " + e.getTarget());
}
// Compile flows to match-actions
Flow flow = (Flow) e.getTarget();
List<MatchActionOperations> maOps = flow.compile(
e.getOperator(), matchActionService);
verifyNotNull(maOps, "Could not compile the flow: " + flow);
verify(maOps.size() == 2,
"The flow generates unspported match-action operations.");
// Map FlowId to MatchActionIds
for (MatchActionOperations maOp : maOps) {
for (MatchActionOperationEntry entry : maOp.getOperations()) {
flowMatchActionsMap.put(
KryoFactory.serialize(flow.getId()),
KryoFactory.serialize(entry.getTarget()));
}
}
// Merge match-action operations
for (MatchActionOperationEntry mae : maOps.get(0).getOperations()) {
verify(mae.getOperator() == MatchActionOperations.Operator.INSTALL);
tailOps.addOperation(mae);
}
for (MatchActionOperationEntry mae : maOps.get(1).getOperations()) {
verify(mae.getOperator() == MatchActionOperations.Operator.INSTALL);
headOps.addOperation(mae);
}
}
*/
/**
* Generates MatchActionOperations for a REMOVE FlowBatchOperation.
*
* @param e Flow BatchOperationEntry
* @param removeOps MatchActionOperation set that the remove
* MatchActionOperations will be placed in
*/
/*
private void generateRemoveMatchActionOperations(
BatchOperationEntry<Operator, ?> e,
MatchActionOperations removeOps) {
if (!(e.getTarget() instanceof FlowId)) {
throw new IllegalStateException(
"The target is not a FlowId object: " + e.getTarget());
}
// Compile flows to match-actions
FlowId flowId = (FlowId) e.getTarget();
for (byte[] matchActionIdBytes :
flowMatchActionsMap.remove(KryoFactory.serialize(flowId))) {
MatchActionId matchActionId = KryoFactory.deserialize(matchActionIdBytes);
removeOps.addOperation(new MatchActionOperationEntry(
MatchActionOperations.Operator.REMOVE, matchActionId));
}
}
*/
}
......
......@@ -55,6 +55,7 @@ public class ProxyArpManager implements ProxyArpService {
private static final String REQUEST_NULL = "Arp request cannot be null.";
private static final String REQUEST_NOT_ARP = "Ethernet frame does not contain ARP request.";
private static final String NOT_ARP_REQUEST = "ARP is not a request.";
private static final String NOT_ARP_REPLY = "ARP is not a reply.";
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected HostService hostService;
......@@ -141,7 +142,7 @@ public class ProxyArpManager implements ProxyArpService {
checkArgument(eth.getEtherType() == Ethernet.TYPE_ARP,
REQUEST_NOT_ARP);
ARP arp = (ARP) eth.getPayload();
checkArgument(arp.getOpCode() == ARP.OP_REPLY, NOT_ARP_REQUEST);
checkArgument(arp.getOpCode() == ARP.OP_REPLY, NOT_ARP_REPLY);
Host h = hostService.getHost(HostId.hostId(eth.getDestinationMAC(),
VlanId.vlanId(eth.getVlanID())));
......
......@@ -22,9 +22,9 @@ import org.onlab.onos.net.Link;
import org.onlab.onos.net.Path;
import org.onlab.onos.net.PortNumber;
import org.onlab.onos.net.host.HostService;
import org.onlab.onos.net.topology.PathService;
import org.onlab.onos.net.provider.ProviderId;
import org.onlab.onos.net.topology.LinkWeight;
import org.onlab.onos.net.topology.PathService;
import org.onlab.onos.net.topology.Topology;
import org.onlab.onos.net.topology.TopologyService;
import org.slf4j.Logger;
......@@ -33,7 +33,6 @@ import java.util.List;
import java.util.Set;
import static com.google.common.base.Preconditions.checkNotNull;
import static org.onlab.onos.net.DeviceId.deviceId;
import static org.slf4j.LoggerFactory.getLogger;
/**
......@@ -162,8 +161,8 @@ public class PathManager implements PathService {
// edge link since the src or dst are really an infrastructure device.
private static class NotHost extends DefaultEdgeLink implements EdgeLink {
NotHost() {
super(PID, new ConnectPoint(HostId.hostId("nic:none"), P0),
new HostLocation(deviceId("none:none"), P0, 0L), false);
super(PID, new ConnectPoint(HostId.NONE, P0),
new HostLocation(DeviceId.NONE, P0, 0L), false);
}
}
}
......
......@@ -19,6 +19,7 @@ import org.junit.Before;
import org.junit.Test;
import org.onlab.onos.ApplicationId;
import org.onlab.onos.event.impl.TestEventDispatcher;
import org.onlab.onos.impl.DefaultApplicationId;
import org.onlab.onos.net.DefaultDevice;
import org.onlab.onos.net.Device;
import org.onlab.onos.net.Device.Type;
......@@ -28,6 +29,7 @@ import org.onlab.onos.net.Port;
import org.onlab.onos.net.PortNumber;
import org.onlab.onos.net.device.DeviceListener;
import org.onlab.onos.net.device.DeviceService;
import org.onlab.onos.net.flow.CompletedBatchOperation;
import org.onlab.onos.net.flow.DefaultFlowEntry;
import org.onlab.onos.net.flow.DefaultFlowRule;
import org.onlab.onos.net.flow.FlowEntry;
......@@ -58,6 +60,8 @@ import com.google.common.collect.Sets;
*/
public class FlowRuleManagerTest {
private static final ProviderId PID = new ProviderId("of", "foo");
private static final DeviceId DID = DeviceId.deviceId("of:001");
private static final int TIMEOUT = 10;
......@@ -86,7 +90,7 @@ public class FlowRuleManagerTest {
mgr.addListener(listener);
provider = new TestProvider(PID);
providerService = registry.register(provider);
appId = ApplicationId.getAppId();
appId = new TestApplicationId((short) 0, "FlowRuleManagerTest");
assertTrue("provider should be registered",
registry.getProviders().contains(provider.id()));
}
......@@ -408,7 +412,7 @@ public class FlowRuleManagerTest {
}
@Override
public Future<Void> executeBatch(
public Future<CompletedBatchOperation> executeBatch(
BatchOperation<FlowRuleBatchEntry> batch) {
// TODO Auto-generated method stub
return null;
......@@ -474,4 +478,11 @@ public class FlowRuleManagerTest {
}
public class TestApplicationId extends DefaultApplicationId {
public TestApplicationId(short id, String name) {
super(id, name);
}
}
}
......
......@@ -58,8 +58,6 @@ public class HostManagerTest {
private static final IpPrefix IP1 = IpPrefix.valueOf("10.0.0.1");
private static final IpPrefix IP2 = IpPrefix.valueOf("10.0.0.2");
private static final Set<IpPrefix> IPSET1 = Sets.newHashSet(IP1);
private static final Set<IpPrefix> IPSET2 = Sets.newHashSet(IP2);
private static final DeviceId DID1 = DeviceId.deviceId("of:001");
private static final DeviceId DID2 = DeviceId.deviceId("of:002");
......@@ -94,14 +92,14 @@ public class HostManagerTest {
provider = new TestHostProvider();
providerService = registry.register(provider);
assertTrue("provider should be registered",
registry.getProviders().contains(provider.id()));
registry.getProviders().contains(provider.id()));
}
@After
public void tearDown() {
registry.unregister(provider);
assertFalse("provider should not be registered",
registry.getProviders().contains(provider.id()));
registry.getProviders().contains(provider.id()));
mgr.removeListener(listener);
mgr.deactivate();
......@@ -109,8 +107,8 @@ public class HostManagerTest {
}
private void detect(HostId hid, MacAddress mac, VlanId vlan,
HostLocation loc, Set<IpPrefix> ips) {
HostDescription descr = new DefaultHostDescription(mac, vlan, loc, ips);
HostLocation loc, IpPrefix ip) {
HostDescription descr = new DefaultHostDescription(mac, vlan, loc, ip);
providerService.hostDetected(hid, descr);
assertNotNull("host should be found", mgr.getHost(hid));
}
......@@ -130,26 +128,26 @@ public class HostManagerTest {
assertNull("host shouldn't be found", mgr.getHost(HID1));
// host addition
detect(HID1, MAC1, VLAN1, LOC1, IPSET1);
detect(HID1, MAC1, VLAN1, LOC1, IP1);
assertEquals("exactly one should be found", 1, mgr.getHostCount());
detect(HID2, MAC2, VLAN2, LOC2, IPSET1);
detect(HID2, MAC2, VLAN2, LOC2, IP1);
assertEquals("two hosts should be found", 2, mgr.getHostCount());
validateEvents(HOST_ADDED, HOST_ADDED);
// host motion
detect(HID1, MAC1, VLAN1, LOC2, IPSET1);
detect(HID1, MAC1, VLAN1, LOC2, IP1);
validateEvents(HOST_MOVED);
assertEquals("only two hosts should be found", 2, mgr.getHostCount());
// host update
detect(HID1, MAC1, VLAN1, LOC2, IPSET2);
detect(HID1, MAC1, VLAN1, LOC2, IP2);
validateEvents(HOST_UPDATED);
assertEquals("only two hosts should be found", 2, mgr.getHostCount());
}
@Test
public void hostVanished() {
detect(HID1, MAC1, VLAN1, LOC1, IPSET1);
detect(HID1, MAC1, VLAN1, LOC1, IP1);
providerService.hostVanished(HID1);
validateEvents(HOST_ADDED, HOST_REMOVED);
......@@ -157,7 +155,7 @@ public class HostManagerTest {
}
private void validateHosts(
String msg, Iterable<Host> hosts, HostId ... ids) {
String msg, Iterable<Host> hosts, HostId... ids) {
Set<HostId> hids = Sets.newHashSet(ids);
for (Host h : hosts) {
assertTrue(msg, hids.remove(h.id()));
......@@ -167,8 +165,8 @@ public class HostManagerTest {
@Test
public void getHosts() {
detect(HID1, MAC1, VLAN1, LOC1, IPSET1);
detect(HID2, MAC2, VLAN1, LOC2, IPSET2);
detect(HID1, MAC1, VLAN1, LOC1, IP1);
detect(HID2, MAC2, VLAN1, LOC2, IP2);
validateHosts("host not properly stored", mgr.getHosts(), HID1, HID2);
validateHosts("can't get hosts by VLAN", mgr.getHostsByVlan(VLAN1), HID1, HID2);
......@@ -210,7 +208,7 @@ public class HostManagerTest {
@Test
public void bindAddressesToPort() {
PortAddresses add1 = new PortAddresses(CP1,
Sets.newHashSet(PREFIX1, PREFIX2), MAC1);
Sets.newHashSet(PREFIX1, PREFIX2), MAC1);
mgr.bindAddressesToPort(add1);
PortAddresses storedAddresses = mgr.getAddressBindingsForPort(CP1);
......@@ -241,7 +239,7 @@ public class HostManagerTest {
@Test
public void unbindAddressesFromPort() {
PortAddresses add1 = new PortAddresses(CP1,
Sets.newHashSet(PREFIX1, PREFIX2), MAC1);
Sets.newHashSet(PREFIX1, PREFIX2), MAC1);
mgr.bindAddressesToPort(add1);
PortAddresses storedAddresses = mgr.getAddressBindingsForPort(CP1);
......@@ -250,7 +248,7 @@ public class HostManagerTest {
assertNotNull(storedAddresses.mac());
PortAddresses rem1 = new PortAddresses(CP1,
Sets.newHashSet(PREFIX1), null);
Sets.newHashSet(PREFIX1), null);
mgr.unbindAddressesFromPort(rem1);
storedAddresses = mgr.getAddressBindingsForPort(CP1);
......@@ -267,7 +265,7 @@ public class HostManagerTest {
assertNull(storedAddresses.mac());
PortAddresses rem3 = new PortAddresses(CP1,
Sets.newHashSet(PREFIX2), MAC1);
Sets.newHashSet(PREFIX2), MAC1);
mgr.unbindAddressesFromPort(rem3);
storedAddresses = mgr.getAddressBindingsForPort(CP1);
......@@ -279,7 +277,7 @@ public class HostManagerTest {
@Test
public void clearAddresses() {
PortAddresses add1 = new PortAddresses(CP1,
Sets.newHashSet(PREFIX1, PREFIX2), MAC1);
Sets.newHashSet(PREFIX1, PREFIX2), MAC1);
mgr.bindAddressesToPort(add1);
PortAddresses storedAddresses = mgr.getAddressBindingsForPort(CP1);
......@@ -297,7 +295,7 @@ public class HostManagerTest {
@Test
public void getAddressBindingsForPort() {
PortAddresses add1 = new PortAddresses(CP1,
Sets.newHashSet(PREFIX1, PREFIX2), MAC1);
Sets.newHashSet(PREFIX1, PREFIX2), MAC1);
mgr.bindAddressesToPort(add1);
PortAddresses storedAddresses = mgr.getAddressBindingsForPort(CP1);
......@@ -314,7 +312,7 @@ public class HostManagerTest {
assertTrue(storedAddresses.isEmpty());
PortAddresses add1 = new PortAddresses(CP1,
Sets.newHashSet(PREFIX1, PREFIX2), MAC1);
Sets.newHashSet(PREFIX1, PREFIX2), MAC1);
mgr.bindAddressesToPort(add1);
......@@ -323,7 +321,7 @@ public class HostManagerTest {
assertTrue(storedAddresses.size() == 1);
PortAddresses add2 = new PortAddresses(CP2,
Sets.newHashSet(PREFIX3), MAC2);
Sets.newHashSet(PREFIX3), MAC2);
mgr.bindAddressesToPort(add2);
......
......@@ -65,47 +65,48 @@ public class PathManagerTest {
@Test
public void infraToEdge() {
DeviceId src = did("src");
HostId dst = hid("dst");
HostId dst = hid("12:34:56:78:90:ab/1");
fakeTopoMgr.paths.add(createPath("src", "middle", "edge"));
fakeHostMgr.hosts.put(dst, host("dst", "edge"));
fakeHostMgr.hosts.put(dst, host("12:34:56:78:90:ab/1", "edge"));
Set<Path> paths = service.getPaths(src, dst);
validatePaths(paths, 1, 3, src, dst);
}
@Test
public void edgeToInfra() {
HostId src = hid("src");
HostId src = hid("12:34:56:78:90:ab/1");
DeviceId dst = did("dst");
fakeTopoMgr.paths.add(createPath("edge", "middle", "dst"));
fakeHostMgr.hosts.put(src, host("src", "edge"));
fakeHostMgr.hosts.put(src, host("12:34:56:78:90:ab/1", "edge"));
Set<Path> paths = service.getPaths(src, dst);
validatePaths(paths, 1, 3, src, dst);
}
@Test
public void edgeToEdge() {
HostId src = hid("src");
HostId dst = hid("dst");
HostId src = hid("12:34:56:78:90:ab/1");
HostId dst = hid("12:34:56:78:90:ef/1");
fakeTopoMgr.paths.add(createPath("srcEdge", "middle", "dstEdge"));
fakeHostMgr.hosts.put(src, host("src", "srcEdge"));
fakeHostMgr.hosts.put(dst, host("dst", "dstEdge"));
fakeHostMgr.hosts.put(src, host("12:34:56:78:90:ab/1", "srcEdge"));
fakeHostMgr.hosts.put(dst, host("12:34:56:78:90:ef/1", "dstEdge"));
Set<Path> paths = service.getPaths(src, dst);
validatePaths(paths, 1, 4, src, dst);
}
@Test
public void edgeToEdgeDirect() {
HostId src = hid("src");
HostId dst = hid("dst");
fakeHostMgr.hosts.put(src, host("src", "edge"));
fakeHostMgr.hosts.put(dst, host("dst", "edge"));
HostId src = hid("12:34:56:78:90:ab/1");
HostId dst = hid("12:34:56:78:90:ef/1");
fakeHostMgr.hosts.put(src, host("12:34:56:78:90:ab/1", "edge"));
fakeHostMgr.hosts.put(dst, host("12:34:56:78:90:ef/1", "edge"));
Set<Path> paths = service.getPaths(src, dst);
validatePaths(paths, 1, 2, src, dst);
}
@Test
public void noEdge() {
Set<Path> paths = service.getPaths(hid("src"), hid("dst"));
Set<Path> paths = service.getPaths(hid("12:34:56:78:90:ab/1"),
hid("12:34:56:78:90:ef/1"));
assertTrue("there should be no paths", paths.isEmpty());
}
......
......@@ -134,11 +134,11 @@ public class TopologyManagerTest {
service.isInfrastructure(topology, new ConnectPoint(did("a"), portNumber(3))));
// One of these cannot be a broadcast point... or we have a loop...
assertFalse("should not be broadcast point",
service.isBroadcastPoint(topology, new ConnectPoint(did("a"), portNumber(1))) &&
service.isBroadcastPoint(topology, new ConnectPoint(did("b"), portNumber(1))) &&
service.isBroadcastPoint(topology, new ConnectPoint(did("c"), portNumber(1))) &&
service.isBroadcastPoint(topology, new ConnectPoint(did("d"), portNumber(1))));
// assertFalse("should not be broadcast point",
// service.isBroadcastPoint(topology, new ConnectPoint(did("a"), portNumber(1))) &&
// service.isBroadcastPoint(topology, new ConnectPoint(did("b"), portNumber(1))) &&
// service.isBroadcastPoint(topology, new ConnectPoint(did("c"), portNumber(1))) &&
// service.isBroadcastPoint(topology, new ConnectPoint(did("d"), portNumber(1))));
assertTrue("should be broadcast point",
service.isBroadcastPoint(topology, new ConnectPoint(did("a"), portNumber(3))));
}
......
......@@ -54,8 +54,18 @@
<artifactId>org.apache.felix.scr.annotations</artifactId>
</dependency>
<dependency>
<groupId>de.javakaffee</groupId>
<artifactId>kryo-serializers</artifactId>
<groupId>com.google.guava</groupId>
<artifactId>guava-testlib</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>org.easymock</groupId>
<artifactId>easymock</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
......
package org.onlab.onos.store.common.impl;
import java.util.Map;
import org.onlab.onos.cluster.NodeId;
import org.onlab.onos.store.Timestamp;
import com.google.common.collect.ImmutableMap;
/**
* Anti-Entropy advertisement message.
* <p>
* Message to advertise the information this node holds.
*
* @param <ID> ID type
*/
public class AntiEntropyAdvertisement<ID> {
private final NodeId sender;
private final ImmutableMap<ID, Timestamp> advertisement;
/**
* Creates anti-entropy advertisement message.
*
* @param sender sender of this message
* @param advertisement timestamp information of the data sender holds
*/
public AntiEntropyAdvertisement(NodeId sender, Map<ID, Timestamp> advertisement) {
this.sender = sender;
this.advertisement = ImmutableMap.copyOf(advertisement);
}
public NodeId sender() {
return sender;
}
public ImmutableMap<ID, Timestamp> advertisement() {
return advertisement;
}
// Default constructor for serializer
protected AntiEntropyAdvertisement() {
this.sender = null;
this.advertisement = null;
}
}
package org.onlab.onos.store.common.impl;
import java.util.Map;
import java.util.Set;
import org.onlab.onos.cluster.NodeId;
import org.onlab.onos.store.device.impl.VersionedValue;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
/**
* Anti-Entropy reply message.
* <p>
* Message to send in reply to advertisement or another reply.
* Suggest to the sender about the more up-to-date data this node has,
* and request for more recent data that the receiver has.
*/
public class AntiEntropyReply<ID, V extends VersionedValue<?>> {
private final NodeId sender;
private final ImmutableMap<ID, V> suggestion;
private final ImmutableSet<ID> request;
/**
* Creates a reply to anti-entropy message.
*
* @param sender sender of this message
* @param suggestion collection of more recent values, sender had
* @param request Collection of identifiers
*/
public AntiEntropyReply(NodeId sender,
Map<ID, V> suggestion,
Set<ID> request) {
this.sender = sender;
this.suggestion = ImmutableMap.copyOf(suggestion);
this.request = ImmutableSet.copyOf(request);
}
public NodeId sender() {
return sender;
}
/**
* Returns collection of values, which the recipient of this reply is likely
* to be missing or has outdated version.
*
* @return
*/
public ImmutableMap<ID, V> suggestion() {
return suggestion;
}
/**
* Returns collection of identifier to request.
*
* @return collection of identifier to request
*/
public ImmutableSet<ID> request() {
return request;
}
/**
* Checks if reply contains any suggestion or request.
*
* @return true if nothing is suggested and requested
*/
public boolean isEmpty() {
return suggestion.isEmpty() && request.isEmpty();
}
// Default constructor for serializer
protected AntiEntropyReply() {
this.sender = null;
this.suggestion = null;
this.request = null;
}
}
......@@ -30,6 +30,7 @@ public final class Timestamped<T> {
/**
* Returns the value.
*
* @return value
*/
public T value() {
......@@ -38,6 +39,7 @@ public final class Timestamped<T> {
/**
* Returns the time stamp.
*
* @return time stamp
*/
public Timestamp timestamp() {
......@@ -51,7 +53,16 @@ public final class Timestamped<T> {
* @return true if this instance is newer.
*/
public boolean isNewer(Timestamped<T> other) {
return this.timestamp.compareTo(checkNotNull(other).timestamp()) > 0;
return isNewer(checkNotNull(other).timestamp());
}
/**
* Tests if this timestamp is newer thatn the specified timestamp.
* @param timestamp to compare agains
* @return true if this instance is newer
*/
public boolean isNewer(Timestamp timestamp) {
return this.timestamp.compareTo(checkNotNull(timestamp)) > 0;
}
@Override
......
package org.onlab.onos.store.device.impl;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import org.onlab.onos.cluster.NodeId;
import org.onlab.onos.net.Device;
import org.onlab.onos.net.DeviceId;
import org.onlab.onos.store.Timestamp;
import org.onlab.onos.store.common.impl.AntiEntropyAdvertisement;
// TODO DeviceID needs to be changed to something like (ProviderID, DeviceID)
// TODO: Handle Port as part of these messages, or separate messages for Ports?
public class DeviceAntiEntropyAdvertisement
extends AntiEntropyAdvertisement<DeviceId> {
public DeviceAntiEntropyAdvertisement(NodeId sender,
Map<DeviceId, Timestamp> advertisement) {
super(sender, advertisement);
}
// May need to add ProviderID, etc.
public static DeviceAntiEntropyAdvertisement create(
NodeId self,
Collection<VersionedValue<Device>> localValues) {
Map<DeviceId, Timestamp> ads = new HashMap<>(localValues.size());
for (VersionedValue<Device> e : localValues) {
ads.put(e.entity().id(), e.timestamp());
}
return new DeviceAntiEntropyAdvertisement(self, ads);
}
// For serializer
protected DeviceAntiEntropyAdvertisement() {}
}
package org.onlab.onos.store.device.impl;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.onlab.onos.cluster.NodeId;
import org.onlab.onos.net.Device;
import org.onlab.onos.net.DeviceId;
import org.onlab.onos.store.Timestamp;
import org.onlab.onos.store.common.impl.AntiEntropyReply;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
public class DeviceAntiEntropyReply
extends AntiEntropyReply<DeviceId, VersionedValue<Device>> {
public DeviceAntiEntropyReply(NodeId sender,
Map<DeviceId, VersionedValue<Device>> suggestion,
Set<DeviceId> request) {
super(sender, suggestion, request);
}
/**
* Creates a reply to Anti-Entropy advertisement.
*
* @param advertisement to respond to
* @param self node identifier representing local node
* @param localValues local values held on this node
* @return reply message
*/
public static DeviceAntiEntropyReply reply(
DeviceAntiEntropyAdvertisement advertisement,
NodeId self,
Collection<VersionedValue<Device>> localValues
) {
ImmutableMap<DeviceId, Timestamp> ads = advertisement.advertisement();
ImmutableMap.Builder<DeviceId, VersionedValue<Device>>
sug = ImmutableMap.builder();
Set<DeviceId> req = new HashSet<>(ads.keySet());
for (VersionedValue<Device> e : localValues) {
final DeviceId id = e.entity().id();
final Timestamp local = e.timestamp();
final Timestamp theirs = ads.get(id);
if (theirs == null) {
// they don't have it, suggest
sug.put(id, e);
// don't need theirs
req.remove(id);
} else if (local.compareTo(theirs) < 0) {
// they got older one, suggest
sug.put(id, e);
// don't need theirs
req.remove(id);
} else if (local.equals(theirs)) {
// same, don't need theirs
req.remove(id);
}
}
return new DeviceAntiEntropyReply(self, sug.build(), req);
}
/**
* Creates a reply to request for values held locally.
*
* @param requests message containing the request
* @param self node identifier representing local node
* @param localValues local valeds held on this node
* @return reply message
*/
public static DeviceAntiEntropyReply reply(
DeviceAntiEntropyReply requests,
NodeId self,
Map<DeviceId, VersionedValue<Device>> localValues
) {
Set<DeviceId> reqs = requests.request();
Map<DeviceId, VersionedValue<Device>> requested = new HashMap<>(reqs.size());
for (DeviceId id : reqs) {
final VersionedValue<Device> value = localValues.get(id);
if (value != null) {
requested.put(id, value);
}
}
Set<DeviceId> empty = ImmutableSet.of();
return new DeviceAntiEntropyReply(self, requested, empty);
}
// For serializer
protected DeviceAntiEntropyReply() {}
}
package org.onlab.onos.store.device.impl;
import static com.google.common.base.Preconditions.checkNotNull;
import static org.onlab.onos.net.DefaultAnnotations.union;
import java.util.Collections;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import org.onlab.onos.net.PortNumber;
import org.onlab.onos.net.SparseAnnotations;
import org.onlab.onos.net.device.DefaultDeviceDescription;
import org.onlab.onos.net.device.DefaultPortDescription;
import org.onlab.onos.net.device.DeviceDescription;
import org.onlab.onos.net.device.PortDescription;
import org.onlab.onos.store.Timestamp;
import org.onlab.onos.store.common.impl.Timestamped;
/*
* Collection of Description of a Device and Ports, given from a Provider.
*/
class DeviceDescriptions {
private volatile Timestamped<DeviceDescription> deviceDesc;
private final ConcurrentMap<PortNumber, Timestamped<PortDescription>> portDescs;
public DeviceDescriptions(Timestamped<DeviceDescription> desc) {
this.deviceDesc = checkNotNull(desc);
this.portDescs = new ConcurrentHashMap<>();
}
public Timestamp getLatestTimestamp() {
Timestamp latest = deviceDesc.timestamp();
for (Timestamped<PortDescription> desc : portDescs.values()) {
if (desc.timestamp().compareTo(latest) > 0) {
latest = desc.timestamp();
}
}
return latest;
}
public Timestamped<DeviceDescription> getDeviceDesc() {
return deviceDesc;
}
public Timestamped<PortDescription> getPortDesc(PortNumber number) {
return portDescs.get(number);
}
public Map<PortNumber, Timestamped<PortDescription>> getPortDescs() {
return Collections.unmodifiableMap(portDescs);
}
/**
* Puts DeviceDescription, merging annotations as necessary.
*
* @param newDesc new DeviceDescription
*/
public void putDeviceDesc(Timestamped<DeviceDescription> newDesc) {
Timestamped<DeviceDescription> oldOne = deviceDesc;
Timestamped<DeviceDescription> newOne = newDesc;
if (oldOne != null) {
SparseAnnotations merged = union(oldOne.value().annotations(),
newDesc.value().annotations());
newOne = new Timestamped<DeviceDescription>(
new DefaultDeviceDescription(newDesc.value(), merged),
newDesc.timestamp());
}
deviceDesc = newOne;
}
/**
* Puts PortDescription, merging annotations as necessary.
*
* @param newDesc new PortDescription
*/
public void putPortDesc(Timestamped<PortDescription> newDesc) {
Timestamped<PortDescription> oldOne = portDescs.get(newDesc.value().portNumber());
Timestamped<PortDescription> newOne = newDesc;
if (oldOne != null) {
SparseAnnotations merged = union(oldOne.value().annotations(),
newDesc.value().annotations());
newOne = new Timestamped<PortDescription>(
new DefaultPortDescription(newDesc.value(), merged),
newDesc.timestamp());
}
portDescs.put(newOne.value().portNumber(), newOne);
}
}
......@@ -2,6 +2,7 @@ package org.onlab.onos.store.device.impl;
import org.onlab.onos.store.cluster.messaging.MessageSubject;
// TODO: add prefix to assure uniqueness.
/**
* MessageSubjects used by GossipDeviceStore peer-peer communication.
*/
......@@ -14,4 +15,8 @@ public final class GossipDeviceStoreMessageSubjects {
public static final MessageSubject DEVICE_REMOVED = new MessageSubject("peer-device-removed");
public static final MessageSubject PORT_UPDATE = new MessageSubject("peer-port-update");
public static final MessageSubject PORT_STATUS_UPDATE = new MessageSubject("peer-port-status-update");
public static final MessageSubject DEVICE_ADVERTISE = new MessageSubject("peer-device-advertisements");
// to be used with 3-way anti-entropy process
public static final MessageSubject DEVICE_REQUEST = new MessageSubject("peer-device-request");
}
......
package org.onlab.onos.store.device.impl;
import static com.google.common.base.Preconditions.checkNotNull;
import org.apache.commons.lang3.concurrent.ConcurrentException;
import org.apache.commons.lang3.concurrent.ConcurrentInitializer;
import org.onlab.onos.net.device.DeviceDescription;
import org.onlab.onos.store.common.impl.Timestamped;
// FIXME: consider removing this class
public final class InitDeviceDescs
implements ConcurrentInitializer<DeviceDescriptions> {
private final Timestamped<DeviceDescription> deviceDesc;
public InitDeviceDescs(Timestamped<DeviceDescription> deviceDesc) {
this.deviceDesc = checkNotNull(deviceDesc);
}
@Override
public DeviceDescriptions get() throws ConcurrentException {
return new DeviceDescriptions(deviceDesc);
}
}
\ No newline at end of file
......@@ -5,6 +5,8 @@ import org.onlab.onos.net.device.DeviceDescription;
import org.onlab.onos.net.provider.ProviderId;
import org.onlab.onos.store.common.impl.Timestamped;
import com.google.common.base.MoreObjects;
/**
* Information published by GossipDeviceStore to notify peers of a device
* change event.
......@@ -36,6 +38,15 @@ public class InternalDeviceEvent {
return deviceDescription;
}
@Override
public String toString() {
return MoreObjects.toStringHelper(getClass())
.add("providerId", providerId)
.add("deviceId", deviceId)
.add("deviceDescription", deviceDescription)
.toString();
}
// for serializer
protected InternalDeviceEvent() {
this.providerId = null;
......
......@@ -3,6 +3,8 @@ package org.onlab.onos.store.device.impl;
import org.onlab.onos.net.DeviceId;
import org.onlab.onos.store.Timestamp;
import com.google.common.base.MoreObjects;
/**
* Information published by GossipDeviceStore to notify peers of a device
* going offline.
......@@ -30,6 +32,14 @@ public class InternalDeviceOfflineEvent {
return timestamp;
}
@Override
public String toString() {
return MoreObjects.toStringHelper(getClass())
.add("deviceId", deviceId)
.add("timestamp", timestamp)
.toString();
}
// for serializer
@SuppressWarnings("unused")
private InternalDeviceOfflineEvent() {
......
......@@ -3,6 +3,8 @@ package org.onlab.onos.store.device.impl;
import org.onlab.onos.net.DeviceId;
import org.onlab.onos.store.Timestamp;
import com.google.common.base.MoreObjects;
/**
* Information published by GossipDeviceStore to notify peers of a device
* being administratively removed.
......@@ -30,6 +32,14 @@ public class InternalDeviceRemovedEvent {
return timestamp;
}
@Override
public String toString() {
return MoreObjects.toStringHelper(getClass())
.add("deviceId", deviceId)
.add("timestamp", timestamp)
.toString();
}
// for serializer
@SuppressWarnings("unused")
private InternalDeviceRemovedEvent() {
......
......@@ -7,6 +7,8 @@ import org.onlab.onos.net.device.PortDescription;
import org.onlab.onos.net.provider.ProviderId;
import org.onlab.onos.store.common.impl.Timestamped;
import com.google.common.base.MoreObjects;
/**
* Information published by GossipDeviceStore to notify peers of a port
* change event.
......@@ -38,6 +40,15 @@ public class InternalPortEvent {
return portDescriptions;
}
@Override
public String toString() {
return MoreObjects.toStringHelper(getClass())
.add("providerId", providerId)
.add("deviceId", deviceId)
.add("portDescriptions", portDescriptions)
.toString();
}
// for serializer
protected InternalPortEvent() {
this.providerId = null;
......
......@@ -5,6 +5,8 @@ import org.onlab.onos.net.device.PortDescription;
import org.onlab.onos.net.provider.ProviderId;
import org.onlab.onos.store.common.impl.Timestamped;
import com.google.common.base.MoreObjects;
/**
* Information published by GossipDeviceStore to notify peers of a port
* status change event.
......@@ -36,6 +38,15 @@ public class InternalPortStatusEvent {
return portDescription;
}
@Override
public String toString() {
return MoreObjects.toStringHelper(getClass())
.add("providerId", providerId)
.add("deviceId", deviceId)
.add("portDescription", portDescription)
.toString();
}
// for serializer
protected InternalPortStatusEvent() {
this.providerId = null;
......
......@@ -35,6 +35,7 @@ public class InternalPortStatusEventSerializer extends Serializer<InternalPortSt
Class<InternalPortStatusEvent> type) {
ProviderId providerId = (ProviderId) kryo.readClassAndObject(input);
DeviceId deviceId = (DeviceId) kryo.readClassAndObject(input);
@SuppressWarnings("unchecked")
Timestamped<PortDescription> portDescription = (Timestamped<PortDescription>) kryo.readClassAndObject(input);
return new InternalPortStatusEvent(providerId, deviceId, portDescription);
......
package org.onlab.onos.store.device.impl;
import java.util.Objects;
import org.onlab.onos.store.Timestamp;
/**
* Wrapper class for a entity that is versioned
* and can either be up or down.
*
* @param <T> type of the value.
*/
public class VersionedValue<T> {
private final T entity;
private final Timestamp timestamp;
private final boolean isUp;
public VersionedValue(T entity, boolean isUp, Timestamp timestamp) {
this.entity = entity;
this.isUp = isUp;
this.timestamp = timestamp;
}
/**
* Returns the value.
* @return value.
*/
public T entity() {
return entity;
}
/**
* Tells whether the entity is up or down.
* @return true if up, false otherwise.
*/
public boolean isUp() {
return isUp;
}
/**
* Returns the timestamp (version) associated with this entity.
* @return timestamp.
*/
public Timestamp timestamp() {
return timestamp;
}
@Override
public int hashCode() {
return Objects.hash(entity, timestamp, isUp);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
@SuppressWarnings("unchecked")
VersionedValue<T> that = (VersionedValue<T>) obj;
return Objects.equals(this.entity, that.entity) &&
Objects.equals(this.timestamp, that.timestamp) &&
Objects.equals(this.isUp, that.isUp);
}
// Default constructor for serializer
protected VersionedValue() {
this.entity = null;
this.isUp = false;
this.timestamp = null;
}
}
package org.onlab.onos.store.device.impl.peermsg;
import static com.google.common.base.Preconditions.checkNotNull;
import java.util.Map;
import org.onlab.onos.cluster.NodeId;
import org.onlab.onos.net.DeviceId;
import org.onlab.onos.store.Timestamp;
/**
* Device Advertisement message.
*/
public class DeviceAntiEntropyAdvertisement {
private final NodeId sender;
private final Map<DeviceFragmentId, Timestamp> deviceFingerPrints;
private final Map<PortFragmentId, Timestamp> portFingerPrints;
private final Map<DeviceId, Timestamp> offline;
public DeviceAntiEntropyAdvertisement(NodeId sender,
Map<DeviceFragmentId, Timestamp> devices,
Map<PortFragmentId, Timestamp> ports,
Map<DeviceId, Timestamp> offline) {
this.sender = checkNotNull(sender);
this.deviceFingerPrints = checkNotNull(devices);
this.portFingerPrints = checkNotNull(ports);
this.offline = checkNotNull(offline);
}
public NodeId sender() {
return sender;
}
public Map<DeviceFragmentId, Timestamp> deviceFingerPrints() {
return deviceFingerPrints;
}
public Map<PortFragmentId, Timestamp> ports() {
return portFingerPrints;
}
public Map<DeviceId, Timestamp> offline() {
return offline;
}
// For serializer
@SuppressWarnings("unused")
private DeviceAntiEntropyAdvertisement() {
this.sender = null;
this.deviceFingerPrints = null;
this.portFingerPrints = null;
this.offline = null;
}
}
package org.onlab.onos.store.device.impl.peermsg;
import static com.google.common.base.Preconditions.checkNotNull;
import java.util.Collection;
import org.onlab.onos.cluster.NodeId;
/**
* Message to request for other peers information.
*/
public class DeviceAntiEntropyRequest {
private final NodeId sender;
private final Collection<DeviceFragmentId> devices;
private final Collection<PortFragmentId> ports;
public DeviceAntiEntropyRequest(NodeId sender,
Collection<DeviceFragmentId> devices,
Collection<PortFragmentId> ports) {
this.sender = checkNotNull(sender);
this.devices = checkNotNull(devices);
this.ports = checkNotNull(ports);
}
public NodeId sender() {
return sender;
}
public Collection<DeviceFragmentId> devices() {
return devices;
}
public Collection<PortFragmentId> ports() {
return ports;
}
// For serializer
@SuppressWarnings("unused")
private DeviceAntiEntropyRequest() {
this.sender = null;
this.devices = null;
this.ports = null;
}
}
package org.onlab.onos.store.device.impl.peermsg;
import java.util.Objects;
import org.onlab.onos.net.DeviceId;
import org.onlab.onos.net.provider.ProviderId;
import com.google.common.base.MoreObjects;
/**
* Identifier for DeviceDesctiption from a Provider.
*/
public final class DeviceFragmentId {
public final ProviderId providerId;
public final DeviceId deviceId;
public DeviceFragmentId(DeviceId deviceId, ProviderId providerId) {
this.providerId = providerId;
this.deviceId = deviceId;
}
@Override
public int hashCode() {
return Objects.hash(providerId, deviceId);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof DeviceFragmentId)) {
return false;
}
DeviceFragmentId that = (DeviceFragmentId) obj;
return Objects.equals(this.deviceId, that.deviceId) &&
Objects.equals(this.providerId, that.providerId);
}
@Override
public String toString() {
return MoreObjects.toStringHelper(getClass())
.add("providerId", providerId)
.add("deviceId", deviceId)
.toString();
}
// for serializer
@SuppressWarnings("unused")
private DeviceFragmentId() {
this.providerId = null;
this.deviceId = null;
}
}
\ No newline at end of file
package org.onlab.onos.store.device.impl.peermsg;
import java.util.Objects;
import org.onlab.onos.net.DeviceId;
import org.onlab.onos.net.PortNumber;
import org.onlab.onos.net.provider.ProviderId;
import com.google.common.base.MoreObjects;
/**
* Identifier for PortDescription from a Provider.
*/
public final class PortFragmentId {
public final ProviderId providerId;
public final DeviceId deviceId;
public final PortNumber portNumber;
public PortFragmentId(DeviceId deviceId, ProviderId providerId,
PortNumber portNumber) {
this.providerId = providerId;
this.deviceId = deviceId;
this.portNumber = portNumber;
}
@Override
public int hashCode() {
return Objects.hash(providerId, deviceId, portNumber);
};
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof PortFragmentId)) {
return false;
}
PortFragmentId that = (PortFragmentId) obj;
return Objects.equals(this.deviceId, that.deviceId) &&
Objects.equals(this.portNumber, that.portNumber) &&
Objects.equals(this.providerId, that.providerId);
}
@Override
public String toString() {
return MoreObjects.toStringHelper(getClass())
.add("providerId", providerId)
.add("deviceId", deviceId)
.add("portNumber", portNumber)
.toString();
}
// for serializer
@SuppressWarnings("unused")
private PortFragmentId() {
this.providerId = null;
this.deviceId = null;
this.portNumber = null;
}
}
\ No newline at end of file
/**
* Structure and utilities used for inter-Node messaging.
*/
package org.onlab.onos.store.device.impl.peermsg;
......@@ -43,8 +43,8 @@ public class DistributedFlowRuleStore
private final Multimap<DeviceId, FlowEntry> flowEntries =
ArrayListMultimap.<DeviceId, FlowEntry>create();
private final Multimap<ApplicationId, FlowRule> flowEntriesById =
ArrayListMultimap.<ApplicationId, FlowRule>create();
private final Multimap<Short, FlowRule> flowEntriesById =
ArrayListMultimap.<Short, FlowRule>create();
@Activate
public void activate() {
......@@ -83,7 +83,7 @@ public class DistributedFlowRuleStore
@Override
public synchronized Iterable<FlowRule> getFlowRulesByAppId(ApplicationId appId) {
Collection<FlowRule> rules = flowEntriesById.get(appId);
Collection<FlowRule> rules = flowEntriesById.get(appId.id());
if (rules == null) {
return Collections.emptyList();
}
......
package org.onlab.onos.store.host.impl;
import static org.onlab.onos.net.host.HostEvent.Type.HOST_ADDED;
import static org.onlab.onos.net.host.HostEvent.Type.HOST_MOVED;
import static org.onlab.onos.net.host.HostEvent.Type.HOST_REMOVED;
import static org.onlab.onos.net.host.HostEvent.Type.HOST_UPDATED;
import static org.slf4j.LoggerFactory.getLogger;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Multimap;
import com.google.common.collect.Sets;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Deactivate;
import org.apache.felix.scr.annotations.Service;
import org.onlab.onos.net.Annotations;
import org.onlab.onos.net.ConnectPoint;
import org.onlab.onos.net.DefaultHost;
import org.onlab.onos.net.DeviceId;
import org.onlab.onos.net.Host;
import org.onlab.onos.net.HostId;
import org.onlab.onos.net.HostLocation;
import org.onlab.onos.net.host.HostDescription;
import org.onlab.onos.net.host.HostEvent;
import org.onlab.onos.net.host.HostStore;
......@@ -33,10 +27,13 @@ import org.onlab.packet.MacAddress;
import org.onlab.packet.VlanId;
import org.slf4j.Logger;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Multimap;
import com.google.common.collect.Sets;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import static org.onlab.onos.net.host.HostEvent.Type.*;
import static org.slf4j.LoggerFactory.getLogger;
/**
* Manages inventory of end-station hosts using trivial in-memory
......@@ -46,13 +43,13 @@ import com.google.common.collect.Sets;
@Component(immediate = true)
@Service
public class DistributedHostStore
extends AbstractStore<HostEvent, HostStoreDelegate>
implements HostStore {
extends AbstractStore<HostEvent, HostStoreDelegate>
implements HostStore {
private final Logger log = getLogger(getClass());
// Host inventory
private final Map<HostId, Host> hosts = new ConcurrentHashMap<>();
private final Map<HostId, StoredHost> hosts = new ConcurrentHashMap<>(2000000, 0.75f, 16);
// Hosts tracked by their location
private final Multimap<ConnectPoint, Host> locations = HashMultimap.create();
......@@ -72,8 +69,8 @@ implements HostStore {
@Override
public HostEvent createOrUpdateHost(ProviderId providerId, HostId hostId,
HostDescription hostDescription) {
Host host = hosts.get(hostId);
HostDescription hostDescription) {
StoredHost host = hosts.get(hostId);
if (host == null) {
return createHost(providerId, hostId, hostDescription);
}
......@@ -82,12 +79,12 @@ implements HostStore {
// creates a new host and sends HOST_ADDED
private HostEvent createHost(ProviderId providerId, HostId hostId,
HostDescription descr) {
DefaultHost newhost = new DefaultHost(providerId, hostId,
descr.hwAddress(),
descr.vlan(),
descr.location(),
descr.ipAddresses());
HostDescription descr) {
StoredHost newhost = new StoredHost(providerId, hostId,
descr.hwAddress(),
descr.vlan(),
descr.location(),
ImmutableSet.of(descr.ipAddress()));
synchronized (this) {
hosts.put(hostId, newhost);
locations.put(descr.location(), newhost);
......@@ -96,28 +93,24 @@ implements HostStore {
}
// checks for type of update to host, sends appropriate event
private HostEvent updateHost(ProviderId providerId, Host host,
HostDescription descr) {
DefaultHost updated;
private HostEvent updateHost(ProviderId providerId, StoredHost host,
HostDescription descr) {
HostEvent event;
if (!host.location().equals(descr.location())) {
updated = new DefaultHost(providerId, host.id(),
host.mac(),
host.vlan(),
descr.location(),
host.ipAddresses());
event = new HostEvent(HOST_MOVED, updated);
} else if (!(host.ipAddresses().equals(descr.ipAddresses()))) {
updated = new DefaultHost(providerId, host.id(),
host.mac(),
host.vlan(),
descr.location(),
descr.ipAddresses());
event = new HostEvent(HOST_UPDATED, updated);
} else {
host.setLocation(descr.location());
return new HostEvent(HOST_MOVED, host);
}
if (host.ipAddresses().contains(descr.ipAddress())) {
return null;
}
Set<IpPrefix> addresses = new HashSet<>(host.ipAddresses());
addresses.add(descr.ipAddress());
StoredHost updated = new StoredHost(providerId, host.id(),
host.mac(), host.vlan(),
descr.location(), addresses);
event = new HostEvent(HOST_UPDATED, updated);
synchronized (this) {
hosts.put(host.id(), updated);
locations.remove(host.location(), host);
......@@ -145,7 +138,7 @@ implements HostStore {
@Override
public Iterable<Host> getHosts() {
return Collections.unmodifiableSet(new HashSet<>(hosts.values()));
return ImmutableSet.<Host>copyOf(hosts.values());
}
@Override
......@@ -275,4 +268,35 @@ implements HostStore {
return addresses;
}
// Auxiliary extension to allow location to mutate.
private class StoredHost extends DefaultHost {
private HostLocation location;
/**
* Creates an end-station host using the supplied information.
*
* @param providerId provider identity
* @param id host identifier
* @param mac host MAC address
* @param vlan host VLAN identifier
* @param location host location
* @param ips host IP addresses
* @param annotations optional key/value annotations
*/
public StoredHost(ProviderId providerId, HostId id,
MacAddress mac, VlanId vlan, HostLocation location,
Set<IpPrefix> ips, Annotations... annotations) {
super(providerId, id, mac, vlan, location, ips, annotations);
this.location = location;
}
void setLocation(HostLocation location) {
this.location = location;
}
@Override
public HostLocation location() {
return location;
}
}
}
......
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.