Merge "Unit tests for LinkKey and Criteria classes."
diff --git a/apps/calendar/pom.xml b/apps/calendar/pom.xml
index dd73c99..396a4d0 100644
--- a/apps/calendar/pom.xml
+++ b/apps/calendar/pom.xml
@@ -62,6 +62,32 @@
<groupId>org.osgi</groupId>
<artifactId>org.osgi.core</artifactId>
</dependency>
+
+ <dependency>
+ <groupId>org.onlab.onos</groupId>
+ <artifactId>onlab-thirdparty</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.onlab.onos</groupId>
+ <artifactId>onlab-misc</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.onlab.onos</groupId>
+ <artifactId>onlab-junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.onlab.onos</groupId>
+ <artifactId>onos-cli</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.karaf.shell</groupId>
+ <artifactId>org.apache.karaf.shell.console</artifactId>
+ </dependency>
</dependencies>
<build>
@@ -77,6 +103,7 @@
${project.groupId}.${project.artifactId}
</Bundle-SymbolicName>
<Import-Package>
+ org.slf4j,
org.osgi.framework,
javax.ws.rs,javax.ws.rs.core,
com.sun.jersey.api.core,
diff --git a/apps/calendar/src/main/java/org/onlab/onos/calendar/BandwidthCalendarResource.java b/apps/calendar/src/main/java/org/onlab/onos/calendar/BandwidthCalendarResource.java
index cd8cc5c..cfa1a63 100644
--- a/apps/calendar/src/main/java/org/onlab/onos/calendar/BandwidthCalendarResource.java
+++ b/apps/calendar/src/main/java/org/onlab/onos/calendar/BandwidthCalendarResource.java
@@ -16,43 +16,47 @@
package org.onlab.onos.calendar;
import java.net.URI;
-
-import javax.ws.rs.POST;
-import javax.ws.rs.Path;
-import javax.ws.rs.PathParam;
-import javax.ws.rs.core.Response;
-
-import org.onlab.onos.core.ApplicationId;
-import org.onlab.onos.core.CoreService;
import org.onlab.onos.net.ConnectPoint;
import org.onlab.onos.net.DeviceId;
+import org.onlab.onos.net.intent.IntentService;
+import org.onlab.rest.BaseResource;
+import javax.ws.rs.POST;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.core.Response;
+import org.onlab.onos.core.ApplicationId;
+import org.onlab.onos.core.CoreService;
import org.onlab.onos.net.flow.DefaultTrafficSelector;
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.IntentService;
-import org.onlab.onos.net.intent.PointToPointIntentWithBandwidthConstraint;
-import org.onlab.onos.net.resource.BandwidthResourceRequest;
+import org.onlab.onos.net.intent.PointToPointIntent;
import org.onlab.packet.Ethernet;
-import org.onlab.rest.BaseResource;
-
import static org.onlab.onos.net.PortNumber.portNumber;
import static org.onlab.onos.net.flow.DefaultTrafficTreatment.builder;
+import static org.slf4j.LoggerFactory.getLogger;
+import org.slf4j.Logger;
+
/**
* Web resource for triggering calendared intents.
*/
-@Path("intent")
+@javax.ws.rs.Path("intent")
public class BandwidthCalendarResource extends BaseResource {
+ private static final Logger log = getLogger(BandwidthCalendarResource.class);
+
+ @javax.ws.rs.Path("/{src}/{dst}/{srcPort}/{dstPort}/{bandwidth}")
@POST
- @Path("{src}/{dst}/{srcPort}/{dstPort}/{bandwidth}")
public Response createIntent(@PathParam("src") String src,
@PathParam("dst") String dst,
@PathParam("srcPort") String srcPort,
@PathParam("dstPort") String dstPort,
@PathParam("bandwidth") String bandwidth) {
- // TODO: implement calls to intent framework
+
+ log.info("Receiving Create Intent request...");
+ log.info("Path Constraints: Src = {} SrcPort = {} Dest = {} DestPort = {} BW = {}",
+ src, srcPort, dst, dstPort, bandwidth);
+
IntentService service = get(IntentService.class);
ConnectPoint srcPoint = new ConnectPoint(deviceId(src), portNumber(srcPort));
@@ -61,13 +65,38 @@
TrafficSelector selector = buildTrafficSelector();
TrafficTreatment treatment = builder().build();
- Intent intent = new PointToPointIntentWithBandwidthConstraint(
- appId(), selector, treatment,
- srcPoint, dstPoint, new BandwidthResourceRequest(Double.parseDouble(bandwidth)));
- service.submit(intent);
+ PointToPointIntent intentP2P =
+ new PointToPointIntent(appId(), selector, treatment,
+ srcPoint, dstPoint);
+ service.submit(intentP2P);
+ log.info("Submitted Calendar App intent: src = " + src + "dest = " + dst
+ + "srcPort = " + srcPort + "destPort" + dstPort + "intentID = " + intentP2P.id().toString());
+ String reply = intentP2P.id().toString() + "\n";
- return Response.ok("Yo! We got src=" + srcPoint + "; dst=" + dstPoint +
- "; bw=" + bandwidth + "; intent service " + service).build();
+ return Response.ok(reply).build();
+ }
+
+ @javax.ws.rs.Path("/cancellation/{intentId}")
+ @DELETE
+ public Response withdrawIntent(@PathParam("intentId") String intentId) {
+
+ log.info("Receiving Teardown request...");
+ log.info("Withdraw intentId = {} ", intentId);
+
+ String reply = "ok\n";
+ return Response.ok(reply).build();
+ }
+
+ @javax.ws.rs.Path("/modification/{intentId}/{bandwidth}")
+ @POST
+ public Response modifyBandwidth(@PathParam("intentId") String intentId,
+ @PathParam("bandwidth") String bandwidth) {
+
+ log.info("Receiving Modify request...");
+ log.info("Modify bw for intentId = {} with new bandwidth = {}", intentId, bandwidth);
+
+ String reply = "ok\n";
+ return Response.ok(reply).build();
}
private TrafficSelector buildTrafficSelector() {
@@ -86,5 +115,4 @@
protected ApplicationId appId() {
return get(CoreService.class).registerApplication("org.onlab.onos.calendar");
}
-
}
diff --git a/apps/foo/src/main/java/org/onlab/onos/foo/IOLoopTestClient.java b/apps/foo/src/main/java/org/onlab/onos/foo/IOLoopTestClient.java
index dd72128..7ea0d26 100644
--- a/apps/foo/src/main/java/org/onlab/onos/foo/IOLoopTestClient.java
+++ b/apps/foo/src/main/java/org/onlab/onos/foo/IOLoopTestClient.java
@@ -85,7 +85,7 @@
System.exit(0);
}
- /**
+ /*
* Starts a standalone IO loop test client.
*
* @param args command-line arguments
diff --git a/apps/foo/src/main/java/org/onlab/onos/foo/IOLoopTestServer.java b/apps/foo/src/main/java/org/onlab/onos/foo/IOLoopTestServer.java
index a4aed30..7fe743c 100644
--- a/apps/foo/src/main/java/org/onlab/onos/foo/IOLoopTestServer.java
+++ b/apps/foo/src/main/java/org/onlab/onos/foo/IOLoopTestServer.java
@@ -85,7 +85,7 @@
System.exit(0);
}
- /**
+ /*
* Starts a standalone IO loop test server.
*
* @param args command-line arguments
diff --git a/apps/foo/src/main/java/org/onlab/onos/foo/TestMessage.java b/apps/foo/src/main/java/org/onlab/onos/foo/TestMessage.java
index 20dfee0..fcfbb07 100644
--- a/apps/foo/src/main/java/org/onlab/onos/foo/TestMessage.java
+++ b/apps/foo/src/main/java/org/onlab/onos/foo/TestMessage.java
@@ -32,6 +32,7 @@
/**
* Creates a new message with the specified data.
*
+ * @param length message length
* @param requestorTime requester time
* @param responderTime responder time
* @param padding message padding
diff --git a/apps/oecfg/pom.xml b/apps/oecfg/pom.xml
index 7a84412..d0cd081 100644
--- a/apps/oecfg/pom.xml
+++ b/apps/oecfg/pom.xml
@@ -49,6 +49,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
+ <version>2.3</version>
<executions>
<execution>
<phase>package</phase>
diff --git a/apps/sdnip/src/main/java/org/onlab/onos/sdnip/HostToInterfaceAdaptor.java b/apps/sdnip/src/main/java/org/onlab/onos/sdnip/HostToInterfaceAdaptor.java
index f9d6951..604d12d 100644
--- a/apps/sdnip/src/main/java/org/onlab/onos/sdnip/HostToInterfaceAdaptor.java
+++ b/apps/sdnip/src/main/java/org/onlab/onos/sdnip/HostToInterfaceAdaptor.java
@@ -53,11 +53,13 @@
public Interface getInterface(ConnectPoint connectPoint) {
checkNotNull(connectPoint);
- PortAddresses portAddresses =
+ Set<PortAddresses> portAddresses =
hostService.getAddressBindingsForPort(connectPoint);
- if (!portAddresses.ipAddresses().isEmpty()) {
- return new Interface(portAddresses);
+ for (PortAddresses addresses : portAddresses) {
+ if (addresses.connectPoint().equals(connectPoint)) {
+ return new Interface(addresses);
+ }
}
return null;
diff --git a/apps/sdnip/src/main/java/org/onlab/onos/sdnip/Router.java b/apps/sdnip/src/main/java/org/onlab/onos/sdnip/Router.java
index e4eafb5..f1a14e7 100644
--- a/apps/sdnip/src/main/java/org/onlab/onos/sdnip/Router.java
+++ b/apps/sdnip/src/main/java/org/onlab/onos/sdnip/Router.java
@@ -143,7 +143,7 @@
}
/**
- * Starts the Router.
+ * Starts the router.
*/
public void start() {
bgpUpdatesExecutor.execute(new Runnable() {
@@ -161,6 +161,14 @@
});
}
+ /**
+ * Shuts the router down.
+ */
+ public void shutdown() {
+ bgpUpdatesExecutor.shutdownNow();
+ bgpIntentsSynchronizerExecutor.shutdownNow();
+ }
+
//@Override TODO hook this up to something
public void leaderChanged(boolean isLeader) {
log.debug("Leader changed: {}", isLeader);
diff --git a/apps/sdnip/src/main/java/org/onlab/onos/sdnip/SdnIp.java b/apps/sdnip/src/main/java/org/onlab/onos/sdnip/SdnIp.java
index 0f6e38a..4abefa7 100644
--- a/apps/sdnip/src/main/java/org/onlab/onos/sdnip/SdnIp.java
+++ b/apps/sdnip/src/main/java/org/onlab/onos/sdnip/SdnIp.java
@@ -90,6 +90,9 @@
@Deactivate
protected void deactivate() {
+ bgpSessionManager.shutDown();
+ router.shutdown();
+
log.info("Stopped");
}
diff --git a/apps/sdnip/src/main/java/org/onlab/onos/sdnip/config/SdnIpConfigReader.java b/apps/sdnip/src/main/java/org/onlab/onos/sdnip/config/SdnIpConfigReader.java
index cde65c6..2fcd1fe 100644
--- a/apps/sdnip/src/main/java/org/onlab/onos/sdnip/config/SdnIpConfigReader.java
+++ b/apps/sdnip/src/main/java/org/onlab/onos/sdnip/config/SdnIpConfigReader.java
@@ -16,6 +16,7 @@
package org.onlab.onos.sdnip.config;
import java.io.File;
+import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Collections;
import java.util.Map;
@@ -40,12 +41,8 @@
private static final String DEFAULT_CONFIG_FILE = "config/sdnip.json";
private String configFileName = DEFAULT_CONFIG_FILE;
- //private Map<String, Interface> interfaces;
- // We call the BGP routers in our SDN network the BGP speakers, and call
- // the BGP routers outside our SDN network the BGP peers.
- private Map<String, BgpSpeaker> bgpSpeakers;
- private Map<IpAddress, BgpPeer> bgpPeers;
- //private InvertedRadixTree<Interface> interfaceRoutes;
+ private Map<String, BgpSpeaker> bgpSpeakers = new ConcurrentHashMap<>();
+ private Map<IpAddress, BgpPeer> bgpPeers = new ConcurrentHashMap<>();
/**
* Reads the info contained in the configuration file.
@@ -58,78 +55,25 @@
try {
Configuration config = mapper.readValue(gatewaysFile, Configuration.class);
- /*interfaces = new ConcurrentHashMap<>();
- for (Interface intf : config.getInterfaces()) {
- interfaces.put(intf.getName(), intf);
- }*/
- bgpSpeakers = new ConcurrentHashMap<>();
for (BgpSpeaker speaker : config.getBgpSpeakers()) {
bgpSpeakers.put(speaker.name(), speaker);
}
- bgpPeers = new ConcurrentHashMap<>();
for (BgpPeer peer : config.getPeers()) {
bgpPeers.put(peer.ipAddress(), peer);
}
+ } catch (FileNotFoundException e) {
+ log.warn("Configuration file not found: {}", configFileName);
} catch (IOException e) {
log.error("Error reading JSON file", e);
- //throw new ConfigurationRuntimeException("Error in JSON file", e);
}
-
- // Populate the interface InvertedRadixTree
- /*for (Interface intf : interfaces.values()) {
- Ip4Prefix prefix = intf.getIp4Prefix();
- String binaryString = RouteEntry.createBinaryString(prefix);
- interfaceRoutes.put(binaryString, intf);
- }*/
}
- /*
- * To find the Interface which has longest matchable IP prefix (sub-network
- * prefix) to next hop IP address.
- *
- * @param address the IP address of next hop router
- * @return the Interface which has longest matchable IP prefix
- */
- /*private Interface longestInterfacePrefixMatch(IpAddress address) {
- Ip4Prefix prefixToSearchFor =
- new Ip4Prefix(address, (short) Ip4Address.BIT_LENGTH);
- String binaryString = RouteEntry.createBinaryString(prefixToSearchFor);
-
- Iterator<Interface> it =
- interfaceRoutes.getValuesForKeysPrefixing(binaryString).iterator();
- Interface intf = null;
- // Find the last prefix, which will be the longest prefix
- while (it.hasNext()) {
- intf = it.next();
- }
-
- return intf;
- }*/
-
- /*@Override
- public Interface getOutgoingInterface(IpAddress dstIpAddress) {
- return longestInterfacePrefixMatch(dstIpAddress);
- }*/
-
public void init() {
- //interfaceRoutes = new ConcurrentInvertedRadixTree<>(
- //new DefaultByteArrayNodeFactory());
-
- // Reading config values
- /*String configFilenameParameter = context.getConfigParams(this).get("configfile");
- if (configFilenameParameter != null) {
- currentConfigFilename = configFilenameParameter;
- }*/
log.debug("Config file set to {}", configFileName);
readConfiguration(configFileName);
}
- /*@Override
- public Map<String, Interface> getInterfaces() {
- return Collections.unmodifiableMap(interfaces);
- }*/
-
@Override
public Map<String, BgpSpeaker> getBgpSpeakers() {
return Collections.unmodifiableMap(bgpSpeakers);
diff --git a/apps/sdnip/src/test/java/org/onlab/onos/sdnip/HostToInterfaceAdaptorTest.java b/apps/sdnip/src/test/java/org/onlab/onos/sdnip/HostToInterfaceAdaptorTest.java
index 2a65616..9e31389 100644
--- a/apps/sdnip/src/test/java/org/onlab/onos/sdnip/HostToInterfaceAdaptorTest.java
+++ b/apps/sdnip/src/test/java/org/onlab/onos/sdnip/HostToInterfaceAdaptorTest.java
@@ -23,6 +23,7 @@
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
+import java.util.Collections;
import java.util.Map;
import java.util.Set;
@@ -63,10 +64,6 @@
private static final ConnectPoint NON_EXISTENT_CP = new ConnectPoint(
DeviceId.deviceId("doesnotexist"), PortNumber.portNumber(1));
- private static final PortAddresses DEFAULT_PA = new PortAddresses(
- NON_EXISTENT_CP, null, null);
-
-
@Before
public void setUp() throws Exception {
hostService = createMock(HostService.class);
@@ -123,7 +120,8 @@
MacAddress mac) {
PortAddresses pa = new PortAddresses(cp, ipAddresses, mac);
portAddresses.add(pa);
- expect(hostService.getAddressBindingsForPort(cp)).andReturn(pa).anyTimes();
+ expect(hostService.getAddressBindingsForPort(cp)).andReturn(
+ Collections.singleton(pa)).anyTimes();
Interface intf = new Interface(cp, ipAddresses, mac);
interfaces.put(cp, intf);
@@ -158,7 +156,7 @@
// Try and get an interface for a connect point with no addresses
reset(hostService);
expect(hostService.getAddressBindingsForPort(NON_EXISTENT_CP))
- .andReturn(DEFAULT_PA).anyTimes();
+ .andReturn(Collections.<PortAddresses>emptySet()).anyTimes();
replay(hostService);
assertNull(adaptor.getInterface(NON_EXISTENT_CP));
diff --git a/cli/src/main/java/org/onlab/onos/cli/AbstractShellCommand.java b/cli/src/main/java/org/onlab/onos/cli/AbstractShellCommand.java
index 35dc2a2..122245a 100644
--- a/cli/src/main/java/org/onlab/onos/cli/AbstractShellCommand.java
+++ b/cli/src/main/java/org/onlab/onos/cli/AbstractShellCommand.java
@@ -92,6 +92,7 @@
/**
* Produces a JSON object from the specified key/value annotations.
*
+ * @param mapper ObjectMapper to use while converting to JSON
* @param annotations key/value annotations
* @return JSON object
*/
diff --git a/cli/src/main/java/org/onlab/onos/cli/net/AddPointToPointIntentWithBandwidthConstraintCommand.java b/cli/src/main/java/org/onlab/onos/cli/net/AddPointToPointIntentWithBandwidthConstraintCommand.java
index 1207f1a..1590ae7 100644
--- a/cli/src/main/java/org/onlab/onos/cli/net/AddPointToPointIntentWithBandwidthConstraintCommand.java
+++ b/cli/src/main/java/org/onlab/onos/cli/net/AddPointToPointIntentWithBandwidthConstraintCommand.java
@@ -27,8 +27,7 @@
import org.onlab.onos.net.flow.TrafficTreatment;
import org.onlab.onos.net.intent.Intent;
import org.onlab.onos.net.intent.IntentService;
-import org.onlab.onos.net.intent.PointToPointIntentWithBandwidthConstraint;
-import org.onlab.onos.net.resource.BandwidthResourceRequest;
+import org.onlab.onos.net.intent.PointToPointIntent;
import static org.onlab.onos.net.DeviceId.deviceId;
import static org.onlab.onos.net.PortNumber.portNumber;
@@ -73,9 +72,10 @@
TrafficSelector selector = buildTrafficSelector();
TrafficTreatment treatment = builder().build();
- Intent intent = new PointToPointIntentWithBandwidthConstraint(
+ // FIXME: add bandwitdh constraint
+ Intent intent = new PointToPointIntent(
appId(), selector, treatment,
- ingress, egress, new BandwidthResourceRequest(bandwidth));
+ ingress, egress);
service.submit(intent);
}
diff --git a/cli/src/main/java/org/onlab/onos/cli/net/ConnectivityIntentCommand.java b/cli/src/main/java/org/onlab/onos/cli/net/ConnectivityIntentCommand.java
index b61a646..d8ec3c7 100644
--- a/cli/src/main/java/org/onlab/onos/cli/net/ConnectivityIntentCommand.java
+++ b/cli/src/main/java/org/onlab/onos/cli/net/ConnectivityIntentCommand.java
@@ -44,6 +44,7 @@
/**
* Constructs a traffic selector based on the command line arguments
* presented to the command.
+ * @return traffic selector
*/
protected TrafficSelector buildTrafficSelector() {
TrafficSelector.Builder selectorBuilder = DefaultTrafficSelector.builder();
diff --git a/cli/src/main/java/org/onlab/onos/cli/net/FlowsListCommand.java b/cli/src/main/java/org/onlab/onos/cli/net/FlowsListCommand.java
index ab79d03..0bc7c61 100644
--- a/cli/src/main/java/org/onlab/onos/cli/net/FlowsListCommand.java
+++ b/cli/src/main/java/org/onlab/onos/cli/net/FlowsListCommand.java
@@ -141,7 +141,8 @@
/**
* Returns the list of devices sorted using the device ID URIs.
*
- * @param service device service
+ * @param deviceService device service
+ * @param service flow rule service
* @return sorted device list
*/
protected Map<Device, List<FlowEntry>> getSortedFlows(DeviceService deviceService,
@@ -175,7 +176,8 @@
* Prints flows.
*
* @param d the device
- * @param flows the set of flows for that device.
+ * @param flows the set of flows for that device
+ * @param coreService core system service
*/
protected void printFlows(Device d, List<FlowEntry> flows,
CoreService coreService) {
diff --git a/cli/src/main/java/org/onlab/onos/cli/net/IntentsListCommand.java b/cli/src/main/java/org/onlab/onos/cli/net/IntentsListCommand.java
index f5c65e5..e18cc5e 100644
--- a/cli/src/main/java/org/onlab/onos/cli/net/IntentsListCommand.java
+++ b/cli/src/main/java/org/onlab/onos/cli/net/IntentsListCommand.java
@@ -78,6 +78,9 @@
if (!ci.treatment().instructions().isEmpty()) {
print(" treatment=%s", ci.treatment().instructions());
}
+ if (ci.constraints() != null && !ci.constraints().isEmpty()) {
+ print(" constraints=%s", ci.constraints());
+ }
}
if (intent instanceof PointToPointIntent) {
diff --git a/core/api/src/main/java/org/onlab/onos/cluster/DefaultControllerNode.java b/core/api/src/main/java/org/onlab/onos/cluster/DefaultControllerNode.java
index 3aee1a6..4ed972f 100644
--- a/core/api/src/main/java/org/onlab/onos/cluster/DefaultControllerNode.java
+++ b/core/api/src/main/java/org/onlab/onos/cluster/DefaultControllerNode.java
@@ -54,6 +54,7 @@
*
* @param id instance identifier
* @param ip instance IP address
+ * @param tcpPort TCP port
*/
public DefaultControllerNode(NodeId id, IpAddress ip, int tcpPort) {
this.id = id;
diff --git a/core/api/src/main/java/org/onlab/onos/net/DefaultDevice.java b/core/api/src/main/java/org/onlab/onos/net/DefaultDevice.java
index f6ba352..1207427 100644
--- a/core/api/src/main/java/org/onlab/onos/net/DefaultDevice.java
+++ b/core/api/src/main/java/org/onlab/onos/net/DefaultDevice.java
@@ -54,6 +54,7 @@
* @param hwVersion device HW version
* @param swVersion device SW version
* @param serialNumber device serial number
+ * @param chassisId chasis id
* @param annotations optional key/value annotations
*/
public DefaultDevice(ProviderId providerId, DeviceId id, Type type,
diff --git a/core/api/src/main/java/org/onlab/onos/net/DeviceId.java b/core/api/src/main/java/org/onlab/onos/net/DeviceId.java
index 5dcc076..e1080f2 100644
--- a/core/api/src/main/java/org/onlab/onos/net/DeviceId.java
+++ b/core/api/src/main/java/org/onlab/onos/net/DeviceId.java
@@ -48,6 +48,7 @@
* Creates a device id using the supplied URI.
*
* @param uri device URI
+ * @return DeviceId
*/
public static DeviceId deviceId(URI uri) {
return new DeviceId(uri);
@@ -57,6 +58,7 @@
* Creates a device id using the supplied URI string.
*
* @param string device URI string
+ * @return DeviceId
*/
public static DeviceId deviceId(String string) {
return deviceId(URI.create(string));
diff --git a/core/api/src/main/java/org/onlab/onos/net/LinkKey.java b/core/api/src/main/java/org/onlab/onos/net/LinkKey.java
index 48df67a..c4e461d 100644
--- a/core/api/src/main/java/org/onlab/onos/net/LinkKey.java
+++ b/core/api/src/main/java/org/onlab/onos/net/LinkKey.java
@@ -84,7 +84,7 @@
@Override
public int hashCode() {
- return Objects.hash(src(), dst);
+ return Objects.hash(src, dst);
}
@Override
diff --git a/core/api/src/main/java/org/onlab/onos/net/device/DefaultDeviceDescription.java b/core/api/src/main/java/org/onlab/onos/net/device/DefaultDeviceDescription.java
index 1bbdaf7..e3aa2ed 100644
--- a/core/api/src/main/java/org/onlab/onos/net/device/DefaultDeviceDescription.java
+++ b/core/api/src/main/java/org/onlab/onos/net/device/DefaultDeviceDescription.java
@@ -47,6 +47,7 @@
* @param hwVersion device HW version
* @param swVersion device SW version
* @param serialNumber device serial number
+ * @param chassis chasis id
* @param annotations optional key/value annotations map
*/
public DefaultDeviceDescription(URI uri, Type type, String manufacturer,
diff --git a/core/api/src/main/java/org/onlab/onos/net/device/DeviceProviderService.java b/core/api/src/main/java/org/onlab/onos/net/device/DeviceProviderService.java
index 6f71495..d156b28 100644
--- a/core/api/src/main/java/org/onlab/onos/net/device/DeviceProviderService.java
+++ b/core/api/src/main/java/org/onlab/onos/net/device/DeviceProviderService.java
@@ -32,6 +32,7 @@
/**
* Signals the core that a device has connected or has been detected somehow.
*
+ * @param deviceId device identifier
* @param deviceDescription information about network device
*/
void deviceConnected(DeviceId deviceId, DeviceDescription deviceDescription);
@@ -65,7 +66,7 @@
*
* @param deviceId identity of the device
* @param requested mastership role that was requested by the node
- * @param replied mastership role the switch accepted
+ * @param response mastership role the switch accepted
*/
void receivedRoleReply(DeviceId deviceId, MastershipRole requested, MastershipRole response);
diff --git a/core/api/src/main/java/org/onlab/onos/net/device/DeviceStore.java b/core/api/src/main/java/org/onlab/onos/net/device/DeviceStore.java
index f3f124a..bc0b5c1 100644
--- a/core/api/src/main/java/org/onlab/onos/net/device/DeviceStore.java
+++ b/core/api/src/main/java/org/onlab/onos/net/device/DeviceStore.java
@@ -125,6 +125,7 @@
* Administratively removes the specified device from the store.
*
* @param deviceId device to be removed
+ * @return null if no such device, or was forwarded to remove master
*/
DeviceEvent removeDevice(DeviceId deviceId);
}
diff --git a/core/api/src/main/java/org/onlab/onos/net/flow/DefaultTrafficSelector.java b/core/api/src/main/java/org/onlab/onos/net/flow/DefaultTrafficSelector.java
index 960eb02..413473f 100644
--- a/core/api/src/main/java/org/onlab/onos/net/flow/DefaultTrafficSelector.java
+++ b/core/api/src/main/java/org/onlab/onos/net/flow/DefaultTrafficSelector.java
@@ -99,6 +99,7 @@
* Returns a new traffic selector builder primed to produce entities
* patterned after the supplied selector.
*
+ * @param selector base selector
* @return traffic selector builder
*/
public static TrafficSelector.Builder builder(TrafficSelector selector) {
@@ -188,7 +189,7 @@
}
@Override
- public Builder matchOpticalSignalType(Byte signalType) {
+ public Builder matchOpticalSignalType(Short signalType) {
return add(Criteria.matchOpticalSignalType(signalType));
}
diff --git a/core/api/src/main/java/org/onlab/onos/net/flow/DefaultTrafficTreatment.java b/core/api/src/main/java/org/onlab/onos/net/flow/DefaultTrafficTreatment.java
index c0b3986..a63ae13 100644
--- a/core/api/src/main/java/org/onlab/onos/net/flow/DefaultTrafficTreatment.java
+++ b/core/api/src/main/java/org/onlab/onos/net/flow/DefaultTrafficTreatment.java
@@ -63,6 +63,7 @@
* Returns a new traffic treatment builder primed to produce entities
* patterned after the supplied treatment.
*
+ * @param treatment base treatment
* @return traffic treatment builder
*/
public static TrafficTreatment.Builder builder(TrafficTreatment treatment) {
diff --git a/core/api/src/main/java/org/onlab/onos/net/flow/FlowRuleBatchEvent.java b/core/api/src/main/java/org/onlab/onos/net/flow/FlowRuleBatchEvent.java
index d5c762d..1dbf8bd 100644
--- a/core/api/src/main/java/org/onlab/onos/net/flow/FlowRuleBatchEvent.java
+++ b/core/api/src/main/java/org/onlab/onos/net/flow/FlowRuleBatchEvent.java
@@ -27,11 +27,14 @@
*/
public enum Type {
+ // Request has been forwarded to MASTER Node
/**
* Signifies that a batch operation has been initiated.
*/
BATCH_OPERATION_REQUESTED,
+ // MASTER Node has pushed the batch down to the Device
+ // (e.g., Received barrier reply)
/**
* Signifies that a batch operation has completed.
*/
diff --git a/core/api/src/main/java/org/onlab/onos/net/flow/FlowRuleBatchRequest.java b/core/api/src/main/java/org/onlab/onos/net/flow/FlowRuleBatchRequest.java
index 4a2bcf9..f75c663 100644
--- a/core/api/src/main/java/org/onlab/onos/net/flow/FlowRuleBatchRequest.java
+++ b/core/api/src/main/java/org/onlab/onos/net/flow/FlowRuleBatchRequest.java
@@ -25,29 +25,29 @@
public class FlowRuleBatchRequest {
private final int batchId;
- private final List<FlowEntry> toAdd;
- private final List<FlowEntry> toRemove;
+ private final List<FlowRule> toAdd;
+ private final List<FlowRule> toRemove;
- public FlowRuleBatchRequest(int batchId, List<? extends FlowEntry> toAdd, List<? extends FlowEntry> toRemove) {
+ public FlowRuleBatchRequest(int batchId, List<? extends FlowRule> toAdd, List<? extends FlowRule> toRemove) {
this.batchId = batchId;
this.toAdd = Collections.unmodifiableList(toAdd);
this.toRemove = Collections.unmodifiableList(toRemove);
}
- public List<FlowEntry> toAdd() {
+ public List<FlowRule> toAdd() {
return toAdd;
}
- public List<FlowEntry> toRemove() {
+ public List<FlowRule> toRemove() {
return toRemove;
}
public FlowRuleBatchOperation asBatchOperation() {
List<FlowRuleBatchEntry> entries = Lists.newArrayList();
- for (FlowEntry e : toAdd) {
+ for (FlowRule e : toAdd) {
entries.add(new FlowRuleBatchEntry(FlowRuleOperation.ADD, e));
}
- for (FlowEntry e : toRemove) {
+ for (FlowRule e : toRemove) {
entries.add(new FlowRuleBatchEntry(FlowRuleOperation.REMOVE, e));
}
return new FlowRuleBatchOperation(entries);
diff --git a/core/api/src/main/java/org/onlab/onos/net/flow/FlowRuleProvider.java b/core/api/src/main/java/org/onlab/onos/net/flow/FlowRuleProvider.java
index 05ecd66..de2c7fd 100644
--- a/core/api/src/main/java/org/onlab/onos/net/flow/FlowRuleProvider.java
+++ b/core/api/src/main/java/org/onlab/onos/net/flow/FlowRuleProvider.java
@@ -48,6 +48,7 @@
/**
* Removes rules by their id.
* @param id the id to remove
+ * @param flowRules one or more flow rules
*/
void removeRulesById(ApplicationId id, FlowRule... flowRules);
diff --git a/core/api/src/main/java/org/onlab/onos/net/flow/FlowRuleProviderService.java b/core/api/src/main/java/org/onlab/onos/net/flow/FlowRuleProviderService.java
index 2f2dd5e..4c0d98d 100644
--- a/core/api/src/main/java/org/onlab/onos/net/flow/FlowRuleProviderService.java
+++ b/core/api/src/main/java/org/onlab/onos/net/flow/FlowRuleProviderService.java
@@ -35,6 +35,7 @@
* Pushes the collection of flow entries currently applied on the given
* device.
*
+ * @param deviceId device identifier
* @param flowEntries collection of flow rules
*/
void pushFlowMetrics(DeviceId deviceId, Iterable<FlowEntry> flowEntries);
diff --git a/core/api/src/main/java/org/onlab/onos/net/flow/FlowRuleService.java b/core/api/src/main/java/org/onlab/onos/net/flow/FlowRuleService.java
index 789b6bf..f40a0f1 100644
--- a/core/api/src/main/java/org/onlab/onos/net/flow/FlowRuleService.java
+++ b/core/api/src/main/java/org/onlab/onos/net/flow/FlowRuleService.java
@@ -86,6 +86,7 @@
/**
* Applies a batch operation of FlowRules.
*
+ * @param batch batch operation to apply
* @return future indicating the state of the batch operation
*/
Future<CompletedBatchOperation> applyBatch(FlowRuleBatchOperation batch);
diff --git a/core/api/src/main/java/org/onlab/onos/net/flow/TrafficSelector.java b/core/api/src/main/java/org/onlab/onos/net/flow/TrafficSelector.java
index aaf0ece..e0c8c78 100644
--- a/core/api/src/main/java/org/onlab/onos/net/flow/TrafficSelector.java
+++ b/core/api/src/main/java/org/onlab/onos/net/flow/TrafficSelector.java
@@ -137,17 +137,17 @@
/**
* Matches an optical signal ID or lambda.
- * @param lambda
+ * @param lambda lamda
* @return a selection builder
*/
public Builder matchLambda(Short lambda);
/**
* Matches an optical Signal Type.
- * @param signalType
+ * @param signalType signalType
* @return a selection builder
*/
- public Builder matchOpticalSignalType(Byte signalType);
+ public Builder matchOpticalSignalType(Short signalType);
/**
* Builds an immutable traffic selector.
diff --git a/core/api/src/main/java/org/onlab/onos/net/flow/criteria/Criteria.java b/core/api/src/main/java/org/onlab/onos/net/flow/criteria/Criteria.java
index aba5680..bac1bab 100644
--- a/core/api/src/main/java/org/onlab/onos/net/flow/criteria/Criteria.java
+++ b/core/api/src/main/java/org/onlab/onos/net/flow/criteria/Criteria.java
@@ -131,7 +131,7 @@
/**
* Creates a match on TCP source port field using the specified value.
*
- * @param tcpPort
+ * @param tcpPort TCP source port
* @return match criterion
*/
public static Criterion matchTcpSrc(Short tcpPort) {
@@ -141,7 +141,7 @@
/**
* Creates a match on TCP destination port field using the specified value.
*
- * @param tcpPort
+ * @param tcpPort TCP destination port
* @return match criterion
*/
public static Criterion matchTcpDst(Short tcpPort) {
@@ -151,7 +151,7 @@
/**
* Creates a match on lambda field using the specified value.
*
- * @param lambda
+ * @param lambda lamda to match on
* @return match criterion
*/
public static Criterion matchLambda(Short lambda) {
@@ -161,11 +161,11 @@
/**
* Creates a match on lambda field using the specified value.
*
- * @param lambda
+ * @param sigType signame type
* @return match criterion
*/
- public static Criterion matchOpticalSignalType(Byte lambda) {
- return new OpticalSignalTypeCriterion(lambda, Type.OCH_SIGTYPE);
+ public static Criterion matchOpticalSignalType(Short sigType) {
+ return new OpticalSignalTypeCriterion(sigType, Type.OCH_SIGTYPE);
}
@@ -587,10 +587,10 @@
public static final class OpticalSignalTypeCriterion implements Criterion {
- private final byte signalType;
+ private final Short signalType;
private final Type type;
- public OpticalSignalTypeCriterion(byte signalType, Type type) {
+ public OpticalSignalTypeCriterion(Short signalType, Type type) {
this.signalType = signalType;
this.type = type;
}
@@ -600,7 +600,7 @@
return this.type;
}
- public Byte signalType() {
+ public Short signalType() {
return this.signalType;
}
diff --git a/core/api/src/main/java/org/onlab/onos/net/host/HostAdminService.java b/core/api/src/main/java/org/onlab/onos/net/host/HostAdminService.java
index f421fd8..c51a847 100644
--- a/core/api/src/main/java/org/onlab/onos/net/host/HostAdminService.java
+++ b/core/api/src/main/java/org/onlab/onos/net/host/HostAdminService.java
@@ -34,11 +34,7 @@
* Binds IP and MAC addresses to the given connection point.
* <p>
* The addresses are added to the set of addresses already bound to the
- * connection point. If any of the fields in addresses is null, no change
- * is made to the corresponding addresses in the store.
- * {@link #unbindAddressesFromPort(PortAddresses)} must be use to unbind
- * addresses that have previously been bound.
- * </p>
+ * connection point.
*
* @param addresses address object containing addresses to add and the port
* to add them to
diff --git a/core/api/src/main/java/org/onlab/onos/net/host/HostService.java b/core/api/src/main/java/org/onlab/onos/net/host/HostService.java
index aa31459..7f7be50 100644
--- a/core/api/src/main/java/org/onlab/onos/net/host/HostService.java
+++ b/core/api/src/main/java/org/onlab/onos/net/host/HostService.java
@@ -135,7 +135,7 @@
* @param connectPoint the connection point to retrieve address bindings for
* @return addresses bound to the port
*/
- PortAddresses getAddressBindingsForPort(ConnectPoint connectPoint);
+ Set<PortAddresses> getAddressBindingsForPort(ConnectPoint connectPoint);
/**
* Adds the specified host listener.
diff --git a/core/api/src/main/java/org/onlab/onos/net/host/HostStore.java b/core/api/src/main/java/org/onlab/onos/net/host/HostStore.java
index a6bf96d..0316dcf 100644
--- a/core/api/src/main/java/org/onlab/onos/net/host/HostStore.java
+++ b/core/api/src/main/java/org/onlab/onos/net/host/HostStore.java
@@ -15,6 +15,8 @@
*/
package org.onlab.onos.net.host;
+import java.util.Set;
+
import org.onlab.onos.net.ConnectPoint;
import org.onlab.onos.net.DeviceId;
import org.onlab.onos.net.Host;
@@ -25,8 +27,6 @@
import org.onlab.packet.MacAddress;
import org.onlab.packet.VlanId;
-import java.util.Set;
-
/**
* Manages inventory of end-station hosts; not intended for direct use.
*/
@@ -153,5 +153,5 @@
* for
* @return address information for the connection point
*/
- PortAddresses getAddressBindingsForPort(ConnectPoint connectPoint);
+ Set<PortAddresses> getAddressBindingsForPort(ConnectPoint connectPoint);
}
diff --git a/core/api/src/main/java/org/onlab/onos/net/intent/ConnectivityIntent.java b/core/api/src/main/java/org/onlab/onos/net/intent/ConnectivityIntent.java
index 65d48d5..2269aa0 100644
--- a/core/api/src/main/java/org/onlab/onos/net/intent/ConnectivityIntent.java
+++ b/core/api/src/main/java/org/onlab/onos/net/intent/ConnectivityIntent.java
@@ -23,6 +23,7 @@
import org.onlab.onos.net.flow.TrafficTreatment;
import java.util.Collection;
+import java.util.List;
import static com.google.common.base.Preconditions.checkNotNull;
@@ -40,10 +41,14 @@
private final TrafficSelector selector;
private final TrafficTreatment treatment;
+ private final List<Constraint> constraints;
/**
* Creates a connectivity intent that matches on the specified selector
* and applies the specified treatment.
+ * <p>
+ * Path will be chosen without any constraints.
+ * </p>
*
* @param id intent identifier
* @param appId application identifier
@@ -56,9 +61,33 @@
Collection<NetworkResource> resources,
TrafficSelector selector,
TrafficTreatment treatment) {
+ this(id, appId, resources, selector, treatment, null);
+ }
+
+ /**
+ * Creates a connectivity intent that matches on the specified selector
+ * and applies the specified treatment.
+ * <p>
+ * Path will be optimized based on the first constraint if one is given.
+ * </p>
+ *
+ * @param id intent identifier
+ * @param appId application identifier
+ * @param resources required network resources (optional)
+ * @param selector traffic selector
+ * @param treatment treatment
+ * @param constraints optional prioritized list of constraints
+ * @throws NullPointerException if the selector or treatement is null
+ */
+ protected ConnectivityIntent(IntentId id, ApplicationId appId,
+ Collection<NetworkResource> resources,
+ TrafficSelector selector,
+ TrafficTreatment treatment,
+ List<Constraint> constraints) {
super(id, appId, resources);
this.selector = checkNotNull(selector);
this.treatment = checkNotNull(treatment);
+ this.constraints = constraints;
}
/**
@@ -68,6 +97,7 @@
super();
this.selector = null;
this.treatment = null;
+ this.constraints = null;
}
/**
@@ -89,13 +119,22 @@
}
/**
+ * Returns the set of connectivity constraints.
+ *
+ * @return list of intent constraints
+ */
+ public List<Constraint> constraints() {
+ return constraints;
+ }
+
+ /**
* Produces a collection of network resources from the given links.
*
* @param links collection of links
* @return collection of link resources
*/
protected static Collection<NetworkResource> resources(Collection<Link> links) {
- return ImmutableSet.<NetworkResource>copyOf(links);
+ return ImmutableSet.copyOf(links);
}
}
diff --git a/core/api/src/main/java/org/onlab/onos/net/intent/Constraint.java b/core/api/src/main/java/org/onlab/onos/net/intent/Constraint.java
new file mode 100644
index 0000000..9004aa6
--- /dev/null
+++ b/core/api/src/main/java/org/onlab/onos/net/intent/Constraint.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onlab.onos.net.intent;
+
+import org.onlab.onos.net.Link;
+import org.onlab.onos.net.Path;
+import org.onlab.onos.net.resource.LinkResourceService;
+
+/**
+ * Representation of a connectivity constraint capable of evaluating a link
+ * and determining the cost of traversing that link in the context of this
+ * constraint.
+ */
+public interface Constraint {
+
+ // TODO: Consider separating cost vs viability.
+
+ /**
+ * Evaluates the specified link and provides the cost for its traversal.
+ *
+ * @param link link to be evaluated
+ * @param resourceService resource service for validating availability of
+ * link resources
+ * @return cost of link traversal
+ */
+ double cost(Link link, LinkResourceService resourceService);
+
+ /**
+ * Validates that the specified path satisfies the constraint.
+ *
+ * @param path path to be validated
+ * @param resourceService resource service for validating availability of
+ * link resources
+ * @return cost of link traversal
+ */
+ boolean validate(Path path, LinkResourceService resourceService);
+
+}
diff --git a/core/api/src/main/java/org/onlab/onos/net/intent/HostToHostIntent.java b/core/api/src/main/java/org/onlab/onos/net/intent/HostToHostIntent.java
index 376a122..3fad93d 100644
--- a/core/api/src/main/java/org/onlab/onos/net/intent/HostToHostIntent.java
+++ b/core/api/src/main/java/org/onlab/onos/net/intent/HostToHostIntent.java
@@ -21,6 +21,8 @@
import org.onlab.onos.net.flow.TrafficSelector;
import org.onlab.onos.net.flow.TrafficTreatment;
+import java.util.List;
+
import static com.google.common.base.Preconditions.checkNotNull;
/**
@@ -44,11 +46,30 @@
public HostToHostIntent(ApplicationId appId, HostId one, HostId two,
TrafficSelector selector,
TrafficTreatment treatment) {
+ this(appId, one, two, selector, treatment, null);
+ }
+
+ /**
+ * Creates a new host-to-host intent with the supplied host pair.
+ *
+ * @param appId application identifier
+ * @param one first host
+ * @param two second host
+ * @param selector action
+ * @param treatment ingress port
+ * @param constraints optional prioritized list of path selection constraints
+ * @throws NullPointerException if {@code one} or {@code two} is null.
+ */
+ public HostToHostIntent(ApplicationId appId, HostId one, HostId two,
+ TrafficSelector selector,
+ TrafficTreatment treatment,
+ List<Constraint> constraints) {
super(id(HostToHostIntent.class, min(one, two), max(one, two),
- selector, treatment),
- appId, null, selector, treatment);
+ selector, treatment, constraints),
+ appId, null, selector, treatment, constraints);
this.one = checkNotNull(one);
this.two = checkNotNull(two);
+
}
private static HostId min(HostId one, HostId two) {
diff --git a/core/api/src/main/java/org/onlab/onos/net/intent/Intent.java b/core/api/src/main/java/org/onlab/onos/net/intent/Intent.java
index 885b851..657a3e4 100644
--- a/core/api/src/main/java/org/onlab/onos/net/intent/Intent.java
+++ b/core/api/src/main/java/org/onlab/onos/net/intent/Intent.java
@@ -91,6 +91,7 @@
* Produces an intent identifier backed by hash-like fingerprint for the
* specified class of intent and its constituent fields.
*
+ * @param intentClass Class of the intent
* @param fields intent fields
* @return intent identifier
*/
diff --git a/core/api/src/main/java/org/onlab/onos/net/intent/IntentId.java b/core/api/src/main/java/org/onlab/onos/net/intent/IntentId.java
index 8c75397..c4c2752 100644
--- a/core/api/src/main/java/org/onlab/onos/net/intent/IntentId.java
+++ b/core/api/src/main/java/org/onlab/onos/net/intent/IntentId.java
@@ -51,6 +51,15 @@
this.fingerprint = fingerprint;
}
+ /**
+ * Returns the backing fingerprint.
+ *
+ * @return the fingerprint
+ */
+ public long fingerprint() {
+ return fingerprint;
+ }
+
@Override
public int hashCode() {
return (int) (fingerprint ^ (fingerprint >>> 32));
diff --git a/core/api/src/main/java/org/onlab/onos/net/intent/IntentInstaller.java b/core/api/src/main/java/org/onlab/onos/net/intent/IntentInstaller.java
index 7566210..93d8a40 100644
--- a/core/api/src/main/java/org/onlab/onos/net/intent/IntentInstaller.java
+++ b/core/api/src/main/java/org/onlab/onos/net/intent/IntentInstaller.java
@@ -27,6 +27,7 @@
* Installs the specified intent to the environment.
*
* @param intent intent to be installed
+ * @return FlowRule operations to install
* @throws IntentException if issues are encountered while installing the intent
*/
List<FlowRuleBatchOperation> install(T intent);
@@ -35,6 +36,7 @@
* Uninstalls the specified intent from the environment.
*
* @param intent intent to be uninstalled
+ * @return FlowRule operations to uninstall
* @throws IntentException if issues are encountered while uninstalling the intent
*/
List<FlowRuleBatchOperation> uninstall(T intent);
diff --git a/core/api/src/main/java/org/onlab/onos/net/intent/IntentService.java b/core/api/src/main/java/org/onlab/onos/net/intent/IntentService.java
index 9cffb11..090af63 100644
--- a/core/api/src/main/java/org/onlab/onos/net/intent/IntentService.java
+++ b/core/api/src/main/java/org/onlab/onos/net/intent/IntentService.java
@@ -59,6 +59,7 @@
* affected at later time.
* </p>
* @param operations batch of intent operations
+ * @return Future to get execution result
*/
Future<IntentOperations> execute(IntentOperations operations);
diff --git a/core/api/src/main/java/org/onlab/onos/net/intent/PathIntent.java b/core/api/src/main/java/org/onlab/onos/net/intent/PathIntent.java
index 0789db0..9189bae 100644
--- a/core/api/src/main/java/org/onlab/onos/net/intent/PathIntent.java
+++ b/core/api/src/main/java/org/onlab/onos/net/intent/PathIntent.java
@@ -20,7 +20,6 @@
import org.onlab.onos.net.Path;
import org.onlab.onos.net.flow.TrafficSelector;
import org.onlab.onos.net.flow.TrafficTreatment;
-import org.onlab.onos.net.resource.LinkResourceRequest;
/**
* Abstraction of explicitly path specified connectivity intent.
@@ -28,7 +27,6 @@
public class PathIntent extends ConnectivityIntent {
private final Path path;
- private final LinkResourceRequest[] resourceRequests;
/**
* Creates a new point-to-point intent with the supplied ingress/egress
@@ -41,11 +39,10 @@
* @throws NullPointerException {@code path} is null
*/
public PathIntent(ApplicationId appId, TrafficSelector selector,
- TrafficTreatment treatment, Path path, LinkResourceRequest[] resourceRequests) {
+ TrafficTreatment treatment, Path path) {
super(id(PathIntent.class, selector, treatment, path), appId,
resources(path.links()), selector, treatment);
this.path = path;
- this.resourceRequests = resourceRequests;
}
/**
@@ -54,7 +51,6 @@
protected PathIntent() {
super();
this.path = null;
- this.resourceRequests = new LinkResourceRequest[0];
}
/**
@@ -71,9 +67,6 @@
return true;
}
- public LinkResourceRequest[] resourceRequests() {
- return resourceRequests;
- }
@Override
public String toString() {
diff --git a/core/api/src/main/java/org/onlab/onos/net/intent/PointToPointIntent.java b/core/api/src/main/java/org/onlab/onos/net/intent/PointToPointIntent.java
index 61a1a56..e480ee2 100644
--- a/core/api/src/main/java/org/onlab/onos/net/intent/PointToPointIntent.java
+++ b/core/api/src/main/java/org/onlab/onos/net/intent/PointToPointIntent.java
@@ -16,10 +16,15 @@
package org.onlab.onos.net.intent;
import com.google.common.base.MoreObjects;
+import com.google.common.collect.ImmutableList;
import org.onlab.onos.core.ApplicationId;
import org.onlab.onos.net.ConnectPoint;
+import org.onlab.onos.net.Link;
import org.onlab.onos.net.flow.TrafficSelector;
import org.onlab.onos.net.flow.TrafficTreatment;
+import org.onlab.onos.net.intent.constraint.LinkTypeConstraint;
+
+import java.util.List;
import static com.google.common.base.Preconditions.checkNotNull;
@@ -33,7 +38,7 @@
/**
* Creates a new point-to-point intent with the supplied ingress/egress
- * ports.
+ * ports and with built-in link type constraint to avoid optical links.
*
* @param appId application identifier
* @param selector traffic selector
@@ -46,8 +51,30 @@
TrafficTreatment treatment,
ConnectPoint ingressPoint,
ConnectPoint egressPoint) {
- super(id(PointToPointIntent.class, selector, treatment, ingressPoint, egressPoint),
- appId, null, selector, treatment);
+ this(appId, selector, treatment, ingressPoint, egressPoint,
+ ImmutableList.of(new LinkTypeConstraint(false, Link.Type.OPTICAL)));
+ }
+
+ /**
+ * Creates a new point-to-point intent with the supplied ingress/egress
+ * ports and constraints.
+ *
+ * @param appId application identifier
+ * @param selector traffic selector
+ * @param treatment treatment
+ * @param ingressPoint ingress port
+ * @param egressPoint egress port
+ * @param constraints optional list of constraints
+ * @throws NullPointerException if {@code ingressPoint} or {@code egressPoints} is null.
+ */
+ public PointToPointIntent(ApplicationId appId, TrafficSelector selector,
+ TrafficTreatment treatment,
+ ConnectPoint ingressPoint,
+ ConnectPoint egressPoint,
+ List<Constraint> constraints) {
+ super(id(PointToPointIntent.class, selector, treatment,
+ ingressPoint, egressPoint, constraints),
+ appId, null, selector, treatment, constraints);
this.ingressPoint = checkNotNull(ingressPoint);
this.egressPoint = checkNotNull(egressPoint);
}
@@ -89,6 +116,7 @@
.add("treatment", treatment())
.add("ingress", ingressPoint)
.add("egress", egressPoint)
+ .add("constraints", constraints())
.toString();
}
diff --git a/core/api/src/main/java/org/onlab/onos/net/intent/PointToPointIntentWithBandwidthConstraint.java b/core/api/src/main/java/org/onlab/onos/net/intent/PointToPointIntentWithBandwidthConstraint.java
deleted file mode 100644
index d5c4d57..0000000
--- a/core/api/src/main/java/org/onlab/onos/net/intent/PointToPointIntentWithBandwidthConstraint.java
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.onlab.onos.net.intent;
-
-import org.onlab.onos.core.ApplicationId;
-import org.onlab.onos.net.ConnectPoint;
-import org.onlab.onos.net.flow.TrafficSelector;
-import org.onlab.onos.net.flow.TrafficTreatment;
-import org.onlab.onos.net.resource.BandwidthResourceRequest;
-
-import com.google.common.base.MoreObjects;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-
-/**
- * Abstraction of point-to-point connectivity.
- */
-public class PointToPointIntentWithBandwidthConstraint extends ConnectivityIntent {
-
- private final ConnectPoint ingressPoint;
- private final ConnectPoint egressPoint;
- private final BandwidthResourceRequest bandwidthResourceRequest;
-
- /**
- * Creates a new point-to-point intent with the supplied ingress/egress
- * ports.
- *
- * @param appId application identifier
- * @param selector traffic selector
- * @param treatment treatment
- * @param ingressPoint ingress port
- * @param egressPoint egress port
- * @throws NullPointerException if {@code ingressPoint} or {@code egressPoints} is null.
- */
- public PointToPointIntentWithBandwidthConstraint(ApplicationId appId, TrafficSelector selector,
- TrafficTreatment treatment,
- ConnectPoint ingressPoint,
- ConnectPoint egressPoint,
- BandwidthResourceRequest bandwidthResourceRequest) {
- super(id(PointToPointIntentWithBandwidthConstraint.class, selector,
- treatment, ingressPoint, egressPoint, bandwidthResourceRequest.bandwidth()),
- appId, null, selector, treatment);
- this.ingressPoint = checkNotNull(ingressPoint);
- this.egressPoint = checkNotNull(egressPoint);
- this.bandwidthResourceRequest = bandwidthResourceRequest;
- }
-
- /**
- * Constructor for serializer.
- */
- protected PointToPointIntentWithBandwidthConstraint() {
- super();
- this.ingressPoint = null;
- this.egressPoint = null;
- bandwidthResourceRequest = new BandwidthResourceRequest(0.0);
- }
-
- /**
- * Returns the port on which the ingress traffic should be connected to
- * the egress.
- *
- * @return ingress port
- */
- public ConnectPoint ingressPoint() {
- return ingressPoint;
- }
-
- /**
- * Returns the port on which the traffic should egress.
- *
- * @return egress port
- */
- public ConnectPoint egressPoint() {
- return egressPoint;
- }
-
- public BandwidthResourceRequest bandwidthRequest() {
- return this.bandwidthResourceRequest;
- }
-
- @Override
- public String toString() {
- return MoreObjects.toStringHelper(getClass())
- .add("id", id())
- .add("appId", appId())
- .add("selector", selector())
- .add("treatment", treatment())
- .add("ingress", ingressPoint)
- .add("egress", egressPoint)
- .add("bandwidth", bandwidthResourceRequest.bandwidth().toString())
- .toString();
- }
-
-}
diff --git a/core/api/src/main/java/org/onlab/onos/net/intent/constraint/BandwidthConstraint.java b/core/api/src/main/java/org/onlab/onos/net/intent/constraint/BandwidthConstraint.java
new file mode 100644
index 0000000..fc08214
--- /dev/null
+++ b/core/api/src/main/java/org/onlab/onos/net/intent/constraint/BandwidthConstraint.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onlab.onos.net.intent.constraint;
+
+import org.onlab.onos.net.Link;
+import org.onlab.onos.net.resource.Bandwidth;
+import org.onlab.onos.net.resource.BandwidthResourceRequest;
+import org.onlab.onos.net.resource.LinkResourceService;
+import org.onlab.onos.net.resource.ResourceRequest;
+import org.onlab.onos.net.resource.ResourceType;
+
+import java.util.Objects;
+
+import static com.google.common.base.MoreObjects.toStringHelper;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Constraint that evaluates links based on available bandwidths.
+ */
+public class BandwidthConstraint extends BooleanConstraint {
+
+ private final Bandwidth bandwidth;
+
+ /**
+ * Creates a new bandwidth constraint.
+ *
+ * @param bandwidth required bandwidth
+ */
+ public BandwidthConstraint(Bandwidth bandwidth) {
+ this.bandwidth = checkNotNull(bandwidth, "Bandwidth cannot be null");
+ }
+
+ // Constructor for serialization
+ private BandwidthConstraint() {
+ this.bandwidth = null;
+ }
+
+ @Override
+ public boolean isValid(Link link, LinkResourceService resourceService) {
+ for (ResourceRequest request : resourceService.getAvailableResources(link)) {
+ if (request.type() == ResourceType.BANDWIDTH) {
+ BandwidthResourceRequest brr = (BandwidthResourceRequest) request;
+ if (brr.bandwidth().toDouble() >= bandwidth.toDouble()) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Returns the bandwidth required by this constraint.
+ *
+ * @return required bandwidth
+ */
+ public Bandwidth bandwidth() {
+ return bandwidth;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(bandwidth);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null || getClass() != obj.getClass()) {
+ return false;
+ }
+ final BandwidthConstraint other = (BandwidthConstraint) obj;
+ return Objects.equals(this.bandwidth, other.bandwidth);
+ }
+
+ @Override
+ public String toString() {
+ return toStringHelper(this).add("bandwidth", bandwidth).toString();
+ }
+}
diff --git a/core/api/src/main/java/org/onlab/onos/net/intent/constraint/BooleanConstraint.java b/core/api/src/main/java/org/onlab/onos/net/intent/constraint/BooleanConstraint.java
new file mode 100644
index 0000000..97c5695
--- /dev/null
+++ b/core/api/src/main/java/org/onlab/onos/net/intent/constraint/BooleanConstraint.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onlab.onos.net.intent.constraint;
+
+import org.onlab.onos.net.Link;
+import org.onlab.onos.net.Path;
+import org.onlab.onos.net.intent.Constraint;
+import org.onlab.onos.net.resource.LinkResourceService;
+
+/**
+ * Abstract base class for various constraints that evaluate link viability
+ * in a yes/no fashion.
+ */
+public abstract class BooleanConstraint implements Constraint {
+
+ /**
+ * Returns true if the specified link satisfies the constraint.
+ *
+ * @param link link to be validated
+ * @param resourceService resource service for checking available link resources
+ * @return true if link is viable
+ */
+ public abstract boolean isValid(Link link, LinkResourceService resourceService);
+
+ /**
+ * {@inheritDoc}
+ *
+ * Negative return value means the specified link does not satisfy this constraint.
+ *
+ * @param link {@inheritDoc}
+ * @param resourceService {@inheritDoc}
+ * @return {@inheritDoc}
+ */
+ @Override
+ public double cost(Link link, LinkResourceService resourceService) {
+ return isValid(link, resourceService) ? +1 : -1;
+ }
+
+ @Override
+ public boolean validate(Path path, LinkResourceService resourceService) {
+ for (Link link : path.links()) {
+ if (isValid(link, resourceService)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+}
diff --git a/core/api/src/main/java/org/onlab/onos/net/intent/constraint/LambdaConstraint.java b/core/api/src/main/java/org/onlab/onos/net/intent/constraint/LambdaConstraint.java
new file mode 100644
index 0000000..ab88c92
--- /dev/null
+++ b/core/api/src/main/java/org/onlab/onos/net/intent/constraint/LambdaConstraint.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onlab.onos.net.intent.constraint;
+
+import org.onlab.onos.net.Link;
+import org.onlab.onos.net.resource.Lambda;
+import org.onlab.onos.net.resource.LinkResourceService;
+import org.onlab.onos.net.resource.ResourceRequest;
+import org.onlab.onos.net.resource.ResourceType;
+
+import java.util.Objects;
+
+import static com.google.common.base.MoreObjects.toStringHelper;
+
+/**
+ * Constraint that evaluates links based on available lambda.
+ */
+public class LambdaConstraint extends BooleanConstraint {
+
+ private final Lambda lambda;
+
+ /**
+ * Creates a new optical lambda constraint.
+ *
+ * @param lambda optional lambda to indicate a specific lambda
+ */
+ public LambdaConstraint(Lambda lambda) {
+ this.lambda = lambda;
+ }
+
+ // Constructor for serialization
+ private LambdaConstraint() {
+ this.lambda = null;
+ }
+
+ @Override
+ public boolean isValid(Link link, LinkResourceService resourceService) {
+ for (ResourceRequest request : resourceService.getAvailableResources(link)) {
+ if (request.type() == ResourceType.LAMBDA) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Returns the lambda required by this constraint.
+ *
+ * @return required lambda
+ */
+ public Lambda lambda() {
+ return lambda;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(lambda);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null || getClass() != obj.getClass()) {
+ return false;
+ }
+ final LambdaConstraint other = (LambdaConstraint) obj;
+ return Objects.equals(this.lambda, other.lambda);
+ }
+
+ @Override
+ public String toString() {
+ return toStringHelper(this).add("lambda", lambda).toString();
+ }
+}
diff --git a/core/api/src/main/java/org/onlab/onos/net/intent/constraint/LinkTypeConstraint.java b/core/api/src/main/java/org/onlab/onos/net/intent/constraint/LinkTypeConstraint.java
new file mode 100644
index 0000000..7d44b4d
--- /dev/null
+++ b/core/api/src/main/java/org/onlab/onos/net/intent/constraint/LinkTypeConstraint.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onlab.onos.net.intent.constraint;
+
+import com.google.common.collect.ImmutableSet;
+import org.onlab.onos.net.Link;
+import org.onlab.onos.net.resource.LinkResourceService;
+
+import java.util.Objects;
+import java.util.Set;
+
+import static com.google.common.base.MoreObjects.toStringHelper;
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Constraint that evaluates links based on their type.
+ */
+public class LinkTypeConstraint extends BooleanConstraint {
+
+ private final Set<Link.Type> types;
+ private final boolean isInclusive;
+
+ /**
+ * Creates a new constraint for requesting connectivity using or avoiding
+ * the specified link types.
+ *
+ * @param inclusive indicates whether the given link types are to be
+ * permitted or avoided
+ * @param types link types
+ */
+ public LinkTypeConstraint(boolean inclusive, Link.Type... types) {
+ checkNotNull(types, "Link types cannot be null");
+ checkArgument(types.length > 0, "There must be more than one type");
+ this.types = ImmutableSet.copyOf(types);
+ this.isInclusive = inclusive;
+ }
+
+ // Constructor for serialization
+ private LinkTypeConstraint() {
+ this.types = null;
+ this.isInclusive = false;
+ }
+
+ @Override
+ public boolean isValid(Link link, LinkResourceService resourceService) {
+ boolean contains = types.contains(link.type());
+ return isInclusive ? contains : !contains;
+ }
+
+ /**
+ * Returns the set of link types.
+ *
+ * @return set of link types
+ */
+ public Set<Link.Type> types() {
+ return types;
+ }
+
+ /**
+ * Indicates if the constraint is inclusive or exclusive.
+ *
+ * @return true if inclusive
+ */
+ public boolean isInclusive() {
+ return isInclusive;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(types, isInclusive);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null || getClass() != obj.getClass()) {
+ return false;
+ }
+ final LinkTypeConstraint other = (LinkTypeConstraint) obj;
+ return Objects.equals(this.types, other.types) && Objects.equals(this.isInclusive, other.isInclusive);
+ }
+
+ @Override
+ public String toString() {
+ return toStringHelper(this)
+ .add("inclusive", isInclusive)
+ .add("types", types)
+ .toString();
+ }
+}
diff --git a/web/gui/src/main/webapp/module-template.js b/core/api/src/main/java/org/onlab/onos/net/intent/constraint/package-info.java
similarity index 67%
copy from web/gui/src/main/webapp/module-template.js
copy to core/api/src/main/java/org/onlab/onos/net/intent/constraint/package-info.java
index 3de7d79..762d6d1 100644
--- a/web/gui/src/main/webapp/module-template.js
+++ b/core/api/src/main/java/org/onlab/onos/net/intent/constraint/package-info.java
@@ -14,23 +14,7 @@
* limitations under the License.
*/
-/*
- Module template file.
-
- @author Simon Hunt
+/**
+ * Definitions of constraints used to refine intent specifications.
*/
-
-(function (onos) {
- 'use strict';
-
- var api = onos.api;
-
- // == define your functions here.....
-
-
- // == register views here, with links to lifecycle callbacks
-
-// api.addView('view-id', {/* callbacks */});
-
-
-}(ONOS));
+package org.onlab.onos.net.intent.constraint;
diff --git a/core/api/src/main/java/org/onlab/onos/net/resource/Bandwidth.java b/core/api/src/main/java/org/onlab/onos/net/resource/Bandwidth.java
index 9dddb86..fcb3cd8 100644
--- a/core/api/src/main/java/org/onlab/onos/net/resource/Bandwidth.java
+++ b/core/api/src/main/java/org/onlab/onos/net/resource/Bandwidth.java
@@ -33,6 +33,11 @@
this.bandwidth = bandwidth;
}
+ // Constructor for serialization
+ private Bandwidth() {
+ this.bandwidth = 0;
+ }
+
/**
* Creates a new instance with given bandwidth.
*
diff --git a/core/api/src/main/java/org/onlab/onos/net/resource/DefaultLinkResourceRequest.java b/core/api/src/main/java/org/onlab/onos/net/resource/DefaultLinkResourceRequest.java
index 73b48df6..c3f5c28 100644
--- a/core/api/src/main/java/org/onlab/onos/net/resource/DefaultLinkResourceRequest.java
+++ b/core/api/src/main/java/org/onlab/onos/net/resource/DefaultLinkResourceRequest.java
@@ -20,9 +20,12 @@
import java.util.Set;
import org.onlab.onos.net.Link;
+import org.onlab.onos.net.intent.Constraint;
import org.onlab.onos.net.intent.IntentId;
import com.google.common.collect.ImmutableSet;
+import org.onlab.onos.net.intent.constraint.BandwidthConstraint;
+import org.onlab.onos.net.intent.constraint.LambdaConstraint;
/**
* Implementation of {@link LinkResourceRequest}.
@@ -125,6 +128,18 @@
return this;
}
+ @Override
+ public LinkResourceRequest.Builder addConstraint(Constraint constraint) {
+ if (constraint instanceof LambdaConstraint) {
+ return addLambdaRequest();
+ } else if (constraint instanceof BandwidthConstraint) {
+ BandwidthConstraint bw = (BandwidthConstraint) constraint;
+ return addBandwidthRequest(bw.bandwidth().toDouble());
+ }
+ return this;
+ }
+
+
/**
* Returns link resource request.
*
diff --git a/core/api/src/main/java/org/onlab/onos/net/resource/Lambda.java b/core/api/src/main/java/org/onlab/onos/net/resource/Lambda.java
index e09d915..d179d64 100644
--- a/core/api/src/main/java/org/onlab/onos/net/resource/Lambda.java
+++ b/core/api/src/main/java/org/onlab/onos/net/resource/Lambda.java
@@ -33,6 +33,11 @@
this.lambda = lambda;
}
+ // Constructor for serialization
+ private Lambda() {
+ this.lambda = 0;
+ }
+
/**
* Creates a new instance with given lambda.
*
diff --git a/core/api/src/main/java/org/onlab/onos/net/resource/LinkResourceRequest.java b/core/api/src/main/java/org/onlab/onos/net/resource/LinkResourceRequest.java
index a511778..23a0c30 100644
--- a/core/api/src/main/java/org/onlab/onos/net/resource/LinkResourceRequest.java
+++ b/core/api/src/main/java/org/onlab/onos/net/resource/LinkResourceRequest.java
@@ -19,6 +19,7 @@
import java.util.Set;
import org.onlab.onos.net.Link;
+import org.onlab.onos.net.intent.Constraint;
import org.onlab.onos.net.intent.IntentId;
/**
@@ -67,6 +68,14 @@
public Builder addBandwidthRequest(double bandwidth);
/**
+ * Adds the resources required for a constraint.
+ *
+ * @param constraint the constraint
+ * @return self
+ */
+ public Builder addConstraint(Constraint constraint);
+
+ /**
* Returns link resource request.
*
* @return link resource request
diff --git a/core/api/src/main/java/org/onlab/onos/net/statistic/StatisticStore.java b/core/api/src/main/java/org/onlab/onos/net/statistic/StatisticStore.java
index 29eced7..96fcc11 100644
--- a/core/api/src/main/java/org/onlab/onos/net/statistic/StatisticStore.java
+++ b/core/api/src/main/java/org/onlab/onos/net/statistic/StatisticStore.java
@@ -34,7 +34,7 @@
/**
* Remove entries associated with this rule.
- a @param rule {@link org.onlab.onos.net.flow.FlowRule}
+ * @param rule {@link org.onlab.onos.net.flow.FlowRule}
*/
void removeFromStatistics(FlowRule rule);
diff --git a/core/api/src/main/java/org/onlab/onos/net/topology/PathService.java b/core/api/src/main/java/org/onlab/onos/net/topology/PathService.java
index c4623b6..1719285 100644
--- a/core/api/src/main/java/org/onlab/onos/net/topology/PathService.java
+++ b/core/api/src/main/java/org/onlab/onos/net/topology/PathService.java
@@ -43,6 +43,7 @@
*
* @param src source element
* @param dst destination element
+ * @param weight edge-weight entity
* @return set of all shortest paths between the two element
*/
Set<Path> getPaths(ElementId src, ElementId dst, LinkWeight weight);
diff --git a/core/api/src/main/java/org/onlab/onos/net/topology/TopologyService.java b/core/api/src/main/java/org/onlab/onos/net/topology/TopologyService.java
index 31a718b..5d8bbdf 100644
--- a/core/api/src/main/java/org/onlab/onos/net/topology/TopologyService.java
+++ b/core/api/src/main/java/org/onlab/onos/net/topology/TopologyService.java
@@ -103,6 +103,7 @@
* @param topology topology descriptor
* @param src source device
* @param dst destination device
+ * @param weight edge-weight entity
* @return set of all shortest paths between the two devices
*/
Set<Path> getPaths(Topology topology, DeviceId src, DeviceId dst,
diff --git a/core/api/src/main/java/org/onlab/onos/store/cluster/messaging/ClusterCommunicationService.java b/core/api/src/main/java/org/onlab/onos/store/cluster/messaging/ClusterCommunicationService.java
index cd2daff..283f92d 100644
--- a/core/api/src/main/java/org/onlab/onos/store/cluster/messaging/ClusterCommunicationService.java
+++ b/core/api/src/main/java/org/onlab/onos/store/cluster/messaging/ClusterCommunicationService.java
@@ -33,6 +33,7 @@
*
* @param message message to send
* @return true if the message was sent successfully to all nodes; false otherwise.
+ * @throws IOException when I/O exception of some sort has occurred
*/
boolean broadcast(ClusterMessage message) throws IOException;
@@ -42,6 +43,7 @@
* @param message message to send
* @param toNodeId node identifier
* @return true if the message was sent successfully; false otherwise.
+ * @throws IOException when I/O exception of some sort has occurred
*/
boolean unicast(ClusterMessage message, NodeId toNodeId) throws IOException;
@@ -49,7 +51,9 @@
* Multicast a message to a set of controller nodes.
*
* @param message message to send
+ * @param nodeIds recipient node identifiers
* @return true if the message was sent successfully to all nodes in the group; false otherwise.
+ * @throws IOException when I/O exception of some sort has occurred
*/
boolean multicast(ClusterMessage message, Set<NodeId> nodeIds) throws IOException;
@@ -58,7 +62,7 @@
* @param message message to send
* @param toNodeId recipient node identifier
* @return reply future.
- * @throws IOException
+ * @throws IOException when I/O exception of some sort has occurred
*/
ListenableFuture<byte[]> sendAndReceive(ClusterMessage message, NodeId toNodeId) throws IOException;
diff --git a/core/api/src/main/java/org/onlab/onos/store/cluster/messaging/ClusterMessage.java b/core/api/src/main/java/org/onlab/onos/store/cluster/messaging/ClusterMessage.java
index 5c8550f..c820704 100644
--- a/core/api/src/main/java/org/onlab/onos/store/cluster/messaging/ClusterMessage.java
+++ b/core/api/src/main/java/org/onlab/onos/store/cluster/messaging/ClusterMessage.java
@@ -32,7 +32,9 @@
/**
* Creates a cluster message.
*
+ * @param sender message sender
* @param subject message subject
+ * @param payload message payload
*/
public ClusterMessage(NodeId sender, MessageSubject subject, byte[] payload) {
this.sender = sender;
@@ -71,7 +73,7 @@
* Sends a response to the sender.
*
* @param data payload response.
- * @throws IOException
+ * @throws IOException when I/O exception of some sort has occurred
*/
public void respond(byte[] data) throws IOException {
throw new IllegalStateException("One can only repond to message recived from others.");
diff --git a/core/api/src/test/java/org/onlab/onos/event/DefaultEventSinkRegistryTest.java b/core/api/src/test/java/org/onlab/onos/event/DefaultEventSinkRegistryTest.java
index 0bb1e8c..cde9314 100644
--- a/core/api/src/test/java/org/onlab/onos/event/DefaultEventSinkRegistryTest.java
+++ b/core/api/src/test/java/org/onlab/onos/event/DefaultEventSinkRegistryTest.java
@@ -28,11 +28,15 @@
private DefaultEventSinkRegistry registry;
private static class FooEvent extends TestEvent {
- public FooEvent(String subject) { super(Type.FOO, subject); }
+ public FooEvent(String subject) {
+ super(Type.FOO, subject);
+ }
}
private static class BarEvent extends TestEvent {
- public BarEvent(String subject) { super(Type.BAR, subject); }
+ public BarEvent(String subject) {
+ super(Type.BAR, subject);
+ }
}
private static class FooSink implements EventSink<FooEvent> {
diff --git a/core/api/src/test/java/org/onlab/onos/net/NetTestTools.java b/core/api/src/test/java/org/onlab/onos/net/NetTestTools.java
index 9bc0450..8d2a61b 100644
--- a/core/api/src/test/java/org/onlab/onos/net/NetTestTools.java
+++ b/core/api/src/test/java/org/onlab/onos/net/NetTestTools.java
@@ -23,6 +23,7 @@
import java.util.HashSet;
import java.util.List;
+import static org.junit.Assert.assertEquals;
import static org.onlab.onos.net.DeviceId.deviceId;
import static org.onlab.onos.net.HostId.hostId;
import static org.onlab.onos.net.PortNumber.portNumber;
@@ -85,4 +86,23 @@
return new DefaultPath(PID, links, ids.length);
}
+
+ /**
+ * Verifies that Annotations created by merging {@code annotations} is
+ * equal to actual Annotations.
+ *
+ * @param actual Annotations to check
+ * @param annotations
+ */
+ public static void assertAnnotationsEquals(Annotations actual, SparseAnnotations... annotations) {
+ DefaultAnnotations expected = DefaultAnnotations.builder().build();
+ for (SparseAnnotations a : annotations) {
+ expected = DefaultAnnotations.merge(expected, a);
+ }
+ assertEquals(expected.keys(), actual.keys());
+ for (String key : expected.keys()) {
+ assertEquals(expected.value(key), actual.value(key));
+ }
+ }
+
}
diff --git a/core/api/src/test/java/org/onlab/onos/net/host/HostServiceAdapter.java b/core/api/src/test/java/org/onlab/onos/net/host/HostServiceAdapter.java
index a0a0e36..03a8a43 100644
--- a/core/api/src/test/java/org/onlab/onos/net/host/HostServiceAdapter.java
+++ b/core/api/src/test/java/org/onlab/onos/net/host/HostServiceAdapter.java
@@ -95,7 +95,7 @@
}
@Override
- public PortAddresses getAddressBindingsForPort(ConnectPoint connectPoint) {
+ public Set<PortAddresses> getAddressBindingsForPort(ConnectPoint connectPoint) {
return null;
}
diff --git a/core/api/src/test/java/org/onlab/onos/net/intent/PathIntentTest.java b/core/api/src/test/java/org/onlab/onos/net/intent/PathIntentTest.java
index 02ca0f5..5fce240 100644
--- a/core/api/src/test/java/org/onlab/onos/net/intent/PathIntentTest.java
+++ b/core/api/src/test/java/org/onlab/onos/net/intent/PathIntentTest.java
@@ -18,7 +18,6 @@
import org.junit.Test;
import org.onlab.onos.net.NetTestTools;
import org.onlab.onos.net.Path;
-import org.onlab.onos.net.resource.LinkResourceRequest;
import static org.junit.Assert.assertEquals;
@@ -40,11 +39,11 @@
@Override
protected PathIntent createOne() {
- return new PathIntent(APPID, MATCH, NOP, PATH1, new LinkResourceRequest[0]);
+ return new PathIntent(APPID, MATCH, NOP, PATH1);
}
@Override
protected PathIntent createAnother() {
- return new PathIntent(APPID, MATCH, NOP, PATH2, new LinkResourceRequest[0]);
+ return new PathIntent(APPID, MATCH, NOP, PATH2);
}
}
diff --git a/core/net/src/main/java/org/onlab/onos/net/flow/impl/FlowRuleManager.java b/core/net/src/main/java/org/onlab/onos/net/flow/impl/FlowRuleManager.java
index 4fe9022..bd7fb94 100644
--- a/core/net/src/main/java/org/onlab/onos/net/flow/impl/FlowRuleManager.java
+++ b/core/net/src/main/java/org/onlab/onos/net/flow/impl/FlowRuleManager.java
@@ -371,10 +371,11 @@
final FlowRuleBatchRequest request = event.subject();
switch (event.type()) {
case BATCH_OPERATION_REQUESTED:
- for (FlowEntry entry : request.toAdd()) {
+ // Request has been forwarded to MASTER Node, and was
+ for (FlowRule entry : request.toAdd()) {
eventDispatcher.post(new FlowRuleEvent(FlowRuleEvent.Type.RULE_ADD_REQUESTED, entry));
}
- for (FlowEntry entry : request.toRemove()) {
+ for (FlowRule entry : request.toRemove()) {
eventDispatcher.post(new FlowRuleEvent(FlowRuleEvent.Type.RULE_REMOVE_REQUESTED, entry));
}
// FIXME: what about op.equals(FlowRuleOperation.MODIFY) ?
@@ -392,21 +393,15 @@
Futures.getUnchecked(result)));
}
}, futureListeners);
+ break;
- break;
case BATCH_OPERATION_COMPLETED:
- Set<FlowRule> failedItems = event.result().failedItems();
- for (FlowEntry entry : request.toAdd()) {
- if (!failedItems.contains(entry)) {
- eventDispatcher.post(new FlowRuleEvent(FlowRuleEvent.Type.RULE_ADDED, entry));
- }
- }
- for (FlowEntry entry : request.toRemove()) {
- if (!failedItems.contains(entry)) {
- eventDispatcher.post(new FlowRuleEvent(FlowRuleEvent.Type.RULE_REMOVED, entry));
- }
- }
+ // MASTER Node has pushed the batch down to the Device
+
+ // Note: RULE_ADDED will be posted
+ // when Flow was actually confirmed by stats reply.
break;
+
default:
break;
}
diff --git a/core/net/src/main/java/org/onlab/onos/net/host/impl/HostManager.java b/core/net/src/main/java/org/onlab/onos/net/host/impl/HostManager.java
index 762bae0..5b5e5b7 100644
--- a/core/net/src/main/java/org/onlab/onos/net/host/impl/HostManager.java
+++ b/core/net/src/main/java/org/onlab/onos/net/host/impl/HostManager.java
@@ -207,7 +207,7 @@
}
@Override
- public PortAddresses getAddressBindingsForPort(ConnectPoint connectPoint) {
+ public Set<PortAddresses> getAddressBindingsForPort(ConnectPoint connectPoint) {
return store.getAddressBindingsForPort(connectPoint);
}
diff --git a/core/net/src/main/java/org/onlab/onos/net/host/impl/HostMonitor.java b/core/net/src/main/java/org/onlab/onos/net/host/impl/HostMonitor.java
index a6af018..d7299e8 100644
--- a/core/net/src/main/java/org/onlab/onos/net/host/impl/HostMonitor.java
+++ b/core/net/src/main/java/org/onlab/onos/net/host/impl/HostMonitor.java
@@ -175,13 +175,15 @@
for (Device device : deviceService.getDevices()) {
for (Port port : deviceService.getPorts(device.id())) {
ConnectPoint cp = new ConnectPoint(device.id(), port.number());
- PortAddresses portAddresses =
+ Set<PortAddresses> portAddressSet =
hostManager.getAddressBindingsForPort(cp);
- for (InterfaceIpAddress ia : portAddresses.ipAddresses()) {
- if (ia.subnetAddress().contains(targetIp)) {
- sendProbe(device.id(), port, targetIp,
- ia.ipAddress(), portAddresses.mac());
+ for (PortAddresses portAddresses : portAddressSet) {
+ for (InterfaceIpAddress ia : portAddresses.ipAddresses()) {
+ if (ia.subnetAddress().contains(targetIp)) {
+ sendProbe(device.id(), port, targetIp,
+ ia.ipAddress(), portAddresses.mac());
+ }
}
}
}
diff --git a/core/net/src/main/java/org/onlab/onos/net/intent/impl/ConnectivityIntentCompiler.java b/core/net/src/main/java/org/onlab/onos/net/intent/impl/ConnectivityIntentCompiler.java
new file mode 100644
index 0000000..4cf1830
--- /dev/null
+++ b/core/net/src/main/java/org/onlab/onos/net/intent/impl/ConnectivityIntentCompiler.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onlab.onos.net.intent.impl;
+
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.onlab.onos.net.ElementId;
+import org.onlab.onos.net.Path;
+import org.onlab.onos.net.intent.ConnectivityIntent;
+import org.onlab.onos.net.intent.Constraint;
+import org.onlab.onos.net.intent.IntentCompiler;
+import org.onlab.onos.net.intent.IntentExtensionService;
+import org.onlab.onos.net.provider.ProviderId;
+import org.onlab.onos.net.resource.LinkResourceService;
+import org.onlab.onos.net.topology.LinkWeight;
+import org.onlab.onos.net.topology.PathService;
+import org.onlab.onos.net.topology.TopologyEdge;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Base class for compilers of various
+ * {@link org.onlab.onos.net.intent.ConnectivityIntent connectivity intents}.
+ */
+@Component(immediate = true)
+public abstract class ConnectivityIntentCompiler<T extends ConnectivityIntent>
+ implements IntentCompiler<T> {
+
+ private static final ProviderId PID = new ProviderId("core", "org.onlab.onos.core", true);
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected IntentExtensionService intentManager;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected PathService pathService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected LinkResourceService resourceService;
+
+ /**
+ * Returns an edge-weight capable of evaluating links on the basis of the
+ * specified constraints.
+ *
+ * @param constraints path constraints
+ * @return edge-weight function
+ */
+ protected LinkWeight weight(List<Constraint> constraints) {
+ return new ConstraintBasedLinkWeight(constraints);
+ }
+
+ /**
+ * Validates the specified path against the given constraints.
+ *
+ * @param path path to be checked
+ * @param constraints path constraints
+ * @return true if the path passes all constraints
+ */
+ protected boolean checkPath(Path path, List<Constraint> constraints) {
+ for (Constraint constraint : constraints) {
+ if (!constraint.validate(path, resourceService)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Computes a path between two ConnectPoints.
+ *
+ * @param intent intent on which behalf path is being computed
+ * @param one start of the path
+ * @param two end of the path
+ * @return Path between the two
+ * @throws PathNotFoundException if a path cannot be found
+ */
+ protected Path getPath(ConnectivityIntent intent,
+ ElementId one, ElementId two) {
+ Set<Path> paths = pathService.getPaths(one, two, weight(intent.constraints()));
+ if (paths.isEmpty()) {
+ throw new PathNotFoundException("No packet path from " + one + " to " + two);
+ }
+ // TODO: let's be more intelligent about this eventually
+ return paths.iterator().next();
+ }
+
+ /**
+ * Edge-weight capable of evaluating link cost using a set of constraints.
+ */
+ protected class ConstraintBasedLinkWeight implements LinkWeight {
+
+ private final List<Constraint> constraints;
+
+ /**
+ * Creates a new edge-weight function capable of evaluating links
+ * on the basis of the specified constraints.
+ *
+ * @param constraints path constraints
+ */
+ ConstraintBasedLinkWeight(List<Constraint> constraints) {
+ this.constraints = constraints;
+ }
+
+ @Override
+ public double weight(TopologyEdge edge) {
+ if (constraints == null) {
+ return 1.0;
+ }
+
+ // iterate over all constraints in order and return the weight of
+ // the first one with fast fail over the first failure
+ Iterator<Constraint> it = constraints.iterator();
+ double cost = it.next().cost(edge.link(), resourceService);
+ while (it.hasNext() && cost > 0) {
+ if (it.next().cost(edge.link(), resourceService) < 0) {
+ return -1;
+ }
+ }
+ return cost;
+ }
+ }
+
+}
diff --git a/core/net/src/main/java/org/onlab/onos/net/intent/impl/HostToHostIntentCompiler.java b/core/net/src/main/java/org/onlab/onos/net/intent/impl/HostToHostIntentCompiler.java
index 37cf84b..605d3c7 100644
--- a/core/net/src/main/java/org/onlab/onos/net/intent/impl/HostToHostIntentCompiler.java
+++ b/core/net/src/main/java/org/onlab/onos/net/intent/impl/HostToHostIntentCompiler.java
@@ -15,30 +15,21 @@
*/
package org.onlab.onos.net.intent.impl;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Set;
-
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Deactivate;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.onlab.onos.net.Host;
-import org.onlab.onos.net.HostId;
-import org.onlab.onos.net.Link;
import org.onlab.onos.net.Path;
import org.onlab.onos.net.flow.TrafficSelector;
import org.onlab.onos.net.host.HostService;
import org.onlab.onos.net.intent.HostToHostIntent;
import org.onlab.onos.net.intent.Intent;
-import org.onlab.onos.net.intent.IntentCompiler;
-import org.onlab.onos.net.intent.IntentExtensionService;
import org.onlab.onos.net.intent.PathIntent;
-import org.onlab.onos.net.topology.LinkWeight;
-import org.onlab.onos.net.resource.LinkResourceRequest;
-import org.onlab.onos.net.topology.PathService;
-import org.onlab.onos.net.topology.TopologyEdge;
+
+import java.util.Arrays;
+import java.util.List;
import static org.onlab.onos.net.flow.DefaultTrafficSelector.builder;
@@ -47,13 +38,7 @@
*/
@Component(immediate = true)
public class HostToHostIntentCompiler
- implements IntentCompiler<HostToHostIntent> {
-
- @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
- protected IntentExtensionService intentManager;
-
- @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
- protected PathService pathService;
+ extends ConnectivityIntentCompiler<HostToHostIntent> {
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected HostService hostService;
@@ -70,8 +55,8 @@
@Override
public List<Intent> compile(HostToHostIntent intent) {
- Path pathOne = getPath(intent.one(), intent.two());
- Path pathTwo = getPath(intent.two(), intent.one());
+ Path pathOne = getPath(intent, intent.one(), intent.two());
+ Path pathTwo = getPath(intent, intent.two(), intent.one());
Host one = hostService.getHost(intent.one());
Host two = hostService.getHost(intent.two());
@@ -85,22 +70,7 @@
HostToHostIntent intent) {
TrafficSelector selector = builder(intent.selector())
.matchEthSrc(src.mac()).matchEthDst(dst.mac()).build();
- return new PathIntent(intent.appId(), selector, intent.treatment(),
- path, new LinkResourceRequest[0]);
+ return new PathIntent(intent.appId(), selector, intent.treatment(), path);
}
- private Path getPath(HostId one, HostId two) {
- Set<Path> paths = pathService.getPaths(one, two, new LinkWeight() {
- @Override
- public double weight(TopologyEdge edge) {
- return edge.link().type() == Link.Type.OPTICAL ? -1 : +1;
- }
- });
-
- if (paths.isEmpty()) {
- throw new PathNotFoundException("No path from host " + one + " to " + two);
- }
- // TODO: let's be more intelligent about this eventually
- return paths.iterator().next();
- }
}
diff --git a/core/net/src/main/java/org/onlab/onos/net/intent/impl/OpticalPathIntentInstaller.java b/core/net/src/main/java/org/onlab/onos/net/intent/impl/OpticalPathIntentInstaller.java
index 5faae4d..27b5387 100644
--- a/core/net/src/main/java/org/onlab/onos/net/intent/impl/OpticalPathIntentInstaller.java
+++ b/core/net/src/main/java/org/onlab/onos/net/intent/impl/OpticalPathIntentInstaller.java
@@ -79,6 +79,7 @@
private ApplicationId appId;
//final short WAVELENGTH = 80;
+ static final short SIGNAL_TYPE = (short) 1;
@Activate
public void activate() {
@@ -151,7 +152,9 @@
prev = link.dst();
selectorBuilder.matchInport(link.dst().port());
+ selectorBuilder.matchOpticalSignalType(SIGNAL_TYPE); //todo
selectorBuilder.matchLambda((short) la.toInt());
+
}
// build the last T port rule
diff --git a/core/net/src/main/java/org/onlab/onos/net/intent/impl/PathIntentInstaller.java b/core/net/src/main/java/org/onlab/onos/net/intent/impl/PathIntentInstaller.java
index c3b27bf..461f670 100644
--- a/core/net/src/main/java/org/onlab/onos/net/intent/impl/PathIntentInstaller.java
+++ b/core/net/src/main/java/org/onlab/onos/net/intent/impl/PathIntentInstaller.java
@@ -35,10 +35,13 @@
import org.onlab.onos.net.flow.FlowRuleBatchOperation;
import org.onlab.onos.net.flow.TrafficSelector;
import org.onlab.onos.net.flow.TrafficTreatment;
+import org.onlab.onos.net.intent.Constraint;
import org.onlab.onos.net.intent.IntentExtensionService;
import org.onlab.onos.net.intent.IntentInstaller;
import org.onlab.onos.net.intent.PathIntent;
+import org.onlab.onos.net.resource.DefaultLinkResourceRequest;
import org.onlab.onos.net.resource.LinkResourceAllocations;
+import org.onlab.onos.net.resource.LinkResourceRequest;
import org.onlab.onos.net.resource.LinkResourceService;
import org.slf4j.Logger;
@@ -79,13 +82,7 @@
@Override
public List<FlowRuleBatchOperation> install(PathIntent intent) {
- if (intent.resourceRequests().length > 0) {
- LinkResourceAllocations allocations = allocateBandwidth(intent);
- if (allocations == null) {
- log.debug("Insufficient bandwidth available to install path intent {}", intent);
- return null;
- }
- }
+ LinkResourceAllocations allocations = allocateResources(intent);
TrafficSelector.Builder builder =
DefaultTrafficSelector.builder(intent.selector());
@@ -110,6 +107,10 @@
@Override
public List<FlowRuleBatchOperation> uninstall(PathIntent intent) {
+ LinkResourceAllocations allocatedResources = resourceService.getAllocations(intent.id());
+ if (allocatedResources != null) {
+ resourceService.releaseResources(allocatedResources);
+ }
TrafficSelector.Builder builder =
DefaultTrafficSelector.builder(intent.selector());
Iterator<Link> links = intent.path().links().iterator();
@@ -130,8 +131,23 @@
return Lists.newArrayList(new FlowRuleBatchOperation(rules));
}
- private LinkResourceAllocations allocateBandwidth(PathIntent intent) {
- return resourceService.requestResources(intent.resourceRequests()[0]);
+ /**
+ * Allocate resources required for an intent.
+ *
+ * @param intent intent to allocate resource for
+ * @return allocated resources if any are required, null otherwise
+ */
+ private LinkResourceAllocations allocateResources(PathIntent intent) {
+ if (intent.constraints() == null) {
+ return null;
+ }
+ LinkResourceRequest.Builder builder =
+ DefaultLinkResourceRequest.builder(intent.id(), intent.path().links());
+ for (Constraint constraint : intent.constraints()) {
+ builder.addConstraint(constraint);
+ }
+ LinkResourceRequest request = builder.build();
+ return request.resources().isEmpty() ? null : resourceService.requestResources(request);
}
// TODO refactor below this line... ----------------------------
diff --git a/core/net/src/main/java/org/onlab/onos/net/intent/impl/PointToPointIntentCompiler.java b/core/net/src/main/java/org/onlab/onos/net/intent/impl/PointToPointIntentCompiler.java
index 893b7ea..c32c8ee 100644
--- a/core/net/src/main/java/org/onlab/onos/net/intent/impl/PointToPointIntentCompiler.java
+++ b/core/net/src/main/java/org/onlab/onos/net/intent/impl/PointToPointIntentCompiler.java
@@ -15,31 +15,20 @@
*/
package org.onlab.onos.net.intent.impl;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Set;
-
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Deactivate;
-import org.apache.felix.scr.annotations.Reference;
-import org.apache.felix.scr.annotations.ReferenceCardinality;
-import org.onlab.onos.net.ConnectPoint;
import org.onlab.onos.net.DefaultEdgeLink;
import org.onlab.onos.net.DefaultPath;
import org.onlab.onos.net.Link;
import org.onlab.onos.net.Path;
import org.onlab.onos.net.intent.Intent;
-import org.onlab.onos.net.intent.IntentCompiler;
-import org.onlab.onos.net.intent.IntentExtensionService;
import org.onlab.onos.net.intent.PathIntent;
import org.onlab.onos.net.intent.PointToPointIntent;
import org.onlab.onos.net.provider.ProviderId;
-import org.onlab.onos.net.resource.LinkResourceRequest;
-import org.onlab.onos.net.topology.LinkWeight;
-import org.onlab.onos.net.topology.Topology;
-import org.onlab.onos.net.topology.TopologyEdge;
-import org.onlab.onos.net.topology.TopologyService;
+
+import java.util.ArrayList;
+import java.util.List;
import static java.util.Arrays.asList;
@@ -48,14 +37,11 @@
*/
@Component(immediate = true)
public class PointToPointIntentCompiler
- implements IntentCompiler<PointToPointIntent> {
+ extends ConnectivityIntentCompiler<PointToPointIntent> {
- private static final ProviderId PID = new ProviderId("core", "org.onlab.onos.core", true);
- @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
- protected IntentExtensionService intentManager;
-
- @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
- protected TopologyService topologyService;
+ // TODO: use off-the-shell core provider ID
+ private static final ProviderId PID =
+ new ProviderId("core", "org.onlab.onos.core", true);
@Activate
public void activate() {
@@ -69,15 +55,16 @@
@Override
public List<Intent> compile(PointToPointIntent intent) {
- Path path = getPath(intent.ingressPoint(), intent.egressPoint());
+ Path path = getPath(intent, intent.ingressPoint().deviceId(),
+ intent.egressPoint().deviceId());
List<Link> links = new ArrayList<>();
links.add(DefaultEdgeLink.createEdgeLink(intent.ingressPoint(), true));
links.addAll(path.links());
links.add(DefaultEdgeLink.createEdgeLink(intent.egressPoint(), false));
- return asList(createPathIntent(new DefaultPath(PID, links, path.cost() + 2,
- path.annotations()), intent));
+ return asList(createPathIntent(new DefaultPath(PID, links, path.cost(),
+ path.annotations()), intent));
}
/**
@@ -90,34 +77,7 @@
private Intent createPathIntent(Path path,
PointToPointIntent intent) {
return new PathIntent(intent.appId(),
- intent.selector(), intent.treatment(), path,
- new LinkResourceRequest[0]);
+ intent.selector(), intent.treatment(), path);
}
- /**
- * Computes a path between two ConnectPoints.
- *
- * @param one start of the path
- * @param two end of the path
- * @return Path between the two
- * @throws PathNotFoundException if a path cannot be found
- */
- private Path getPath(ConnectPoint one, ConnectPoint two) {
- Topology topology = topologyService.currentTopology();
- LinkWeight weight = new LinkWeight() {
- @Override
- public double weight(TopologyEdge edge) {
- return edge.link().type() == Link.Type.OPTICAL ? -1 : +1;
- }
- };
-
- Set<Path> paths = topologyService.getPaths(topology, one.deviceId(),
- two.deviceId(), weight);
- if (paths.isEmpty()) {
- throw new PathNotFoundException("No packet path from " + one + " to " + two);
- }
-
- // TODO: let's be more intelligent about this eventually
- return paths.iterator().next();
- }
}
diff --git a/core/net/src/main/java/org/onlab/onos/net/intent/impl/PointToPointIntentWithBandwidthConstraintCompiler.java b/core/net/src/main/java/org/onlab/onos/net/intent/impl/PointToPointIntentWithBandwidthConstraintCompiler.java
deleted file mode 100644
index a192a9f..0000000
--- a/core/net/src/main/java/org/onlab/onos/net/intent/impl/PointToPointIntentWithBandwidthConstraintCompiler.java
+++ /dev/null
@@ -1,146 +0,0 @@
-package org.onlab.onos.net.intent.impl;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Set;
-
-import org.apache.felix.scr.annotations.Activate;
-import org.apache.felix.scr.annotations.Component;
-import org.apache.felix.scr.annotations.Deactivate;
-import org.apache.felix.scr.annotations.Reference;
-import org.apache.felix.scr.annotations.ReferenceCardinality;
-import org.onlab.onos.net.ConnectPoint;
-import org.onlab.onos.net.DefaultEdgeLink;
-import org.onlab.onos.net.DefaultPath;
-import org.onlab.onos.net.Link;
-import org.onlab.onos.net.Path;
-import org.onlab.onos.net.intent.Intent;
-import org.onlab.onos.net.intent.IntentCompiler;
-import org.onlab.onos.net.intent.IntentExtensionService;
-import org.onlab.onos.net.intent.PathIntent;
-import org.onlab.onos.net.intent.PointToPointIntent;
-import org.onlab.onos.net.intent.PointToPointIntentWithBandwidthConstraint;
-import org.onlab.onos.net.provider.ProviderId;
-import org.onlab.onos.net.resource.BandwidthResourceRequest;
-import org.onlab.onos.net.resource.DefaultLinkResourceRequest;
-import org.onlab.onos.net.resource.LinkResourceRequest;
-import org.onlab.onos.net.resource.LinkResourceService;
-import org.onlab.onos.net.resource.ResourceRequest;
-import org.onlab.onos.net.resource.ResourceType;
-import org.onlab.onos.net.topology.LinkWeight;
-import org.onlab.onos.net.topology.Topology;
-import org.onlab.onos.net.topology.TopologyEdge;
-import org.onlab.onos.net.topology.TopologyService;
-
-/**
- * A intent compiler for {@link org.onlab.onos.net.intent.HostToHostIntent}.
- */
-@Component(immediate = true)
-public class PointToPointIntentWithBandwidthConstraintCompiler
- implements IntentCompiler<PointToPointIntentWithBandwidthConstraint> {
-
- private static final ProviderId PID = new ProviderId("core", "org.onlab.onos.core", true);
- @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
- protected IntentExtensionService intentManager;
-
- @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
- protected TopologyService topologyService;
-
- @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
- protected LinkResourceService resourceService;
-
- @Activate
- public void activate() {
- intentManager.registerCompiler(PointToPointIntentWithBandwidthConstraint.class, this);
- }
-
- @Deactivate
- public void deactivate() {
- intentManager.unregisterCompiler(PointToPointIntent.class);
- }
-
- @Override
- public List<Intent> compile(PointToPointIntentWithBandwidthConstraint intent) {
- Path path = getPath(intent.ingressPoint(), intent.egressPoint(), intent.bandwidthRequest());
-
- List<Link> links = new ArrayList<>();
- links.add(DefaultEdgeLink.createEdgeLink(intent.ingressPoint(), true));
- links.addAll(path.links());
- links.add(DefaultEdgeLink.createEdgeLink(intent.egressPoint(), false));
-
- return Arrays.asList(createPathIntent(new DefaultPath(PID, links, path.cost() + 2,
- path.annotations()),
- intent));
- }
-
- /**
- * Creates a path intent from the specified path and original
- * connectivity intent.
- *
- * @param path path to create an intent for
- * @param intent original intent
- */
- private Intent createPathIntent(Path path,
- PointToPointIntentWithBandwidthConstraint intent) {
- LinkResourceRequest.Builder request = DefaultLinkResourceRequest.builder(intent.id(),
- path.links())
- // TODO - this seems awkward, maybe allow directly attaching a BandwidthRequest
- .addBandwidthRequest(intent.bandwidthRequest().bandwidth().toDouble());
- LinkResourceRequest bandwidthRequest = request.build();
- LinkResourceRequest[] bandwidthRequests = {bandwidthRequest};
- return new PathIntent(intent.appId(),
- intent.selector(), intent.treatment(), path,
- bandwidthRequests);
- }
-
- /**
- * Computes a path between two ConnectPoints.
- *
- * @param one start of the path
- * @param two end of the path
- * @return Path between the two
- * @throws org.onlab.onos.net.intent.impl.PathNotFoundException if a path cannot be found
- */
- private Path getPath(ConnectPoint one, ConnectPoint two, final BandwidthResourceRequest bandwidthRequest) {
- Topology topology = topologyService.currentTopology();
- LinkWeight weight = new LinkWeight() {
- @Override
- public double weight(TopologyEdge edge) {
- if (bandwidthRequest != null) {
- double allocatedBandwidth = 0.0;
- Iterable<ResourceRequest> availableResources = resourceService.getAvailableResources(edge.link());
- for (ResourceRequest availableResource : availableResources) {
- if (availableResource.type() == ResourceType.BANDWIDTH) {
- BandwidthResourceRequest bandwidthRequest = (BandwidthResourceRequest) availableResource;
- allocatedBandwidth += bandwidthRequest.bandwidth().toDouble();
- }
- }
-
- // TODO this needs to be discovered from switch/ports somehow
- double maxBandwidth = 1000;
-
- double availableBandwidth = maxBandwidth - allocatedBandwidth;
- if (availableBandwidth >= bandwidthRequest.bandwidth().toDouble()) {
- return 1;
- } else {
- return -1;
- }
- } else {
- return 1;
- }
- }
- };
-
- Set<Path> paths = topologyService.getPaths(topology,
- one.deviceId(),
- two.deviceId(),
- weight);
-
- if (paths.isEmpty()) {
- throw new PathNotFoundException("No packet path from " + one + " to " + two);
- }
- // TODO: let's be more intelligent about this eventually
- return paths.iterator().next();
- }
-}
diff --git a/core/net/src/main/java/org/onlab/onos/net/proxyarp/impl/ProxyArpManager.java b/core/net/src/main/java/org/onlab/onos/net/proxyarp/impl/ProxyArpManager.java
index 0aae62d..49528d0 100644
--- a/core/net/src/main/java/org/onlab/onos/net/proxyarp/impl/ProxyArpManager.java
+++ b/core/net/src/main/java/org/onlab/onos/net/proxyarp/impl/ProxyArpManager.java
@@ -134,14 +134,16 @@
IpAddress target =
IpAddress.valueOf(IpAddress.Version.INET,
arp.getTargetProtocolAddress());
- PortAddresses addresses =
+ Set<PortAddresses> addressSet =
hostService.getAddressBindingsForPort(inPort);
- for (InterfaceIpAddress ia : addresses.ipAddresses()) {
- if (ia.ipAddress().equals(target)) {
- Ethernet arpReply =
- buildArpReply(ia.ipAddress(), addresses.mac(), eth);
- sendTo(arpReply, inPort);
+ for (PortAddresses addresses : addressSet) {
+ for (InterfaceIpAddress ia : addresses.ipAddresses()) {
+ if (ia.ipAddress().equals(target)) {
+ Ethernet arpReply =
+ buildArpReply(ia.ipAddress(), addresses.mac(), eth);
+ sendTo(arpReply, inPort);
+ }
}
}
return;
@@ -244,7 +246,7 @@
// TODO: Is this sufficient to identify outside-facing ports: just
// having IP addresses on a port?
//
- return !hostService.getAddressBindingsForPort(port).ipAddresses().isEmpty();
+ return !hostService.getAddressBindingsForPort(port).isEmpty();
}
@Override
diff --git a/core/net/src/test/java/org/onlab/onos/net/flow/impl/FlowRuleManagerTest.java b/core/net/src/test/java/org/onlab/onos/net/flow/impl/FlowRuleManagerTest.java
index b986d6d..f67d992 100644
--- a/core/net/src/test/java/org/onlab/onos/net/flow/impl/FlowRuleManagerTest.java
+++ b/core/net/src/test/java/org/onlab/onos/net/flow/impl/FlowRuleManagerTest.java
@@ -148,7 +148,7 @@
int i = 0;
System.err.println("events :" + listener.events);
for (FlowRuleEvent e : listener.events) {
- assertTrue("unexpected event", e.type().equals(events[i]));
+ assertEquals("unexpected event", events[i], e.type());
i++;
}
@@ -178,15 +178,13 @@
RULE_ADDED, RULE_ADDED);
addFlowRule(1);
+ System.err.println("events :" + listener.events);
assertEquals("should still be 2 rules", 2, flowCount());
providerService.pushFlowMetrics(DID, ImmutableList.of(fe1));
validateEvents(RULE_UPDATED);
}
-
- // TODO: If preserving iteration order is a requirement, redo FlowRuleStore.
- //backing store is sensitive to the order of additions/removals
private boolean validateState(Map<FlowRule, FlowEntryState> expected) {
Map<FlowRule, FlowEntryState> expectedToCheck = new HashMap<>(expected);
Iterable<FlowEntry> rules = service.getFlowEntries(DID);
@@ -539,17 +537,17 @@
@Override
public boolean cancel(boolean mayInterruptIfRunning) {
- return true;
+ return false;
}
@Override
public boolean isCancelled() {
- return true;
+ return false;
}
@Override
public boolean isDone() {
- return false;
+ return true;
}
@Override
@@ -562,12 +560,14 @@
public CompletedBatchOperation get(long timeout, TimeUnit unit)
throws InterruptedException,
ExecutionException, TimeoutException {
- return null;
+ return new CompletedBatchOperation(true, Collections.<FlowRule>emptySet());
}
@Override
public void addListener(Runnable task, Executor executor) {
- // TODO: add stuff.
+ if (isDone()) {
+ executor.execute(task);
+ }
}
}
diff --git a/core/net/src/test/java/org/onlab/onos/net/host/impl/HostManagerTest.java b/core/net/src/test/java/org/onlab/onos/net/host/impl/HostManagerTest.java
index cbc9cf1..6a058ab 100644
--- a/core/net/src/test/java/org/onlab/onos/net/host/impl/HostManagerTest.java
+++ b/core/net/src/test/java/org/onlab/onos/net/host/impl/HostManagerTest.java
@@ -234,10 +234,10 @@
new PortAddresses(CP1, Sets.newHashSet(IA1, IA2), MAC1);
mgr.bindAddressesToPort(add1);
- PortAddresses storedAddresses = mgr.getAddressBindingsForPort(CP1);
+ Set<PortAddresses> storedAddresses = mgr.getAddressBindingsForPort(CP1);
- assertTrue(add1.ipAddresses().equals(storedAddresses.ipAddresses()));
- assertTrue(add1.mac().equals(storedAddresses.mac()));
+ assertEquals(1, storedAddresses.size());
+ assertTrue(storedAddresses.contains(add1));
// Add some more addresses and check that they're added correctly
PortAddresses add2 =
@@ -246,18 +246,19 @@
mgr.bindAddressesToPort(add2);
storedAddresses = mgr.getAddressBindingsForPort(CP1);
- assertTrue(storedAddresses.ipAddresses().equals(
- Sets.newHashSet(IA1, IA2, IA3)));
- assertTrue(storedAddresses.mac().equals(MAC1));
+ assertEquals(2, storedAddresses.size());
+ assertTrue(storedAddresses.contains(add1));
+ assertTrue(storedAddresses.contains(add2));
PortAddresses add3 = new PortAddresses(CP1, null, MAC2);
mgr.bindAddressesToPort(add3);
storedAddresses = mgr.getAddressBindingsForPort(CP1);
- assertTrue(storedAddresses.ipAddresses().equals(
- Sets.newHashSet(IA1, IA2, IA3)));
- assertTrue(storedAddresses.mac().equals(MAC2));
+ assertEquals(3, storedAddresses.size());
+ assertTrue(storedAddresses.contains(add1));
+ assertTrue(storedAddresses.contains(add2));
+ assertTrue(storedAddresses.contains(add3));
}
@Test
@@ -266,10 +267,10 @@
new PortAddresses(CP1, Sets.newHashSet(IA1, IA2), MAC1);
mgr.bindAddressesToPort(add1);
- PortAddresses storedAddresses = mgr.getAddressBindingsForPort(CP1);
+ Set<PortAddresses> storedAddresses = mgr.getAddressBindingsForPort(CP1);
- assertTrue(storedAddresses.ipAddresses().size() == 2);
- assertNotNull(storedAddresses.mac());
+ assertEquals(1, storedAddresses.size());
+ assertTrue(storedAddresses.contains(add1));
PortAddresses rem1 =
new PortAddresses(CP1, Sets.newHashSet(IA1), null);
@@ -277,25 +278,15 @@
mgr.unbindAddressesFromPort(rem1);
storedAddresses = mgr.getAddressBindingsForPort(CP1);
- assertTrue(storedAddresses.ipAddresses().equals(Sets.newHashSet(IA2)));
- assertTrue(storedAddresses.mac().equals(MAC1));
+ // It shouldn't have been removed because it didn't match the originally
+ // submitted address object
+ assertEquals(1, storedAddresses.size());
+ assertTrue(storedAddresses.contains(add1));
- PortAddresses rem2 = new PortAddresses(CP1, null, MAC1);
-
- mgr.unbindAddressesFromPort(rem2);
+ mgr.unbindAddressesFromPort(add1);
storedAddresses = mgr.getAddressBindingsForPort(CP1);
- assertTrue(storedAddresses.ipAddresses().equals(Sets.newHashSet(IA2)));
- assertNull(storedAddresses.mac());
-
- PortAddresses rem3 =
- new PortAddresses(CP1, Sets.newHashSet(IA2), MAC1);
-
- mgr.unbindAddressesFromPort(rem3);
- storedAddresses = mgr.getAddressBindingsForPort(CP1);
-
- assertTrue(storedAddresses.ipAddresses().isEmpty());
- assertNull(storedAddresses.mac());
+ assertTrue(storedAddresses.isEmpty());
}
@Test
@@ -304,16 +295,15 @@
new PortAddresses(CP1, Sets.newHashSet(IA1, IA2), MAC1);
mgr.bindAddressesToPort(add1);
- PortAddresses storedAddresses = mgr.getAddressBindingsForPort(CP1);
+ Set<PortAddresses> storedAddresses = mgr.getAddressBindingsForPort(CP1);
- assertTrue(storedAddresses.ipAddresses().size() == 2);
- assertNotNull(storedAddresses.mac());
+ assertEquals(1, storedAddresses.size());
+ assertTrue(storedAddresses.contains(add1));
mgr.clearAddresses(CP1);
storedAddresses = mgr.getAddressBindingsForPort(CP1);
- assertTrue(storedAddresses.ipAddresses().isEmpty());
- assertNull(storedAddresses.mac());
+ assertTrue(storedAddresses.isEmpty());
}
@Test
@@ -322,12 +312,10 @@
new PortAddresses(CP1, Sets.newHashSet(IA1, IA2), MAC1);
mgr.bindAddressesToPort(add1);
- PortAddresses storedAddresses = mgr.getAddressBindingsForPort(CP1);
+ Set<PortAddresses> storedAddresses = mgr.getAddressBindingsForPort(CP1);
- assertTrue(storedAddresses.connectPoint().equals(CP1));
- assertTrue(storedAddresses.ipAddresses().equals(
- Sets.newHashSet(IA1, IA2)));
- assertTrue(storedAddresses.mac().equals(MAC1));
+ assertEquals(1, storedAddresses.size());
+ assertTrue(storedAddresses.contains(add1));
}
@Test
diff --git a/core/net/src/test/java/org/onlab/onos/net/host/impl/HostMonitorTest.java b/core/net/src/test/java/org/onlab/onos/net/host/impl/HostMonitorTest.java
index 654cab1..a8febd7 100644
--- a/core/net/src/test/java/org/onlab/onos/net/host/impl/HostMonitorTest.java
+++ b/core/net/src/test/java/org/onlab/onos/net/host/impl/HostMonitorTest.java
@@ -20,7 +20,9 @@
import static org.easymock.EasyMock.expectLastCall;
import static org.easymock.EasyMock.replay;
import static org.easymock.EasyMock.verify;
-import static org.junit.Assert.*;
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
import java.util.ArrayList;
import java.util.Collections;
@@ -130,7 +132,7 @@
expect(hostManager.getHostsByIp(TARGET_IP_ADDR))
.andReturn(Collections.<Host>emptySet()).anyTimes();
expect(hostManager.getAddressBindingsForPort(cp))
- .andReturn(pa).anyTimes();
+ .andReturn(Collections.singleton(pa)).anyTimes();
replay(hostManager);
TestPacketService packetService = new TestPacketService();
diff --git a/core/net/src/test/java/org/onlab/onos/net/proxyarp/impl/ProxyArpManagerTest.java b/core/net/src/test/java/org/onlab/onos/net/proxyarp/impl/ProxyArpManagerTest.java
index e82151e..8beac4a 100644
--- a/core/net/src/test/java/org/onlab/onos/net/proxyarp/impl/ProxyArpManagerTest.java
+++ b/core/net/src/test/java/org/onlab/onos/net/proxyarp/impl/ProxyArpManagerTest.java
@@ -19,7 +19,10 @@
import static org.easymock.EasyMock.createMock;
import static org.easymock.EasyMock.expect;
import static org.easymock.EasyMock.replay;
-import static org.junit.Assert.*;
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
import java.util.ArrayList;
import java.util.Collections;
@@ -207,13 +210,18 @@
IpAddress addr2 = IpAddress.valueOf("10.0." + (2 * i) + ".1");
InterfaceIpAddress ia1 = new InterfaceIpAddress(addr1, prefix1);
InterfaceIpAddress ia2 = new InterfaceIpAddress(addr2, prefix2);
- PortAddresses pa =
- new PortAddresses(cp, Sets.newHashSet(ia1, ia2),
- MacAddress.valueOf(i));
- addresses.add(pa);
+ PortAddresses pa1 =
+ new PortAddresses(cp, Sets.newHashSet(ia1),
+ MacAddress.valueOf(2 * i - 1));
+ PortAddresses pa2 =
+ new PortAddresses(cp, Sets.newHashSet(ia2),
+ MacAddress.valueOf(2 * i));
+
+ addresses.add(pa1);
+ addresses.add(pa2);
expect(hostService.getAddressBindingsForPort(cp))
- .andReturn(pa).anyTimes();
+ .andReturn(Sets.newHashSet(pa1, pa2)).anyTimes();
}
expect(hostService.getAddressBindings()).andReturn(addresses).anyTimes();
@@ -222,7 +230,7 @@
ConnectPoint cp = new ConnectPoint(getDeviceId(i + NUM_ADDRESS_PORTS),
P1);
expect(hostService.getAddressBindingsForPort(cp))
- .andReturn(new PortAddresses(cp, null, null)).anyTimes();
+ .andReturn(Collections.<PortAddresses>emptySet()).anyTimes();
}
}
@@ -339,7 +347,8 @@
IpAddress theirIp = IpAddress.valueOf("10.0.1.254");
IpAddress ourFirstIp = IpAddress.valueOf("10.0.1.1");
IpAddress ourSecondIp = IpAddress.valueOf("10.0.2.1");
- MacAddress ourMac = MacAddress.valueOf(1L);
+ MacAddress firstMac = MacAddress.valueOf(1L);
+ MacAddress secondMac = MacAddress.valueOf(2L);
Host requestor = new DefaultHost(PID, HID2, MAC2, VLAN1, LOC1,
Collections.singleton(theirIp));
@@ -352,7 +361,7 @@
proxyArp.reply(arpRequest, LOC1);
assertEquals(1, packetService.packets.size());
- Ethernet arpReply = buildArp(ARP.OP_REPLY, ourMac, MAC2, ourFirstIp, theirIp);
+ Ethernet arpReply = buildArp(ARP.OP_REPLY, firstMac, MAC2, ourFirstIp, theirIp);
verifyPacketOut(arpReply, LOC1, packetService.packets.get(0));
// Test a request for the second address on that port
@@ -362,7 +371,7 @@
proxyArp.reply(arpRequest, LOC1);
assertEquals(1, packetService.packets.size());
- arpReply = buildArp(ARP.OP_REPLY, ourMac, MAC2, ourSecondIp, theirIp);
+ arpReply = buildArp(ARP.OP_REPLY, secondMac, MAC2, ourSecondIp, theirIp);
verifyPacketOut(arpReply, LOC1, packetService.packets.get(0));
}
diff --git a/core/store/dist/pom.xml b/core/store/dist/pom.xml
index eebb8ff..846583f 100644
--- a/core/store/dist/pom.xml
+++ b/core/store/dist/pom.xml
@@ -44,6 +44,24 @@
<version>${project.version}</version>
</dependency>
+ <dependency>
+ <groupId>net.kuujo.copycat</groupId>
+ <artifactId>copycat</artifactId>
+ <version>0.4.0-SNAPSHOT</version>
+ </dependency>
+
+ <dependency>
+ <groupId>net.kuujo.copycat</groupId>
+ <artifactId>copycat-chronicle</artifactId>
+ <version>0.4.0-SNAPSHOT</version>
+ </dependency>
+
+ <dependency>
+ <groupId>net.kuujo.copycat</groupId>
+ <artifactId>copycat-tcp</artifactId>
+ <version>0.4.0-SNAPSHOT</version>
+ </dependency>
+
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/cluster/impl/ClusterDefinitionStore.java b/core/store/dist/src/main/java/org/onlab/onos/store/cluster/impl/ClusterDefinitionStore.java
index 97ec769..19b6485 100644
--- a/core/store/dist/src/main/java/org/onlab/onos/store/cluster/impl/ClusterDefinitionStore.java
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/cluster/impl/ClusterDefinitionStore.java
@@ -48,7 +48,7 @@
file = new File(filePath);
}
- /**
+ /*
* Returns set of the controller nodes, including self.
*
* @return set of controller nodes
@@ -67,7 +67,7 @@
return nodes;
}
- /**
+ /*
* Writes the given set of the controller nodes.
*
* @param nodes set of controller nodes
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/cluster/messaging/impl/ClusterCommunicationManager.java b/core/store/dist/src/main/java/org/onlab/onos/store/cluster/messaging/impl/ClusterCommunicationManager.java
index 79612c9..38e1322 100644
--- a/core/store/dist/src/main/java/org/onlab/onos/store/cluster/messaging/impl/ClusterCommunicationManager.java
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/cluster/messaging/impl/ClusterCommunicationManager.java
@@ -88,7 +88,7 @@
log.error("NettyMessagingService#activate", e);
}
messagingService = netty;
- log.info("Started");
+ log.info("Started on {}:{}", localNode.ip(), localNode.tcpPort());
}
@Deactivate
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/GossipDeviceStore.java b/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/GossipDeviceStore.java
index 66366c1..19dc3e3 100644
--- a/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/GossipDeviceStore.java
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/GossipDeviceStore.java
@@ -32,6 +32,8 @@
import org.onlab.onos.cluster.ControllerNode;
import org.onlab.onos.cluster.NodeId;
import org.onlab.onos.mastership.MastershipService;
+import org.onlab.onos.mastership.MastershipTerm;
+import org.onlab.onos.mastership.MastershipTermService;
import org.onlab.onos.net.AnnotationsUtil;
import org.onlab.onos.net.DefaultAnnotations;
import org.onlab.onos.net.DefaultDevice;
@@ -39,6 +41,7 @@
import org.onlab.onos.net.Device;
import org.onlab.onos.net.Device.Type;
import org.onlab.onos.net.DeviceId;
+import org.onlab.onos.net.MastershipRole;
import org.onlab.onos.net.Port;
import org.onlab.onos.net.PortNumber;
import org.onlab.onos.net.device.DeviceClockService;
@@ -89,6 +92,7 @@
import static org.onlab.util.Tools.namedThreads;
import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
import static org.onlab.onos.store.device.impl.GossipDeviceStoreMessageSubjects.DEVICE_ADVERTISE;
+import static org.onlab.onos.store.device.impl.GossipDeviceStoreMessageSubjects.DEVICE_REMOVE_REQ;
// TODO: give me a better name
/**
@@ -160,6 +164,7 @@
GossipDeviceStoreMessageSubjects.DEVICE_UPDATE, new InternalDeviceEventListener());
clusterCommunicator.addSubscriber(
GossipDeviceStoreMessageSubjects.DEVICE_OFFLINE, new InternalDeviceOfflineEventListener());
+ clusterCommunicator.addSubscriber(DEVICE_REMOVE_REQ, new InternalRemoveRequestListener());
clusterCommunicator.addSubscriber(
GossipDeviceStoreMessageSubjects.DEVICE_REMOVED, new InternalDeviceRemovedEventListener());
clusterCommunicator.addSubscriber(
@@ -715,14 +720,48 @@
@Override
public synchronized DeviceEvent removeDevice(DeviceId deviceId) {
- final NodeId master = mastershipService.getMasterFor(deviceId);
- if (!clusterService.getLocalNode().id().equals(master)) {
- log.info("Removal of device {} requested on non master node", deviceId);
- // FIXME silently ignoring. Should be forwarding or broadcasting to
- // master.
- return null;
+ final NodeId myId = clusterService.getLocalNode().id();
+ NodeId master = mastershipService.getMasterFor(deviceId);
+
+ // if there exist a master, forward
+ // if there is no master, try to become one and process
+
+ boolean relinquishAtEnd = false;
+ if (master == null) {
+ final MastershipRole myRole = mastershipService.getLocalRole(deviceId);
+ if (myRole != MastershipRole.NONE) {
+ relinquishAtEnd = true;
+ }
+ log.info("Temporarlily requesting role for {} to remove", deviceId);
+ mastershipService.requestRoleFor(deviceId);
+ MastershipTermService termService = mastershipService.requestTermService();
+ MastershipTerm term = termService.getMastershipTerm(deviceId);
+ if (myId.equals(term.master())) {
+ master = myId;
+ }
}
+ if (!myId.equals(master)) {
+ log.info("{} has control of {}, forwarding remove request",
+ master, deviceId);
+
+ ClusterMessage message = new ClusterMessage(
+ myId,
+ DEVICE_REMOVE_REQ,
+ SERIALIZER.encode(deviceId));
+
+ try {
+ clusterCommunicator.unicast(message, master);
+ } catch (IOException e) {
+ log.error("Failed to forward {} remove request to {}", deviceId, master, e);
+ }
+
+ // event will be triggered after master processes it.
+ return null;
+ }
+
+ // I have control..
+
Timestamp timestamp = deviceClockService.getTimestamp(deviceId);
DeviceEvent event = removeDeviceInternal(deviceId, timestamp);
if (event != null) {
@@ -735,6 +774,10 @@
deviceId);
}
}
+ if (relinquishAtEnd) {
+ log.info("Relinquishing temporary role acquired for {}", deviceId);
+ mastershipService.relinquishMastership(deviceId);
+ }
return event;
}
@@ -1241,6 +1284,16 @@
}
}
+ private final class InternalRemoveRequestListener
+ implements ClusterMessageHandler {
+ @Override
+ public void handle(ClusterMessage message) {
+ log.debug("Received device remove request from peer: {}", message.sender());
+ DeviceId did = SERIALIZER.decode(message.payload());
+ removeDevice(did);
+ }
+ }
+
private class InternalDeviceRemovedEventListener implements ClusterMessageHandler {
@Override
public void handle(ClusterMessage message) {
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/GossipDeviceStoreMessageSubjects.java b/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/GossipDeviceStoreMessageSubjects.java
index 89577a8..4c9e48c 100644
--- a/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/GossipDeviceStoreMessageSubjects.java
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/GossipDeviceStoreMessageSubjects.java
@@ -27,6 +27,7 @@
public static final MessageSubject DEVICE_UPDATE = new MessageSubject("peer-device-update");
public static final MessageSubject DEVICE_OFFLINE = new MessageSubject("peer-device-offline");
+ public static final MessageSubject DEVICE_REMOVE_REQ = new MessageSubject("peer-device-remove-request");
public static final MessageSubject DEVICE_REMOVED = new MessageSubject("peer-device-removed");
public static final MessageSubject PORT_UPDATE = new MessageSubject("peer-port-update");
public static final MessageSubject PORT_STATUS_UPDATE = new MessageSubject("peer-port-status-update");
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/flow/impl/DistributedFlowRuleStore.java b/core/store/dist/src/main/java/org/onlab/onos/store/flow/impl/DistributedFlowRuleStore.java
index ca3b29c..81f2bfd 100644
--- a/core/store/dist/src/main/java/org/onlab/onos/store/flow/impl/DistributedFlowRuleStore.java
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/flow/impl/DistributedFlowRuleStore.java
@@ -106,10 +106,11 @@
private final Logger log = getLogger(getClass());
+ // primary data:
+ // read/write needs to be synchronized
// store entries as a pile of rules, no info about device tables
- private final Multimap<DeviceId, StoredFlowEntry> flowEntries =
- ArrayListMultimap.<DeviceId, StoredFlowEntry>create();
-
+ private final Multimap<DeviceId, StoredFlowEntry> flowEntries
+ = ArrayListMultimap.<DeviceId, StoredFlowEntry>create();
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected ReplicaInfoService replicaInfoManager;
@@ -135,6 +136,7 @@
//.removalListener(listener)
.build();
+ // Cache of SMaps used for backup data. each SMap contain device flow table
private LoadingCache<DeviceId, SMap<FlowId, ImmutableList<StoredFlowEntry>>> smaps;
@@ -311,7 +313,7 @@
}
}
- private Set<FlowEntry> getFlowEntriesInternal(DeviceId deviceId) {
+ private synchronized Set<FlowEntry> getFlowEntriesInternal(DeviceId deviceId) {
Collection<? extends FlowEntry> rules = flowEntries.get(deviceId);
if (rules == null) {
return Collections.emptySet();
@@ -365,7 +367,9 @@
}
}
- private ListenableFuture<CompletedBatchOperation> storeBatchInternal(FlowRuleBatchOperation operation) {
+ private synchronized ListenableFuture<CompletedBatchOperation>
+ storeBatchInternal(FlowRuleBatchOperation operation) {
+
final List<StoredFlowEntry> toRemove = new ArrayList<>();
final List<StoredFlowEntry> toAdd = new ArrayList<>();
DeviceId did = null;
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/host/impl/GossipHostStore.java b/core/store/dist/src/main/java/org/onlab/onos/store/host/impl/GossipHostStore.java
index 8029e27..30a73e0 100644
--- a/core/store/dist/src/main/java/org/onlab/onos/store/host/impl/GossipHostStore.java
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/host/impl/GossipHostStore.java
@@ -15,12 +15,25 @@
*/
package org.onlab.onos.store.host.impl;
-import com.google.common.collect.FluentIterable;
-import com.google.common.collect.HashMultimap;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Multimap;
-import com.google.common.collect.Sets;
+import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
+import static org.onlab.onos.cluster.ControllerNodeToNodeId.toNodeId;
+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.onlab.util.Tools.namedThreads;
+import static org.slf4j.LoggerFactory.getLogger;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
import org.apache.commons.lang3.RandomUtils;
import org.apache.felix.scr.annotations.Activate;
@@ -45,7 +58,6 @@
import org.onlab.onos.net.host.HostEvent;
import org.onlab.onos.net.host.HostStore;
import org.onlab.onos.net.host.HostStoreDelegate;
-import org.onlab.onos.net.host.InterfaceIpAddress;
import org.onlab.onos.net.host.PortAddresses;
import org.onlab.onos.net.provider.ProviderId;
import org.onlab.onos.store.AbstractStore;
@@ -63,21 +75,13 @@
import org.onlab.util.KryoNamespace;
import org.slf4j.Logger;
-import java.io.IOException;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-import java.util.Map.Entry;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.TimeUnit;
-
-import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
-import static org.onlab.onos.cluster.ControllerNodeToNodeId.toNodeId;
-import static org.onlab.onos.net.host.HostEvent.Type.*;
-import static org.onlab.util.Tools.namedThreads;
-import static org.slf4j.LoggerFactory.getLogger;
+import com.google.common.collect.FluentIterable;
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Multimap;
+import com.google.common.collect.Multimaps;
+import com.google.common.collect.SetMultimap;
//TODO: multi-provider, annotation not supported.
/**
@@ -100,8 +104,9 @@
// Hosts tracked by their location
private final Multimap<ConnectPoint, Host> locations = HashMultimap.create();
- private final Map<ConnectPoint, PortAddresses> portAddresses =
- new ConcurrentHashMap<>();
+ private final SetMultimap<ConnectPoint, PortAddresses> portAddresses =
+ Multimaps.synchronizedSetMultimap(
+ HashMultimap.<ConnectPoint, PortAddresses>create());
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected HostClockService hostClockService;
@@ -343,77 +348,37 @@
@Override
public void updateAddressBindings(PortAddresses addresses) {
- synchronized (portAddresses) {
- PortAddresses existing = portAddresses.get(addresses.connectPoint());
- if (existing == null) {
- portAddresses.put(addresses.connectPoint(), addresses);
- } else {
- Set<InterfaceIpAddress> union =
- Sets.union(existing.ipAddresses(),
- addresses.ipAddresses()).immutableCopy();
-
- MacAddress newMac = (addresses.mac() == null) ? existing.mac()
- : addresses.mac();
-
- PortAddresses newAddresses =
- new PortAddresses(addresses.connectPoint(), union, newMac);
-
- portAddresses.put(newAddresses.connectPoint(), newAddresses);
- }
- }
+ portAddresses.put(addresses.connectPoint(), addresses);
}
@Override
public void removeAddressBindings(PortAddresses addresses) {
- synchronized (portAddresses) {
- PortAddresses existing = portAddresses.get(addresses.connectPoint());
- if (existing != null) {
- Set<InterfaceIpAddress> difference =
- Sets.difference(existing.ipAddresses(),
- addresses.ipAddresses()).immutableCopy();
-
- // If they removed the existing mac, set the new mac to null.
- // Otherwise, keep the existing mac.
- MacAddress newMac = existing.mac();
- if (addresses.mac() != null && addresses.mac().equals(existing.mac())) {
- newMac = null;
- }
-
- PortAddresses newAddresses =
- new PortAddresses(addresses.connectPoint(), difference, newMac);
-
- portAddresses.put(newAddresses.connectPoint(), newAddresses);
- }
- }
+ portAddresses.remove(addresses.connectPoint(), addresses);
}
@Override
public void clearAddressBindings(ConnectPoint connectPoint) {
- synchronized (portAddresses) {
- portAddresses.remove(connectPoint);
- }
+ portAddresses.removeAll(connectPoint);
}
@Override
public Set<PortAddresses> getAddressBindings() {
synchronized (portAddresses) {
- return new HashSet<>(portAddresses.values());
+ return ImmutableSet.copyOf(portAddresses.values());
}
}
@Override
- public PortAddresses getAddressBindingsForPort(ConnectPoint connectPoint) {
- PortAddresses addresses;
-
+ public Set<PortAddresses> getAddressBindingsForPort(ConnectPoint connectPoint) {
synchronized (portAddresses) {
- addresses = portAddresses.get(connectPoint);
- }
+ Set<PortAddresses> addresses = portAddresses.get(connectPoint);
- if (addresses == null) {
- addresses = new PortAddresses(connectPoint, null, null);
+ if (addresses == null) {
+ return Collections.emptySet();
+ } else {
+ return ImmutableSet.copyOf(addresses);
+ }
}
-
- return addresses;
}
// Auxiliary extension to allow location to mutate.
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/link/impl/GossipLinkStore.java b/core/store/dist/src/main/java/org/onlab/onos/store/link/impl/GossipLinkStore.java
index ab5cc8a..351581d 100644
--- a/core/store/dist/src/main/java/org/onlab/onos/store/link/impl/GossipLinkStore.java
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/link/impl/GossipLinkStore.java
@@ -122,7 +122,7 @@
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected ClusterService clusterService;
- private static final KryoSerializer SERIALIZER = new KryoSerializer() {
+ protected static final KryoSerializer SERIALIZER = new KryoSerializer() {
@Override
protected void setupKryoPool() {
serializerPool = KryoNamespace.newBuilder()
@@ -377,6 +377,7 @@
try {
timestamp = deviceClockService.getTimestamp(dstDeviceId);
} catch (IllegalStateException e) {
+ log.warn("Failed to remove link {}, was not the master", key);
//there are times when this is called before mastership
// handoff correctly completes.
return null;
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/mastership/impl/DistributedMastershipStore.java b/core/store/dist/src/main/java/org/onlab/onos/store/mastership/impl/DistributedMastershipStore.java
index c9d4422..42e0799 100644
--- a/core/store/dist/src/main/java/org/onlab/onos/store/mastership/impl/DistributedMastershipStore.java
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/mastership/impl/DistributedMastershipStore.java
@@ -447,7 +447,16 @@
RoleValue oldValue = event.getOldValue();
RoleValue newValue = event.getValue();
- if (Objects.equal(oldValue.get(MASTER), newValue.get(MASTER))) {
+ // There will be no oldValue at the very first instance of an EntryEvent.
+ // Technically, the progression is: null event -> null master -> some master;
+ // We say a null master and a null oldValue are the same condition.
+ NodeId oldMaster = null;
+ if (oldValue != null) {
+ oldMaster = oldValue.get(MASTER);
+ }
+ NodeId newMaster = newValue.get(MASTER);
+
+ if (!Objects.equal(oldMaster, newMaster)) {
notifyDelegate(new MastershipEvent(
MASTER_CHANGED, event.getKey(), event.getValue().roleInfo()));
} else {
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/mastership/impl/RoleValue.java b/core/store/dist/src/main/java/org/onlab/onos/store/mastership/impl/RoleValue.java
index aa5d911..a074b94 100644
--- a/core/store/dist/src/main/java/org/onlab/onos/store/mastership/impl/RoleValue.java
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/mastership/impl/RoleValue.java
@@ -31,6 +31,7 @@
import com.google.common.base.MoreObjects;
import com.google.common.base.MoreObjects.ToStringHelper;
+import com.google.common.collect.Lists;
/**
* A structure that holds node mastership roles associated with a
@@ -40,12 +41,26 @@
protected final Map<MastershipRole, List<NodeId>> value = new EnumMap<>(MastershipRole.class);
+ /**
+ * Constructs empty RoleValue.
+ */
public RoleValue() {
value.put(MastershipRole.MASTER, new LinkedList<NodeId>());
value.put(MastershipRole.STANDBY, new LinkedList<NodeId>());
value.put(MastershipRole.NONE, new LinkedList<NodeId>());
}
+ /**
+ * Constructs copy of specified RoleValue.
+ *
+ * @param original original to create copy from
+ */
+ public RoleValue(final RoleValue original) {
+ value.put(MASTER, Lists.newLinkedList(original.value.get(MASTER)));
+ value.put(STANDBY, Lists.newLinkedList(original.value.get(STANDBY)));
+ value.put(NONE, Lists.newLinkedList(original.value.get(NONE)));
+ }
+
// exposing internals for serialization purpose only
Map<MastershipRole, List<NodeId>> value() {
return Collections.unmodifiableMap(value);
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/resource/impl/DistributedLinkResourceStore.java b/core/store/dist/src/main/java/org/onlab/onos/store/resource/impl/DistributedLinkResourceStore.java
index 41172cd..ecccd2b 100644
--- a/core/store/dist/src/main/java/org/onlab/onos/store/resource/impl/DistributedLinkResourceStore.java
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/resource/impl/DistributedLinkResourceStore.java
@@ -195,7 +195,7 @@
@Override
public void releaseResources(LinkResourceAllocations allocations) {
checkNotNull(allocations);
- linkResourceAllocationsMap.remove(allocations);
+ linkResourceAllocationsMap.remove(allocations.intendId());
for (Link link : allocations.links()) {
addFreeResources(link, allocations);
Set<LinkResourceAllocations> linkAllocs = allocatedResources.get(link);
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/service/DatabaseAdminService.java b/core/store/dist/src/main/java/org/onlab/onos/store/service/DatabaseAdminService.java
new file mode 100644
index 0000000..3ec4e9b
--- /dev/null
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/service/DatabaseAdminService.java
@@ -0,0 +1,35 @@
+package org.onlab.onos.store.service;
+
+import java.util.List;
+
+/**
+ * Service interface for running administrative tasks on a Database.
+ */
+public interface DatabaseAdminService {
+
+ /**
+ * Creates a new table.
+ * Table creation is idempotent. Attempting to create a table
+ * that already exists will be a noop.
+ * @param name table name.
+ * @return true if the table was created by this call, false otherwise.
+ */
+ public boolean createTable(String name);
+
+ /**
+ * Lists all the tables in the database.
+ * @return list of table names.
+ */
+ public List<String> listTables();
+
+ /**
+ * Deletes a table from the database.
+ * @param name name of the table to delete.
+ */
+ public void dropTable(String name);
+
+ /**
+ * Deletes all tables from the database.
+ */
+ public void dropAllTables();
+}
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/service/DatabaseException.java b/core/store/dist/src/main/java/org/onlab/onos/store/service/DatabaseException.java
new file mode 100644
index 0000000..bbc2daf
--- /dev/null
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/service/DatabaseException.java
@@ -0,0 +1,22 @@
+package org.onlab.onos.store.service;
+
+/**
+ * Base exception type for database failures.
+ */
+@SuppressWarnings("serial")
+public class DatabaseException extends RuntimeException {
+ public DatabaseException(String message, Throwable t) {
+ super(message, t);
+ }
+
+ public DatabaseException(String message) {
+ super(message);
+ }
+
+ public DatabaseException(Throwable t) {
+ super(t);
+ }
+
+ public DatabaseException() {
+ };
+}
\ No newline at end of file
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/service/DatabaseService.java b/core/store/dist/src/main/java/org/onlab/onos/store/service/DatabaseService.java
new file mode 100644
index 0000000..11cc5ff
--- /dev/null
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/service/DatabaseService.java
@@ -0,0 +1,43 @@
+package org.onlab.onos.store.service;
+
+import java.util.List;
+
+/**
+ * Service interface for a strongly consistent and durable
+ * key value data store.
+ */
+public interface DatabaseService {
+
+ /**
+ * Performs a read on the database.
+ * @param request read request.
+ * @return ReadResult
+ * @throws DatabaseException if there is a failure in executing read.
+ */
+ ReadResult read(ReadRequest request);
+
+ /**
+ * Performs a batch read operation on the database.
+ * The main advantage of batch read operation is parallelization.
+ * @param batch batch of read requests to execute.
+ * @return batch read result.
+ */
+ List<OptionalResult<ReadResult, DatabaseException>> batchRead(List<ReadRequest> batch);
+
+ /**
+ * Performs a write operation on the database.
+ * @param request write request
+ * @return write result.
+ * @throws DatabaseException if there is failure in execution write.
+ */
+ WriteResult write(WriteRequest request);
+
+ /**
+ * Performs a batch write operation on the database.
+ * Batch write provides transactional semantics. Either all operations
+ * succeed or none of them do.
+ * @param batch batch of write requests to execute as a transaction.
+ * @return result of executing the batch write operation.
+ */
+ List<OptionalResult<WriteResult, DatabaseException>> batchWrite(List<WriteRequest> batch);
+}
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/service/NoSuchTableException.java b/core/store/dist/src/main/java/org/onlab/onos/store/service/NoSuchTableException.java
new file mode 100644
index 0000000..6e02252
--- /dev/null
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/service/NoSuchTableException.java
@@ -0,0 +1,10 @@
+package org.onlab.onos.store.service;
+
+
+/**
+ * Exception thrown when an operation (read or write) is requested for
+ * a table that does not exist.
+ */
+@SuppressWarnings("serial")
+public class NoSuchTableException extends DatabaseException {
+}
\ No newline at end of file
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/service/OptimisticLockException.java b/core/store/dist/src/main/java/org/onlab/onos/store/service/OptimisticLockException.java
new file mode 100644
index 0000000..090eb63
--- /dev/null
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/service/OptimisticLockException.java
@@ -0,0 +1,8 @@
+package org.onlab.onos.store.service;
+
+/**
+ * Exception that indicates a optimistic lock failure.
+ */
+@SuppressWarnings("serial")
+public class OptimisticLockException extends PreconditionFailedException {
+}
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/service/OptionalResult.java b/core/store/dist/src/main/java/org/onlab/onos/store/service/OptionalResult.java
new file mode 100644
index 0000000..a9341cb
--- /dev/null
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/service/OptionalResult.java
@@ -0,0 +1,26 @@
+package org.onlab.onos.store.service;
+
+/**
+ * A container object which either has a result or an exception.
+ * <p>
+ * If a result is present, get() will return it otherwise get() will throw
+ * the exception that was encountered in the process of generating the result.
+ * </p>
+ * @param <R> type of result.
+ * @param <E> exception encountered in generating the result.
+ */
+public interface OptionalResult<R, E extends Throwable> {
+
+ /**
+ * Returns the result or throws an exception if there is no
+ * valid result.
+ * @return result
+ */
+ public R get();
+
+ /**
+ * Returns true if there is a valid result.
+ * @return true is yes, false otherwise.
+ */
+ public boolean hasValidResult();
+}
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/service/PreconditionFailedException.java b/core/store/dist/src/main/java/org/onlab/onos/store/service/PreconditionFailedException.java
new file mode 100644
index 0000000..9eda528
--- /dev/null
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/service/PreconditionFailedException.java
@@ -0,0 +1,16 @@
+package org.onlab.onos.store.service;
+
+
+/**
+ * Exception that indicates a precondition failure.
+ * Scenarios that can cause this exception:
+ * <ul>
+ * <li>An operation that attempts to write a new value iff the current value is equal
+ * to some specified value.</li>
+ * <li>An operation that attempts to write a new value iff the current version
+ * matches a specified value</li>
+ * </ul>
+ */
+@SuppressWarnings("serial")
+public class PreconditionFailedException extends DatabaseException {
+}
\ No newline at end of file
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/service/ReadRequest.java b/core/store/dist/src/main/java/org/onlab/onos/store/service/ReadRequest.java
new file mode 100644
index 0000000..a22464a
--- /dev/null
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/service/ReadRequest.java
@@ -0,0 +1,41 @@
+package org.onlab.onos.store.service;
+
+import com.google.common.base.MoreObjects;
+
+/**
+ * Database read request.
+ */
+public class ReadRequest {
+
+ private final String tableName;
+ private final String key;
+
+ public ReadRequest(String tableName, String key) {
+ this.tableName = tableName;
+ this.key = key;
+ }
+
+ /**
+ * Return the name of the table.
+ * @return table name.
+ */
+ public String tableName() {
+ return tableName;
+ }
+
+ /**
+ * Returns the key.
+ * @return key.
+ */
+ public String key() {
+ return key;
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(getClass())
+ .add("tableName", tableName)
+ .add("key", key)
+ .toString();
+ }
+}
\ No newline at end of file
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/service/ReadResult.java b/core/store/dist/src/main/java/org/onlab/onos/store/service/ReadResult.java
new file mode 100644
index 0000000..6d28fc2
--- /dev/null
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/service/ReadResult.java
@@ -0,0 +1,53 @@
+package org.onlab.onos.store.service;
+
+import com.google.common.base.MoreObjects;
+
+
+/**
+ * Database read result.
+ */
+public class ReadResult {
+
+ private final String tableName;
+ private final String key;
+ private final VersionedValue value;
+
+ public ReadResult(String tableName, String key, VersionedValue value) {
+ this.tableName = tableName;
+ this.key = key;
+ this.value = value;
+ }
+
+ /**
+ * Returns database table name.
+ * @return table name
+ */
+ public String tableName() {
+ return tableName;
+ }
+
+ /**
+ * Returns database table key.
+ * @return key
+ */
+ public String key() {
+ return key;
+ }
+
+ /**
+ * Returns value associated with the key.
+ * @return non-null value if the table contains one, null otherwise.
+ */
+ public VersionedValue value() {
+ return value;
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(getClass())
+ .add("tableName", tableName)
+ .add("key", key)
+ .add("value", value)
+ .toString();
+ }
+}
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/service/VersionedValue.java b/core/store/dist/src/main/java/org/onlab/onos/store/service/VersionedValue.java
new file mode 100644
index 0000000..852fb07
--- /dev/null
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/service/VersionedValue.java
@@ -0,0 +1,48 @@
+package org.onlab.onos.store.service;
+
+import java.util.Arrays;
+
+import com.google.common.base.MoreObjects;
+
+/**
+ * Wrapper object that holds the object (as byte array) and its version.
+ */
+public class VersionedValue {
+
+ private final byte[] value;
+ private final long version;
+
+ /**
+ * Creates a new instance with the specified value and version.
+ * @param value value
+ * @param version version
+ */
+ public VersionedValue(byte[] value, long version) {
+ this.value = value;
+ this.version = version;
+ }
+
+ /**
+ * Returns the value.
+ * @return value.
+ */
+ public byte[] value() {
+ return value;
+ }
+
+ /**
+ * Returns the version.
+ * @return version.
+ */
+ public long version() {
+ return version;
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(getClass())
+ .add("version", version)
+ .add("value", Arrays.toString(value))
+ .toString();
+ }
+}
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/service/WriteAborted.java b/core/store/dist/src/main/java/org/onlab/onos/store/service/WriteAborted.java
new file mode 100644
index 0000000..a2ebd2a
--- /dev/null
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/service/WriteAborted.java
@@ -0,0 +1,10 @@
+package org.onlab.onos.store.service;
+
+
+/**
+ * Exception that indicates a write operation is aborted.
+ * Aborted operations do not mutate database state is any form.
+ */
+@SuppressWarnings("serial")
+public class WriteAborted extends DatabaseException {
+}
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/service/WriteRequest.java b/core/store/dist/src/main/java/org/onlab/onos/store/service/WriteRequest.java
new file mode 100644
index 0000000..99f73c1
--- /dev/null
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/service/WriteRequest.java
@@ -0,0 +1,102 @@
+package org.onlab.onos.store.service;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+import java.util.Objects;
+
+import com.google.common.base.MoreObjects;
+
+/**
+ * Database write request.
+ */
+public class WriteRequest {
+
+ private final String tableName;
+ private final String key;
+ private final byte[] newValue;
+ private final long previousVersion;
+ private final byte[] oldValue;
+
+ // put regardless of previous value
+ public WriteRequest(String tableName, String key, byte[] newValue) {
+ this(tableName, key, newValue, -1, null);
+ }
+
+ // put if version matches
+ public WriteRequest(String tableName, String key, byte[] newValue, long previousVersion) {
+ this(tableName, key, newValue, previousVersion, null);
+ checkArgument(previousVersion >= 0);
+ }
+
+ // put if value matches
+ public WriteRequest(String tableName, String key, byte[] newValue, byte[] oldValue) {
+ this(tableName, key, newValue, -1, oldValue);
+ }
+
+ // hidden constructor
+ private WriteRequest(String tableName, String key, byte[] newValue, long previousVersion, byte[] oldValue) {
+
+ checkArgument(tableName != null);
+ checkArgument(key != null);
+ checkArgument(newValue != null);
+
+ this.tableName = tableName;
+ this.key = key;
+ this.newValue = newValue;
+ this.previousVersion = previousVersion;
+ this.oldValue = oldValue;
+ }
+
+ public String tableName() {
+ return tableName;
+ }
+
+ public String key() {
+ return key;
+ }
+
+ public byte[] newValue() {
+ return newValue;
+ }
+
+ public long previousVersion() {
+ return previousVersion;
+ }
+
+ public byte[] oldValue() {
+ return oldValue;
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(getClass())
+ .add("tableName", tableName)
+ .add("key", key)
+ .add("newValue", newValue)
+ .add("previousVersion", previousVersion)
+ .add("oldValue", oldValue)
+ .toString();
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(key, tableName, previousVersion);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ WriteRequest other = (WriteRequest) obj;
+ return Objects.equals(this.key, other.key) &&
+ Objects.equals(this.tableName, other.tableName) &&
+ Objects.equals(this.previousVersion, other.previousVersion);
+ }
+}
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/service/WriteResult.java b/core/store/dist/src/main/java/org/onlab/onos/store/service/WriteResult.java
new file mode 100644
index 0000000..aec3046
--- /dev/null
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/service/WriteResult.java
@@ -0,0 +1,41 @@
+package org.onlab.onos.store.service;
+
+import com.google.common.base.MoreObjects;
+
+
+/**
+ * Database write result.
+ */
+public class WriteResult {
+
+ private final String tableName;
+ private final String key;
+ private final VersionedValue previousValue;
+
+ public WriteResult(String tableName, String key, VersionedValue previousValue) {
+ this.tableName = tableName;
+ this.key = key;
+ this.previousValue = previousValue;
+ }
+
+ public String tableName() {
+ return tableName;
+ }
+
+ public String key() {
+ return key;
+ }
+
+ public VersionedValue previousValue() {
+ return previousValue;
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(getClass())
+ .add("tableName", tableName)
+ .add("key", key)
+ .add("previousValue", previousValue)
+ .toString();
+ }
+}
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/service/impl/ClusterMessagingProtocol.java b/core/store/dist/src/main/java/org/onlab/onos/store/service/impl/ClusterMessagingProtocol.java
new file mode 100644
index 0000000..6de66bc
--- /dev/null
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/service/impl/ClusterMessagingProtocol.java
@@ -0,0 +1,180 @@
+package org.onlab.onos.store.service.impl;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.slf4j.LoggerFactory.getLogger;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.Vector;
+
+import net.kuujo.copycat.cluster.TcpClusterConfig;
+import net.kuujo.copycat.cluster.TcpMember;
+import net.kuujo.copycat.internal.log.ConfigurationEntry;
+import net.kuujo.copycat.internal.log.CopycatEntry;
+import net.kuujo.copycat.internal.log.OperationEntry;
+import net.kuujo.copycat.internal.log.SnapshotEntry;
+import net.kuujo.copycat.protocol.PingRequest;
+import net.kuujo.copycat.protocol.PingResponse;
+import net.kuujo.copycat.protocol.PollRequest;
+import net.kuujo.copycat.protocol.PollResponse;
+import net.kuujo.copycat.protocol.Response.Status;
+import net.kuujo.copycat.protocol.SubmitRequest;
+import net.kuujo.copycat.protocol.SubmitResponse;
+import net.kuujo.copycat.protocol.SyncRequest;
+import net.kuujo.copycat.protocol.SyncResponse;
+import net.kuujo.copycat.spi.protocol.Protocol;
+import net.kuujo.copycat.spi.protocol.ProtocolClient;
+import net.kuujo.copycat.spi.protocol.ProtocolServer;
+
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.apache.felix.scr.annotations.Service;
+import org.onlab.onos.cluster.ClusterService;
+import org.onlab.onos.cluster.ControllerNode;
+import org.onlab.onos.store.cluster.messaging.ClusterCommunicationService;
+import org.onlab.onos.store.cluster.messaging.MessageSubject;
+import org.onlab.onos.store.serializers.ImmutableListSerializer;
+import org.onlab.onos.store.serializers.ImmutableMapSerializer;
+import org.onlab.onos.store.serializers.ImmutableSetSerializer;
+import org.onlab.onos.store.serializers.KryoSerializer;
+import org.onlab.onos.store.service.ReadRequest;
+import org.onlab.onos.store.service.ReadResult;
+import org.onlab.onos.store.service.VersionedValue;
+import org.onlab.onos.store.service.WriteRequest;
+import org.onlab.onos.store.service.WriteResult;
+import org.onlab.util.KryoNamespace;
+import org.slf4j.Logger;
+
+import com.esotericsoftware.kryo.Kryo;
+import com.esotericsoftware.kryo.io.Input;
+import com.esotericsoftware.kryo.serializers.CollectionSerializer;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+
+/**
+ * ONOS Cluster messaging based Copycat protocol.
+ */
+@Component(immediate = true)
+@Service
+public class ClusterMessagingProtocol implements Protocol<TcpMember> {
+
+ private final Logger log = getLogger(getClass());
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected ClusterService clusterService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected ClusterCommunicationService clusterCommunicator;
+
+ public static final MessageSubject COPYCAT_PING =
+ new MessageSubject("copycat-raft-consensus-ping");
+ public static final MessageSubject COPYCAT_SYNC =
+ new MessageSubject("copycat-raft-consensus-sync");
+ public static final MessageSubject COPYCAT_POLL =
+ new MessageSubject("copycat-raft-consensus-poll");
+ public static final MessageSubject COPYCAT_SUBMIT =
+ new MessageSubject("copycat-raft-consensus-submit");
+
+ private static final KryoNamespace COPYCAT = KryoNamespace.newBuilder()
+ .register(PingRequest.class)
+ .register(PingResponse.class)
+ .register(PollRequest.class)
+ .register(PollResponse.class)
+ .register(SyncRequest.class)
+ .register(SyncResponse.class)
+ .register(SubmitRequest.class)
+ .register(SubmitResponse.class)
+ .register(Status.class)
+ .register(ConfigurationEntry.class)
+ .register(SnapshotEntry.class)
+ .register(CopycatEntry.class)
+ .register(OperationEntry.class)
+ .register(TcpClusterConfig.class)
+ .register(TcpMember.class)
+ .build();
+
+ private static final KryoNamespace DATABASE = KryoNamespace.newBuilder()
+ .register(ReadRequest.class)
+ .register(WriteRequest.class)
+ .register(InternalReadResult.class)
+ .register(InternalWriteResult.class)
+ .register(InternalReadResult.Status.class)
+ .register(WriteResult.class)
+ .register(ReadResult.class)
+ .register(InternalWriteResult.Status.class)
+ .register(VersionedValue.class)
+ .build();
+
+ public static final KryoNamespace COMMON = KryoNamespace.newBuilder()
+ .register(Arrays.asList().getClass(), new CollectionSerializer() {
+ @Override
+ @SuppressWarnings("rawtypes")
+ protected Collection<?> create(Kryo kryo, Input input, Class<Collection> type) {
+ return new ArrayList();
+ }
+ })
+ .register(ImmutableMap.class, new ImmutableMapSerializer())
+ .register(ImmutableList.class, new ImmutableListSerializer())
+ .register(ImmutableSet.class, new ImmutableSetSerializer())
+ .register(
+ Vector.class,
+ ArrayList.class,
+ Arrays.asList().getClass(),
+ HashMap.class,
+ HashSet.class,
+ LinkedList.class,
+ byte[].class)
+ .build();
+
+ public static final KryoSerializer SERIALIZER = new KryoSerializer() {
+ @Override
+ protected void setupKryoPool() {
+ serializerPool = KryoNamespace.newBuilder()
+ .register(COPYCAT)
+ .register(COMMON)
+ .register(DATABASE)
+ .build()
+ .populate(1);
+ }
+ };
+
+ @Activate
+ public void activate() {
+ log.info("Started.");
+ }
+
+ @Deactivate
+ public void deactivate() {
+ log.info("Stopped.");
+ }
+
+ @Override
+ public ProtocolServer createServer(TcpMember member) {
+ return new ClusterMessagingProtocolServer(clusterCommunicator);
+ }
+
+ @Override
+ public ProtocolClient createClient(TcpMember member) {
+ ControllerNode node = getControllerNode(member.host(), member.port());
+ checkNotNull(node, "A valid controller node is expected");
+ return new ClusterMessagingProtocolClient(
+ clusterCommunicator, node);
+ }
+
+ private ControllerNode getControllerNode(String host, int port) {
+ for (ControllerNode node : clusterService.getNodes()) {
+ if (node.ip().toString().equals(host) && node.tcpPort() == port) {
+ return node;
+ }
+ }
+ return null;
+ }
+}
\ No newline at end of file
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/service/impl/ClusterMessagingProtocolClient.java b/core/store/dist/src/main/java/org/onlab/onos/store/service/impl/ClusterMessagingProtocolClient.java
new file mode 100644
index 0000000..0c2aacd
--- /dev/null
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/service/impl/ClusterMessagingProtocolClient.java
@@ -0,0 +1,149 @@
+package org.onlab.onos.store.service.impl;
+
+import static org.slf4j.LoggerFactory.getLogger;
+
+import java.io.IOException;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledThreadPoolExecutor;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+import net.kuujo.copycat.protocol.PingRequest;
+import net.kuujo.copycat.protocol.PingResponse;
+import net.kuujo.copycat.protocol.PollRequest;
+import net.kuujo.copycat.protocol.PollResponse;
+import net.kuujo.copycat.protocol.SubmitRequest;
+import net.kuujo.copycat.protocol.SubmitResponse;
+import net.kuujo.copycat.protocol.SyncRequest;
+import net.kuujo.copycat.protocol.SyncResponse;
+import net.kuujo.copycat.spi.protocol.ProtocolClient;
+
+import org.onlab.onos.cluster.ControllerNode;
+import org.onlab.onos.store.cluster.messaging.ClusterCommunicationService;
+import org.onlab.onos.store.cluster.messaging.ClusterMessage;
+import org.onlab.onos.store.cluster.messaging.MessageSubject;
+import org.slf4j.Logger;
+
+import com.google.common.util.concurrent.ThreadFactoryBuilder;
+
+/**
+ * ONOS Cluster messaging based Copycat protocol client.
+ */
+public class ClusterMessagingProtocolClient implements ProtocolClient {
+
+ private final Logger log = getLogger(getClass());
+
+ private static final ThreadFactory THREAD_FACTORY =
+ new ThreadFactoryBuilder().setNameFormat("copycat-netty-messaging-%d").build();
+
+ public static final long RETRY_INTERVAL_MILLIS = 2000;
+
+ private final ClusterCommunicationService clusterCommunicator;
+ private final ControllerNode remoteNode;
+
+ // FIXME: Thread pool sizing.
+ private static final ScheduledExecutorService THREAD_POOL =
+ new ScheduledThreadPoolExecutor(10, THREAD_FACTORY);
+
+ public ClusterMessagingProtocolClient(
+ ClusterCommunicationService clusterCommunicator,
+ ControllerNode remoteNode) {
+ this.clusterCommunicator = clusterCommunicator;
+ this.remoteNode = remoteNode;
+ }
+
+ @Override
+ public CompletableFuture<PingResponse> ping(PingRequest request) {
+ return requestReply(request);
+ }
+
+ @Override
+ public CompletableFuture<SyncResponse> sync(SyncRequest request) {
+ return requestReply(request);
+ }
+
+ @Override
+ public CompletableFuture<PollResponse> poll(PollRequest request) {
+ return requestReply(request);
+ }
+
+ @Override
+ public CompletableFuture<SubmitResponse> submit(SubmitRequest request) {
+ return requestReply(request);
+ }
+
+ @Override
+ public CompletableFuture<Void> connect() {
+ return CompletableFuture.completedFuture(null);
+ }
+
+ @Override
+ public CompletableFuture<Void> close() {
+ return CompletableFuture.completedFuture(null);
+ }
+
+ public <I> MessageSubject messageType(I input) {
+ Class<?> clazz = input.getClass();
+ if (clazz.equals(PollRequest.class)) {
+ return ClusterMessagingProtocol.COPYCAT_POLL;
+ } else if (clazz.equals(SyncRequest.class)) {
+ return ClusterMessagingProtocol.COPYCAT_SYNC;
+ } else if (clazz.equals(SubmitRequest.class)) {
+ return ClusterMessagingProtocol.COPYCAT_SUBMIT;
+ } else if (clazz.equals(PingRequest.class)) {
+ return ClusterMessagingProtocol.COPYCAT_PING;
+ } else {
+ throw new IllegalArgumentException("Unknown class " + clazz.getName());
+ }
+
+ }
+
+ private <I, O> CompletableFuture<O> requestReply(I request) {
+ CompletableFuture<O> future = new CompletableFuture<>();
+ THREAD_POOL.schedule(new RPCTask<I, O>(request, future), 0, TimeUnit.MILLISECONDS);
+ return future;
+ }
+
+ private class RPCTask<I, O> implements Runnable {
+
+ private final ClusterMessage message;
+ private final CompletableFuture<O> future;
+
+ public RPCTask(I request, CompletableFuture<O> future) {
+ this.message =
+ new ClusterMessage(
+ null,
+ messageType(request),
+ ClusterMessagingProtocol.SERIALIZER.encode(request));
+ this.future = future;
+ }
+
+ @Override
+ public void run() {
+ try {
+ byte[] response = clusterCommunicator
+ .sendAndReceive(message, remoteNode.id())
+ .get(RETRY_INTERVAL_MILLIS, TimeUnit.MILLISECONDS);
+ future.complete(ClusterMessagingProtocol.SERIALIZER.decode(response));
+
+ } catch (IOException | InterruptedException | ExecutionException | TimeoutException e) {
+ if (message.subject().equals(ClusterMessagingProtocol.COPYCAT_SYNC) ||
+ message.subject().equals(ClusterMessagingProtocol.COPYCAT_PING)) {
+ log.warn("Request to {} failed. Will retry "
+ + "in {} ms", remoteNode, RETRY_INTERVAL_MILLIS);
+ THREAD_POOL.schedule(
+ this,
+ RETRY_INTERVAL_MILLIS,
+ TimeUnit.MILLISECONDS);
+ } else {
+ future.completeExceptionally(e);
+ }
+ } catch (Exception e) {
+ future.completeExceptionally(e);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/service/impl/ClusterMessagingProtocolServer.java b/core/store/dist/src/main/java/org/onlab/onos/store/service/impl/ClusterMessagingProtocolServer.java
new file mode 100644
index 0000000..7d94847
--- /dev/null
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/service/impl/ClusterMessagingProtocolServer.java
@@ -0,0 +1,94 @@
+package org.onlab.onos.store.service.impl;
+
+import static org.slf4j.LoggerFactory.getLogger;
+
+import java.util.concurrent.CompletableFuture;
+
+import net.kuujo.copycat.protocol.PingRequest;
+import net.kuujo.copycat.protocol.PollRequest;
+import net.kuujo.copycat.protocol.RequestHandler;
+import net.kuujo.copycat.protocol.SubmitRequest;
+import net.kuujo.copycat.protocol.SyncRequest;
+import net.kuujo.copycat.spi.protocol.ProtocolServer;
+
+import org.onlab.onos.store.cluster.messaging.ClusterCommunicationService;
+import org.onlab.onos.store.cluster.messaging.ClusterMessage;
+import org.onlab.onos.store.cluster.messaging.ClusterMessageHandler;
+import org.slf4j.Logger;
+
+/**
+ * ONOS Cluster messaging based Copycat protocol server.
+ */
+public class ClusterMessagingProtocolServer implements ProtocolServer {
+
+ private final Logger log = getLogger(getClass());
+ private RequestHandler handler;
+
+ public ClusterMessagingProtocolServer(ClusterCommunicationService clusterCommunicator) {
+
+ clusterCommunicator.addSubscriber(
+ ClusterMessagingProtocol.COPYCAT_PING, new CopycatMessageHandler<PingRequest>());
+ clusterCommunicator.addSubscriber(
+ ClusterMessagingProtocol.COPYCAT_SYNC, new CopycatMessageHandler<SyncRequest>());
+ clusterCommunicator.addSubscriber(
+ ClusterMessagingProtocol.COPYCAT_POLL, new CopycatMessageHandler<PollRequest>());
+ clusterCommunicator.addSubscriber(
+ ClusterMessagingProtocol.COPYCAT_SUBMIT, new CopycatMessageHandler<SubmitRequest>());
+ }
+
+ @Override
+ public void requestHandler(RequestHandler handler) {
+ this.handler = handler;
+ }
+
+ @Override
+ public CompletableFuture<Void> listen() {
+ return CompletableFuture.completedFuture(null);
+ }
+
+ @Override
+ public CompletableFuture<Void> close() {
+ return CompletableFuture.completedFuture(null);
+ }
+
+ private class CopycatMessageHandler<T> implements ClusterMessageHandler {
+
+ @Override
+ public void handle(ClusterMessage message) {
+ T request = ClusterMessagingProtocol.SERIALIZER.decode(message.payload());
+ if (request.getClass().equals(PingRequest.class)) {
+ handler.ping((PingRequest) request).whenComplete((response, error) -> {
+ try {
+ message.respond(ClusterMessagingProtocol.SERIALIZER.encode(response));
+ } catch (Exception e) {
+ log.error("Failed to respond to ping request", e);
+ }
+ });
+ } else if (request.getClass().equals(PollRequest.class)) {
+ handler.poll((PollRequest) request).whenComplete((response, error) -> {
+ try {
+ message.respond(ClusterMessagingProtocol.SERIALIZER.encode(response));
+ } catch (Exception e) {
+ log.error("Failed to respond to poll request", e);
+ }
+ });
+ } else if (request.getClass().equals(SyncRequest.class)) {
+ handler.sync((SyncRequest) request).whenComplete((response, error) -> {
+ try {
+ message.respond(ClusterMessagingProtocol.SERIALIZER.encode(response));
+ } catch (Exception e) {
+ log.error("Failed to respond to sync request", e);
+ }
+ });
+ } else if (request.getClass().equals(SubmitRequest.class)) {
+ handler.submit((SubmitRequest) request).whenComplete((response, error) -> {
+ try {
+ message.respond(ClusterMessagingProtocol.SERIALIZER.encode(response));
+ } catch (Exception e) {
+ log.error("Failed to respond to submit request", e);
+ }
+ });
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/service/impl/DatabaseClient.java b/core/store/dist/src/main/java/org/onlab/onos/store/service/impl/DatabaseClient.java
new file mode 100644
index 0000000..8af567e
--- /dev/null
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/service/impl/DatabaseClient.java
@@ -0,0 +1,132 @@
+package org.onlab.onos.store.service.impl;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.UUID;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+
+import net.kuujo.copycat.protocol.Response.Status;
+import net.kuujo.copycat.protocol.SubmitRequest;
+import net.kuujo.copycat.protocol.SubmitResponse;
+import net.kuujo.copycat.spi.protocol.ProtocolClient;
+
+import org.onlab.onos.store.service.DatabaseException;
+import org.onlab.onos.store.service.ReadRequest;
+import org.onlab.onos.store.service.WriteRequest;
+
+/**
+ * Client for interacting with the Copycat Raft cluster.
+ */
+public class DatabaseClient {
+
+ private final ProtocolClient client;
+
+ public DatabaseClient(ProtocolClient client) {
+ this.client = client;
+ }
+
+ private static String nextId() {
+ return UUID.randomUUID().toString();
+ }
+
+ public boolean createTable(String tableName) {
+
+ SubmitRequest request =
+ new SubmitRequest(
+ nextId(),
+ "createTable",
+ Arrays.asList(tableName));
+ CompletableFuture<SubmitResponse> future = client.submit(request);
+ try {
+ return (boolean) future.get().result();
+ } catch (InterruptedException | ExecutionException e) {
+ throw new DatabaseException(e);
+ }
+ }
+
+ public void dropTable(String tableName) {
+
+ SubmitRequest request =
+ new SubmitRequest(
+ nextId(),
+ "dropTable",
+ Arrays.asList(tableName));
+ CompletableFuture<SubmitResponse> future = client.submit(request);
+ try {
+ if (future.get().status() == Status.OK) {
+ throw new DatabaseException(future.get().toString());
+ }
+
+ } catch (InterruptedException | ExecutionException e) {
+ throw new DatabaseException(e);
+ }
+ }
+
+ public void dropAllTables() {
+
+ SubmitRequest request =
+ new SubmitRequest(
+ nextId(),
+ "dropAllTables",
+ Arrays.asList());
+ CompletableFuture<SubmitResponse> future = client.submit(request);
+ try {
+ if (future.get().status() != Status.OK) {
+ throw new DatabaseException(future.get().toString());
+ }
+ } catch (InterruptedException | ExecutionException e) {
+ throw new DatabaseException(e);
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ public List<String> listTables() {
+
+ SubmitRequest request =
+ new SubmitRequest(
+ nextId(),
+ "listTables",
+ Arrays.asList());
+ CompletableFuture<SubmitResponse> future = client.submit(request);
+ try {
+ return (List<String>) future.get().result();
+ } catch (InterruptedException | ExecutionException e) {
+ throw new DatabaseException(e);
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ public List<InternalReadResult> batchRead(List<ReadRequest> requests) {
+
+ SubmitRequest request = new SubmitRequest(
+ nextId(),
+ "read",
+ Arrays.asList(requests));
+
+ CompletableFuture<SubmitResponse> future = client.submit(request);
+ try {
+ List<InternalReadResult> internalReadResults = (List<InternalReadResult>) future.get().result();
+ return internalReadResults;
+ } catch (InterruptedException | ExecutionException e) {
+ throw new DatabaseException(e);
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ public List<InternalWriteResult> batchWrite(List<WriteRequest> requests) {
+
+ SubmitRequest request = new SubmitRequest(
+ nextId(),
+ "write",
+ Arrays.asList(requests));
+
+ CompletableFuture<SubmitResponse> future = client.submit(request);
+ try {
+ List<InternalWriteResult> internalWriteResults = (List<InternalWriteResult>) future.get().result();
+ return internalWriteResults;
+ } catch (InterruptedException | ExecutionException e) {
+ throw new DatabaseException(e);
+ }
+ }
+}
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/service/impl/DatabaseManager.java b/core/store/dist/src/main/java/org/onlab/onos/store/service/impl/DatabaseManager.java
new file mode 100644
index 0000000..7db4bc7
--- /dev/null
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/service/impl/DatabaseManager.java
@@ -0,0 +1,217 @@
+package org.onlab.onos.store.service.impl;
+
+import static org.slf4j.LoggerFactory.getLogger;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import net.kuujo.copycat.Copycat;
+import net.kuujo.copycat.StateMachine;
+import net.kuujo.copycat.cluster.TcpCluster;
+import net.kuujo.copycat.cluster.TcpClusterConfig;
+import net.kuujo.copycat.cluster.TcpMember;
+import net.kuujo.copycat.log.ChronicleLog;
+import net.kuujo.copycat.log.Log;
+
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.apache.felix.scr.annotations.Service;
+import org.onlab.onos.cluster.ClusterService;
+import org.onlab.onos.cluster.ControllerNode;
+import org.onlab.onos.store.service.DatabaseAdminService;
+import org.onlab.onos.store.service.DatabaseException;
+import org.onlab.onos.store.service.DatabaseService;
+import org.onlab.onos.store.service.NoSuchTableException;
+import org.onlab.onos.store.service.OptimisticLockException;
+import org.onlab.onos.store.service.OptionalResult;
+import org.onlab.onos.store.service.PreconditionFailedException;
+import org.onlab.onos.store.service.ReadRequest;
+import org.onlab.onos.store.service.ReadResult;
+import org.onlab.onos.store.service.WriteAborted;
+import org.onlab.onos.store.service.WriteRequest;
+import org.onlab.onos.store.service.WriteResult;
+import org.slf4j.Logger;
+
+import com.google.common.collect.Lists;
+
+/**
+ * Strongly consistent and durable state management service based on
+ * Copycat implementation of Raft consensus protocol.
+ */
+@Component(immediate = true)
+@Service
+public class DatabaseManager implements DatabaseService, DatabaseAdminService {
+
+ private final Logger log = getLogger(getClass());
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected ClusterService clusterService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected ClusterMessagingProtocol copycatMessagingProtocol;
+
+ public static final String LOG_FILE_PREFIX = "onos-copy-cat-log";
+
+ private Copycat copycat;
+ private DatabaseClient client;
+
+ @Activate
+ public void activate() {
+
+ // TODO: Not every node can be part of the consensus ring.
+
+ TcpMember localMember =
+ new TcpMember(
+ clusterService.getLocalNode().ip().toString(),
+ clusterService.getLocalNode().tcpPort());
+ List<TcpMember> remoteMembers = Lists.newArrayList();
+
+ for (ControllerNode node : clusterService.getNodes()) {
+ TcpMember member = new TcpMember(node.ip().toString(), node.tcpPort());
+ if (!member.equals(localMember)) {
+ remoteMembers.add(member);
+ }
+ }
+
+ // Configure the cluster.
+ TcpClusterConfig config = new TcpClusterConfig();
+
+ config.setLocalMember(localMember);
+ config.setRemoteMembers(remoteMembers.toArray(new TcpMember[]{}));
+
+ // Create the cluster.
+ TcpCluster cluster = new TcpCluster(config);
+
+ StateMachine stateMachine = new DatabaseStateMachine();
+ ControllerNode thisNode = clusterService.getLocalNode();
+ Log consensusLog = new ChronicleLog(LOG_FILE_PREFIX + "_" + thisNode.id());
+
+ copycat = new Copycat(stateMachine, consensusLog, cluster, copycatMessagingProtocol);
+ copycat.start();
+
+ client = new DatabaseClient(copycatMessagingProtocol.createClient(localMember));
+
+ log.info("Started.");
+ }
+
+ @Deactivate
+ public void deactivate() {
+ copycat.stop();
+ log.info("Stopped.");
+ }
+
+ @Override
+ public boolean createTable(String name) {
+ return client.createTable(name);
+ }
+
+ @Override
+ public void dropTable(String name) {
+ client.dropTable(name);
+ }
+
+ @Override
+ public void dropAllTables() {
+ client.dropAllTables();
+ }
+
+ @Override
+ public List<String> listTables() {
+ return client.listTables();
+ }
+
+ @Override
+ public ReadResult read(ReadRequest request) {
+ return batchRead(Arrays.asList(request)).get(0).get();
+ }
+
+ @Override
+ public List<OptionalResult<ReadResult, DatabaseException>> batchRead(
+ List<ReadRequest> batch) {
+ List<OptionalResult<ReadResult, DatabaseException>> readResults = new ArrayList<>(batch.size());
+ for (InternalReadResult internalReadResult : client.batchRead(batch)) {
+ if (internalReadResult.status() == InternalReadResult.Status.NO_SUCH_TABLE) {
+ readResults.add(new DatabaseOperationResult<ReadResult, DatabaseException>(
+ new NoSuchTableException()));
+ } else {
+ readResults.add(new DatabaseOperationResult<ReadResult, DatabaseException>(
+ internalReadResult.result()));
+ }
+ }
+ return readResults;
+ }
+
+ @Override
+ public WriteResult write(WriteRequest request) {
+ return batchWrite(Arrays.asList(request)).get(0).get();
+ }
+
+ @Override
+ public List<OptionalResult<WriteResult, DatabaseException>> batchWrite(
+ List<WriteRequest> batch) {
+ List<OptionalResult<WriteResult, DatabaseException>> writeResults = new ArrayList<>(batch.size());
+ for (InternalWriteResult internalWriteResult : client.batchWrite(batch)) {
+ if (internalWriteResult.status() == InternalWriteResult.Status.NO_SUCH_TABLE) {
+ writeResults.add(new DatabaseOperationResult<WriteResult, DatabaseException>(
+ new NoSuchTableException()));
+ } else if (internalWriteResult.status() == InternalWriteResult.Status.OPTIMISTIC_LOCK_FAILURE) {
+ writeResults.add(new DatabaseOperationResult<WriteResult, DatabaseException>(
+ new OptimisticLockException()));
+ } else if (internalWriteResult.status() == InternalWriteResult.Status.PREVIOUS_VALUE_MISMATCH) {
+ // TODO: throw a different exception?
+ writeResults.add(new DatabaseOperationResult<WriteResult, DatabaseException>(
+ new PreconditionFailedException()));
+ } else if (internalWriteResult.status() == InternalWriteResult.Status.ABORTED) {
+ writeResults.add(new DatabaseOperationResult<WriteResult, DatabaseException>(
+ new WriteAborted()));
+ } else {
+ writeResults.add(new DatabaseOperationResult<WriteResult, DatabaseException>(
+ internalWriteResult.result()));
+ }
+ }
+ return writeResults;
+
+ }
+
+ private class DatabaseOperationResult<R, E extends DatabaseException> implements OptionalResult<R, E> {
+
+ private final R result;
+ private final DatabaseException exception;
+
+ public DatabaseOperationResult(R result) {
+ this.result = result;
+ this.exception = null;
+ }
+
+ public DatabaseOperationResult(DatabaseException exception) {
+ this.result = null;
+ this.exception = exception;
+ }
+
+ @Override
+ public R get() {
+ if (result != null) {
+ return result;
+ }
+ throw exception;
+ }
+
+ @Override
+ public boolean hasValidResult() {
+ return result != null;
+ }
+
+ @Override
+ public String toString() {
+ if (result != null) {
+ return result.toString();
+ } else {
+ return exception.toString();
+ }
+ }
+ }
+}
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/service/impl/DatabaseStateMachine.java b/core/store/dist/src/main/java/org/onlab/onos/store/service/impl/DatabaseStateMachine.java
new file mode 100644
index 0000000..ad6773e
--- /dev/null
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/service/impl/DatabaseStateMachine.java
@@ -0,0 +1,177 @@
+package org.onlab.onos.store.service.impl;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import net.kuujo.copycat.Command;
+import net.kuujo.copycat.Query;
+import net.kuujo.copycat.StateMachine;
+
+import org.onlab.onos.store.serializers.KryoSerializer;
+import org.onlab.onos.store.service.ReadRequest;
+import org.onlab.onos.store.service.ReadResult;
+import org.onlab.onos.store.service.VersionedValue;
+import org.onlab.onos.store.service.WriteRequest;
+import org.onlab.onos.store.service.WriteResult;
+import org.onlab.util.KryoNamespace;
+
+import com.google.common.collect.Maps;
+
+/**
+ * StateMachine whose transitions are coordinated/replicated
+ * by Raft consensus.
+ * Each Raft cluster member has a instance of this state machine that is
+ * independently updated in lock step once there is consensus
+ * on the next transition.
+ */
+public class DatabaseStateMachine implements StateMachine {
+
+ public static final KryoSerializer SERIALIZER = new KryoSerializer() {
+ @Override
+ protected void setupKryoPool() {
+ serializerPool = KryoNamespace.newBuilder()
+ .register(VersionedValue.class)
+ .register(State.class)
+ .register(ClusterMessagingProtocol.COMMON)
+ .build()
+ .populate(1);
+ }
+ };
+
+ private State state = new State();
+
+ @Command
+ public boolean createTable(String tableName) {
+ return state.getTables().putIfAbsent(tableName, Maps.newHashMap()) == null;
+ }
+
+ @Command
+ public boolean dropTable(String tableName) {
+ return state.getTables().remove(tableName) != null;
+ }
+
+ @Command
+ public boolean dropAllTables() {
+ state.getTables().clear();
+ return true;
+ }
+
+ @Query
+ public Set<String> listTables() {
+ return state.getTables().keySet();
+ }
+
+ @Query
+ public List<InternalReadResult> read(List<ReadRequest> requests) {
+ List<InternalReadResult> results = new ArrayList<>(requests.size());
+ for (ReadRequest request : requests) {
+ Map<String, VersionedValue> table = state.getTables().get(request.tableName());
+ if (table == null) {
+ results.add(new InternalReadResult(InternalReadResult.Status.NO_SUCH_TABLE, null));
+ continue;
+ }
+ VersionedValue value = table.get(request.key());
+ results.add(new InternalReadResult(
+ InternalReadResult.Status.OK,
+ new ReadResult(
+ request.tableName(),
+ request.key(),
+ value)));
+ }
+ return results;
+ }
+
+ @Command
+ public List<InternalWriteResult> write(List<WriteRequest> requests) {
+ boolean abort = false;
+ List<InternalWriteResult.Status> validationResults = new ArrayList<>(requests.size());
+ for (WriteRequest request : requests) {
+ Map<String, VersionedValue> table = state.getTables().get(request.tableName());
+ if (table == null) {
+ validationResults.add(InternalWriteResult.Status.NO_SUCH_TABLE);
+ abort = true;
+ continue;
+ }
+ VersionedValue value = table.get(request.key());
+ if (value == null) {
+ if (request.oldValue() != null) {
+ validationResults.add(InternalWriteResult.Status.PREVIOUS_VALUE_MISMATCH);
+ abort = true;
+ continue;
+ } else if (request.previousVersion() >= 0) {
+ validationResults.add(InternalWriteResult.Status.OPTIMISTIC_LOCK_FAILURE);
+ abort = true;
+ continue;
+ }
+ }
+ if (request.previousVersion() >= 0 && value.version() != request.previousVersion()) {
+ validationResults.add(InternalWriteResult.Status.OPTIMISTIC_LOCK_FAILURE);
+ abort = true;
+ continue;
+ }
+
+ validationResults.add(InternalWriteResult.Status.OK);
+ }
+
+ List<InternalWriteResult> results = new ArrayList<>(requests.size());
+
+ if (abort) {
+ for (InternalWriteResult.Status validationResult : validationResults) {
+ if (validationResult == InternalWriteResult.Status.OK) {
+ results.add(new InternalWriteResult(InternalWriteResult.Status.ABORTED, null));
+ } else {
+ results.add(new InternalWriteResult(validationResult, null));
+ }
+ }
+ return results;
+ }
+
+ for (WriteRequest request : requests) {
+ Map<String, VersionedValue> table = state.getTables().get(request.tableName());
+ synchronized (table) {
+ VersionedValue previousValue =
+ table.put(request.key(), new VersionedValue(request.newValue(), state.nextVersion()));
+ results.add(new InternalWriteResult(
+ InternalWriteResult.Status.OK,
+ new WriteResult(request.tableName(), request.key(), previousValue)));
+ }
+ }
+ return results;
+ }
+
+ public class State {
+
+ private final Map<String, Map<String, VersionedValue>> tables =
+ Maps.newHashMap();
+ private long versionCounter = 1;
+
+ Map<String, Map<String, VersionedValue>> getTables() {
+ return tables;
+ }
+
+ long nextVersion() {
+ return versionCounter++;
+ }
+ }
+
+ @Override
+ public byte[] takeSnapshot() {
+ try {
+ return SERIALIZER.encode(state);
+ } catch (Exception e) {
+ e.printStackTrace();
+ return null;
+ }
+ }
+
+ @Override
+ public void installSnapshot(byte[] data) {
+ try {
+ this.state = SERIALIZER.decode(data);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/service/impl/InternalReadResult.java b/core/store/dist/src/main/java/org/onlab/onos/store/service/impl/InternalReadResult.java
new file mode 100644
index 0000000..aadbcfb
--- /dev/null
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/service/impl/InternalReadResult.java
@@ -0,0 +1,36 @@
+package org.onlab.onos.store.service.impl;
+
+import org.onlab.onos.store.service.ReadResult;
+
+/**
+ * Result of a read operation executed on the DatabaseStateMachine.
+ */
+public class InternalReadResult {
+
+ public enum Status {
+ OK,
+ NO_SUCH_TABLE
+ }
+
+ private final Status status;
+ private final ReadResult result;
+
+ public InternalReadResult(Status status, ReadResult result) {
+ this.status = status;
+ this.result = result;
+ }
+
+ public Status status() {
+ return status;
+ }
+
+ public ReadResult result() {
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "InternalReadResult [status=" + status + ", result=" + result
+ + "]";
+ }
+}
\ No newline at end of file
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/service/impl/InternalWriteResult.java b/core/store/dist/src/main/java/org/onlab/onos/store/service/impl/InternalWriteResult.java
new file mode 100644
index 0000000..d757dfb
--- /dev/null
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/service/impl/InternalWriteResult.java
@@ -0,0 +1,33 @@
+package org.onlab.onos.store.service.impl;
+
+import org.onlab.onos.store.service.WriteResult;
+
+/**
+ * Result of a write operation executed on the DatabaseStateMachine.
+ */
+public class InternalWriteResult {
+
+ public enum Status {
+ OK,
+ ABORTED,
+ NO_SUCH_TABLE,
+ OPTIMISTIC_LOCK_FAILURE,
+ PREVIOUS_VALUE_MISMATCH
+ }
+
+ private final Status status;
+ private final WriteResult result;
+
+ public InternalWriteResult(Status status, WriteResult result) {
+ this.status = status;
+ this.result = result;
+ }
+
+ public Status status() {
+ return status;
+ }
+
+ public WriteResult result() {
+ return result;
+ }
+}
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/service/impl/package-info.java b/core/store/dist/src/main/java/org/onlab/onos/store/service/impl/package-info.java
new file mode 100644
index 0000000..f9fd80f
--- /dev/null
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/service/impl/package-info.java
@@ -0,0 +1,5 @@
+/**
+ * Strongly consistent, fault-tolerant and durable state management
+ * based on Raft consensus protocol.
+ */
+package org.onlab.onos.store.service.impl;
\ No newline at end of file
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/service/package-info.java b/core/store/dist/src/main/java/org/onlab/onos/store/service/package-info.java
new file mode 100644
index 0000000..d630870
--- /dev/null
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/service/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * Distributed core state management services.
+ */
+package org.onlab.onos.store.service;
\ No newline at end of file
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/topology/impl/DefaultTopology.java b/core/store/dist/src/main/java/org/onlab/onos/store/topology/impl/DefaultTopology.java
index 6fc2360e..f2e07f1 100644
--- a/core/store/dist/src/main/java/org/onlab/onos/store/topology/impl/DefaultTopology.java
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/topology/impl/DefaultTopology.java
@@ -248,8 +248,9 @@
* Computes on-demand the set of shortest paths between source and
* destination devices.
*
- * @param src source device
- * @param dst destination device
+ * @param src source device
+ * @param dst destination device
+ * @param weight link weight function
* @return set of shortest paths
*/
Set<Path> getPaths(DeviceId src, DeviceId dst, LinkWeight weight) {
diff --git a/core/store/dist/src/test/java/org/onlab/onos/store/cluster/StaticClusterService.java b/core/store/dist/src/test/java/org/onlab/onos/store/cluster/StaticClusterService.java
new file mode 100644
index 0000000..6f34d32
--- /dev/null
+++ b/core/store/dist/src/test/java/org/onlab/onos/store/cluster/StaticClusterService.java
@@ -0,0 +1,48 @@
+package org.onlab.onos.store.cluster;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+import org.onlab.onos.cluster.ClusterEventListener;
+import org.onlab.onos.cluster.ClusterService;
+import org.onlab.onos.cluster.ControllerNode;
+import org.onlab.onos.cluster.NodeId;
+import org.onlab.onos.cluster.ControllerNode.State;
+
+import com.google.common.collect.Sets;
+
+public abstract class StaticClusterService implements ClusterService {
+
+ protected final Map<NodeId, ControllerNode> nodes = new HashMap<>();
+ protected final Map<NodeId, ControllerNode.State> nodeStates = new HashMap<>();
+ protected ControllerNode localNode;
+
+ @Override
+ public ControllerNode getLocalNode() {
+ return localNode;
+ }
+
+ @Override
+ public Set<ControllerNode> getNodes() {
+ return Sets.newHashSet(nodes.values());
+ }
+
+ @Override
+ public ControllerNode getNode(NodeId nodeId) {
+ return nodes.get(nodeId);
+ }
+
+ @Override
+ public State getState(NodeId nodeId) {
+ return nodeStates.get(nodeId);
+ }
+
+ @Override
+ public void addListener(ClusterEventListener listener) {
+ }
+
+ @Override
+ public void removeListener(ClusterEventListener listener) {
+ }
+}
\ No newline at end of file
diff --git a/core/store/dist/src/test/java/org/onlab/onos/store/device/impl/GossipDeviceStoreTest.java b/core/store/dist/src/test/java/org/onlab/onos/store/device/impl/GossipDeviceStoreTest.java
index 33b3c14..ccebb72 100644
--- a/core/store/dist/src/test/java/org/onlab/onos/store/device/impl/GossipDeviceStoreTest.java
+++ b/core/store/dist/src/test/java/org/onlab/onos/store/device/impl/GossipDeviceStoreTest.java
@@ -41,10 +41,8 @@
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test;
-import org.onlab.onos.cluster.ClusterEventListener;
import org.onlab.onos.cluster.ClusterService;
import org.onlab.onos.cluster.ControllerNode;
-import org.onlab.onos.cluster.ControllerNode.State;
import org.onlab.onos.cluster.DefaultControllerNode;
import org.onlab.onos.cluster.NodeId;
import org.onlab.onos.mastership.MastershipServiceAdapter;
@@ -65,6 +63,7 @@
import org.onlab.onos.net.device.DeviceStoreDelegate;
import org.onlab.onos.net.device.PortDescription;
import org.onlab.onos.net.provider.ProviderId;
+import org.onlab.onos.store.cluster.StaticClusterService;
import org.onlab.onos.store.cluster.messaging.ClusterCommunicationService;
import org.onlab.onos.store.cluster.messaging.ClusterMessage;
import org.onlab.onos.store.cluster.messaging.ClusterMessageHandler;
@@ -133,6 +132,7 @@
private DeviceClockManager deviceClockManager;
private DeviceClockService deviceClockService;
private ClusterCommunicationService clusterCommunicator;
+
@BeforeClass
public static void setUpBeforeClass() throws Exception {
}
@@ -838,45 +838,15 @@
}
}
- private static final class TestClusterService implements ClusterService {
-
- private final Map<NodeId, ControllerNode> nodes = new HashMap<>();
- private final Map<NodeId, ControllerNode.State> nodeStates = new HashMap<>();
+ private static final class TestClusterService extends StaticClusterService {
public TestClusterService() {
+ localNode = ONOS1;
nodes.put(NID1, ONOS1);
nodeStates.put(NID1, ACTIVE);
nodes.put(NID2, ONOS2);
nodeStates.put(NID2, ACTIVE);
}
-
- @Override
- public ControllerNode getLocalNode() {
- return GossipDeviceStoreTest.ONOS1;
- }
-
- @Override
- public Set<ControllerNode> getNodes() {
- return Sets.newHashSet(nodes.values());
- }
-
- @Override
- public ControllerNode getNode(NodeId nodeId) {
- return nodes.get(nodeId);
- }
-
- @Override
- public State getState(NodeId nodeId) {
- return nodeStates.get(nodeId);
- }
-
- @Override
- public void addListener(ClusterEventListener listener) {
- }
-
- @Override
- public void removeListener(ClusterEventListener listener) {
- }
}
}
diff --git a/core/store/dist/src/test/java/org/onlab/onos/store/link/impl/GossipLinkStoreTest.java b/core/store/dist/src/test/java/org/onlab/onos/store/link/impl/GossipLinkStoreTest.java
new file mode 100644
index 0000000..d62d0bb
--- /dev/null
+++ b/core/store/dist/src/test/java/org/onlab/onos/store/link/impl/GossipLinkStoreTest.java
@@ -0,0 +1,618 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onlab.onos.store.link.impl;
+
+import com.google.common.collect.Iterables;
+
+import org.easymock.Capture;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.onlab.onos.cluster.ControllerNode;
+import org.onlab.onos.cluster.DefaultControllerNode;
+import org.onlab.onos.cluster.NodeId;
+import org.onlab.onos.mastership.MastershipTerm;
+import org.onlab.onos.net.ConnectPoint;
+import org.onlab.onos.net.DefaultAnnotations;
+import org.onlab.onos.net.DeviceId;
+import org.onlab.onos.net.Link;
+import org.onlab.onos.net.Link.Type;
+import org.onlab.onos.net.LinkKey;
+import org.onlab.onos.net.PortNumber;
+import org.onlab.onos.net.SparseAnnotations;
+import org.onlab.onos.net.device.DeviceClockService;
+import org.onlab.onos.net.link.DefaultLinkDescription;
+import org.onlab.onos.net.link.LinkDescription;
+import org.onlab.onos.net.link.LinkEvent;
+import org.onlab.onos.net.link.LinkStore;
+import org.onlab.onos.net.link.LinkStoreDelegate;
+import org.onlab.onos.net.provider.ProviderId;
+import org.onlab.onos.store.cluster.StaticClusterService;
+import org.onlab.onos.store.cluster.messaging.ClusterCommunicationService;
+import org.onlab.onos.store.cluster.messaging.ClusterMessage;
+import org.onlab.onos.store.cluster.messaging.ClusterMessageHandler;
+import org.onlab.onos.store.cluster.messaging.MessageSubject;
+import org.onlab.onos.store.device.impl.DeviceClockManager;
+import org.onlab.packet.IpAddress;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import static org.easymock.EasyMock.anyObject;
+import static org.easymock.EasyMock.capture;
+import static org.easymock.EasyMock.createNiceMock;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.expectLastCall;
+import static org.easymock.EasyMock.replay;
+import static org.easymock.EasyMock.reset;
+import static org.easymock.EasyMock.verify;
+import static org.junit.Assert.*;
+import static org.onlab.onos.cluster.ControllerNode.State.ACTIVE;
+import static org.onlab.onos.net.DeviceId.deviceId;
+import static org.onlab.onos.net.Link.Type.*;
+import static org.onlab.onos.net.link.LinkEvent.Type.*;
+import static org.onlab.onos.net.NetTestTools.assertAnnotationsEquals;
+
+/**
+ * Test of the GossipLinkStoreTest implementation.
+ */
+public class GossipLinkStoreTest {
+
+ private static final ProviderId PID = new ProviderId("of", "foo");
+ private static final ProviderId PIDA = new ProviderId("of", "bar", true);
+ private static final DeviceId DID1 = deviceId("of:foo");
+ private static final DeviceId DID2 = deviceId("of:bar");
+
+ private static final PortNumber P1 = PortNumber.portNumber(1);
+ private static final PortNumber P2 = PortNumber.portNumber(2);
+ private static final PortNumber P3 = PortNumber.portNumber(3);
+
+ private static final SparseAnnotations A1 = DefaultAnnotations.builder()
+ .set("A1", "a1")
+ .set("B1", "b1")
+ .build();
+ private static final SparseAnnotations A1_2 = DefaultAnnotations.builder()
+ .remove("A1")
+ .set("B3", "b3")
+ .build();
+ private static final SparseAnnotations A2 = DefaultAnnotations.builder()
+ .set("A2", "a2")
+ .set("B2", "b2")
+ .build();
+ private static final SparseAnnotations A2_2 = DefaultAnnotations.builder()
+ .remove("A2")
+ .set("B4", "b4")
+ .build();
+
+ // local node
+ private static final NodeId NID1 = new NodeId("local");
+ private static final ControllerNode ONOS1 =
+ new DefaultControllerNode(NID1, IpAddress.valueOf("127.0.0.1"));
+
+ // remote node
+ private static final NodeId NID2 = new NodeId("remote");
+ private static final ControllerNode ONOS2 =
+ new DefaultControllerNode(NID2, IpAddress.valueOf("127.0.0.2"));
+
+ private GossipLinkStore linkStoreImpl;
+ private LinkStore linkStore;
+
+ private DeviceClockManager deviceClockManager;
+ private DeviceClockService deviceClockService;
+ private ClusterCommunicationService clusterCommunicator;
+
+
+ @BeforeClass
+ public static void setUpBeforeClass() throws Exception {
+ }
+
+ @AfterClass
+ public static void tearDownAfterClass() throws Exception {
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ deviceClockManager = new DeviceClockManager();
+ deviceClockManager.activate();
+ deviceClockService = deviceClockManager;
+
+ // set initial terms
+ deviceClockManager.setMastershipTerm(DID1, MastershipTerm.of(NID1, 1));
+ deviceClockManager.setMastershipTerm(DID2, MastershipTerm.of(NID1, 2));
+
+ // TODO mock clusterCommunicator
+ clusterCommunicator = createNiceMock(ClusterCommunicationService.class);
+ clusterCommunicator.addSubscriber(anyObject(MessageSubject.class),
+ anyObject(ClusterMessageHandler.class));
+ expectLastCall().anyTimes();
+ replay(clusterCommunicator);
+
+ linkStoreImpl = new GossipLinkStore();
+ linkStoreImpl.deviceClockService = deviceClockService;
+ linkStoreImpl.clusterCommunicator = clusterCommunicator;
+ linkStoreImpl.clusterService = new TestClusterService();
+ linkStoreImpl.activate();
+ linkStore = linkStoreImpl;
+
+ verify(clusterCommunicator);
+ reset(clusterCommunicator);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ linkStoreImpl.deactivate();
+ }
+
+ private void putLink(DeviceId srcId, PortNumber srcNum,
+ DeviceId dstId, PortNumber dstNum, Type type,
+ SparseAnnotations... annotations) {
+ ConnectPoint src = new ConnectPoint(srcId, srcNum);
+ ConnectPoint dst = new ConnectPoint(dstId, dstNum);
+ reset(clusterCommunicator);
+ try {
+ expect(clusterCommunicator.broadcast(anyObject(ClusterMessage.class)))
+ .andReturn(true).anyTimes();
+ } catch (IOException e) {
+ fail("Should never reach here");
+ }
+ replay(clusterCommunicator);
+ linkStore.createOrUpdateLink(PID, new DefaultLinkDescription(src, dst, type, annotations));
+ verify(clusterCommunicator);
+ }
+
+ private void resetCommunicatorExpectingNoBroadcast(
+ Capture<ClusterMessage> bcast) {
+ bcast.reset();
+ reset(clusterCommunicator);
+ replay(clusterCommunicator);
+ }
+
+ private void resetCommunicatorExpectingSingleBroadcast(
+ Capture<ClusterMessage> bcast) {
+
+ bcast.reset();
+ reset(clusterCommunicator);
+ try {
+ expect(clusterCommunicator.broadcast(capture(bcast))).andReturn(true).once();
+ } catch (IOException e) {
+ fail("Should never reach here");
+ }
+ replay(clusterCommunicator);
+ }
+
+ private void putLink(LinkKey key, Type type, SparseAnnotations... annotations) {
+ putLink(key.src().deviceId(), key.src().port(),
+ key.dst().deviceId(), key.dst().port(),
+ type, annotations);
+ }
+
+ private static void assertLink(DeviceId srcId, PortNumber srcNum,
+ DeviceId dstId, PortNumber dstNum, Type type,
+ Link link) {
+ assertEquals(srcId, link.src().deviceId());
+ assertEquals(srcNum, link.src().port());
+ assertEquals(dstId, link.dst().deviceId());
+ assertEquals(dstNum, link.dst().port());
+ assertEquals(type, link.type());
+ }
+
+ private static void assertLink(LinkKey key, Type type, Link link) {
+ assertLink(key.src().deviceId(), key.src().port(),
+ key.dst().deviceId(), key.dst().port(),
+ type, link);
+ }
+
+ @Test
+ public final void testGetLinkCount() {
+ assertEquals("initialy empty", 0, linkStore.getLinkCount());
+
+ putLink(DID1, P1, DID2, P2, DIRECT);
+ putLink(DID2, P2, DID1, P1, DIRECT);
+ putLink(DID1, P1, DID2, P2, DIRECT);
+
+ assertEquals("expecting 2 unique link", 2, linkStore.getLinkCount());
+ }
+
+ @Test
+ public final void testGetLinks() {
+ assertEquals("initialy empty", 0,
+ Iterables.size(linkStore.getLinks()));
+
+ LinkKey linkId1 = LinkKey.linkKey(new ConnectPoint(DID1, P1), new ConnectPoint(DID2, P2));
+ LinkKey linkId2 = LinkKey.linkKey(new ConnectPoint(DID2, P2), new ConnectPoint(DID1, P1));
+
+ putLink(linkId1, DIRECT);
+ putLink(linkId2, DIRECT);
+ putLink(linkId1, DIRECT);
+
+ assertEquals("expecting 2 unique link", 2,
+ Iterables.size(linkStore.getLinks()));
+
+ Map<LinkKey, Link> links = new HashMap<>();
+ for (Link link : linkStore.getLinks()) {
+ links.put(LinkKey.linkKey(link), link);
+ }
+
+ assertLink(linkId1, DIRECT, links.get(linkId1));
+ assertLink(linkId2, DIRECT, links.get(linkId2));
+ }
+
+ @Test
+ public final void testGetDeviceEgressLinks() {
+ LinkKey linkId1 = LinkKey.linkKey(new ConnectPoint(DID1, P1), new ConnectPoint(DID2, P2));
+ LinkKey linkId2 = LinkKey.linkKey(new ConnectPoint(DID2, P2), new ConnectPoint(DID1, P1));
+ LinkKey linkId3 = LinkKey.linkKey(new ConnectPoint(DID1, P2), new ConnectPoint(DID2, P3));
+
+ putLink(linkId1, DIRECT);
+ putLink(linkId2, DIRECT);
+ putLink(linkId3, DIRECT);
+
+ // DID1,P1 => DID2,P2
+ // DID2,P2 => DID1,P1
+ // DID1,P2 => DID2,P3
+
+ Set<Link> links1 = linkStore.getDeviceEgressLinks(DID1);
+ assertEquals(2, links1.size());
+ // check
+
+ Set<Link> links2 = linkStore.getDeviceEgressLinks(DID2);
+ assertEquals(1, links2.size());
+ assertLink(linkId2, DIRECT, links2.iterator().next());
+ }
+
+ @Test
+ public final void testGetDeviceIngressLinks() {
+ LinkKey linkId1 = LinkKey.linkKey(new ConnectPoint(DID1, P1), new ConnectPoint(DID2, P2));
+ LinkKey linkId2 = LinkKey.linkKey(new ConnectPoint(DID2, P2), new ConnectPoint(DID1, P1));
+ LinkKey linkId3 = LinkKey.linkKey(new ConnectPoint(DID1, P2), new ConnectPoint(DID2, P3));
+
+ putLink(linkId1, DIRECT);
+ putLink(linkId2, DIRECT);
+ putLink(linkId3, DIRECT);
+
+ // DID1,P1 => DID2,P2
+ // DID2,P2 => DID1,P1
+ // DID1,P2 => DID2,P3
+
+ Set<Link> links1 = linkStore.getDeviceIngressLinks(DID2);
+ assertEquals(2, links1.size());
+ // check
+
+ Set<Link> links2 = linkStore.getDeviceIngressLinks(DID1);
+ assertEquals(1, links2.size());
+ assertLink(linkId2, DIRECT, links2.iterator().next());
+ }
+
+ @Test
+ public final void testGetLink() {
+ ConnectPoint src = new ConnectPoint(DID1, P1);
+ ConnectPoint dst = new ConnectPoint(DID2, P2);
+ LinkKey linkId1 = LinkKey.linkKey(src, dst);
+
+ putLink(linkId1, DIRECT);
+
+ Link link = linkStore.getLink(src, dst);
+ assertLink(linkId1, DIRECT, link);
+
+ assertNull("There shouldn't be reverese link",
+ linkStore.getLink(dst, src));
+ }
+
+ @Test
+ public final void testGetEgressLinks() {
+ final ConnectPoint d1P1 = new ConnectPoint(DID1, P1);
+ final ConnectPoint d2P2 = new ConnectPoint(DID2, P2);
+ LinkKey linkId1 = LinkKey.linkKey(d1P1, d2P2);
+ LinkKey linkId2 = LinkKey.linkKey(d2P2, d1P1);
+ LinkKey linkId3 = LinkKey.linkKey(new ConnectPoint(DID1, P2), new ConnectPoint(DID2, P3));
+
+ putLink(linkId1, DIRECT);
+ putLink(linkId2, DIRECT);
+ putLink(linkId3, DIRECT);
+
+ // DID1,P1 => DID2,P2
+ // DID2,P2 => DID1,P1
+ // DID1,P2 => DID2,P3
+
+ Set<Link> links1 = linkStore.getEgressLinks(d1P1);
+ assertEquals(1, links1.size());
+ assertLink(linkId1, DIRECT, links1.iterator().next());
+
+ Set<Link> links2 = linkStore.getEgressLinks(d2P2);
+ assertEquals(1, links2.size());
+ assertLink(linkId2, DIRECT, links2.iterator().next());
+ }
+
+ @Test
+ public final void testGetIngressLinks() {
+ final ConnectPoint d1P1 = new ConnectPoint(DID1, P1);
+ final ConnectPoint d2P2 = new ConnectPoint(DID2, P2);
+ LinkKey linkId1 = LinkKey.linkKey(d1P1, d2P2);
+ LinkKey linkId2 = LinkKey.linkKey(d2P2, d1P1);
+ LinkKey linkId3 = LinkKey.linkKey(new ConnectPoint(DID1, P2), new ConnectPoint(DID2, P3));
+
+ putLink(linkId1, DIRECT);
+ putLink(linkId2, DIRECT);
+ putLink(linkId3, DIRECT);
+
+ // DID1,P1 => DID2,P2
+ // DID2,P2 => DID1,P1
+ // DID1,P2 => DID2,P3
+
+ Set<Link> links1 = linkStore.getIngressLinks(d2P2);
+ assertEquals(1, links1.size());
+ assertLink(linkId1, DIRECT, links1.iterator().next());
+
+ Set<Link> links2 = linkStore.getIngressLinks(d1P1);
+ assertEquals(1, links2.size());
+ assertLink(linkId2, DIRECT, links2.iterator().next());
+ }
+
+ @Test
+ public final void testCreateOrUpdateLink() {
+ ConnectPoint src = new ConnectPoint(DID1, P1);
+ ConnectPoint dst = new ConnectPoint(DID2, P2);
+
+ Capture<ClusterMessage> bcast = new Capture<>();
+
+ // add link
+ resetCommunicatorExpectingSingleBroadcast(bcast);
+ final DefaultLinkDescription linkDescription = new DefaultLinkDescription(src, dst, INDIRECT);
+ LinkEvent event = linkStore.createOrUpdateLink(PID,
+ linkDescription);
+ verifyLinkBroadcastMessage(PID, NID1, src, dst, INDIRECT, bcast);
+
+ assertLink(DID1, P1, DID2, P2, INDIRECT, event.subject());
+ assertEquals(LINK_ADDED, event.type());
+
+ // update link type
+ resetCommunicatorExpectingSingleBroadcast(bcast);
+ LinkEvent event2 = linkStore.createOrUpdateLink(PID,
+ new DefaultLinkDescription(src, dst, DIRECT));
+ verifyLinkBroadcastMessage(PID, NID1, src, dst, DIRECT, bcast);
+
+ assertLink(DID1, P1, DID2, P2, DIRECT, event2.subject());
+ assertEquals(LINK_UPDATED, event2.type());
+
+ // no change
+ resetCommunicatorExpectingSingleBroadcast(bcast);
+ LinkEvent event3 = linkStore.createOrUpdateLink(PID,
+ new DefaultLinkDescription(src, dst, DIRECT));
+ verifyNoBroadcastMessage(bcast);
+
+ assertNull("No change event expected", event3);
+ }
+
+ private void verifyNoBroadcastMessage(Capture<ClusterMessage> bcast) {
+ assertFalse("No broadcast expected", bcast.hasCaptured());
+ }
+
+ private void verifyLinkBroadcastMessage(ProviderId providerId,
+ NodeId sender,
+ ConnectPoint src,
+ ConnectPoint dst,
+ Type type,
+ Capture<ClusterMessage> actualMsg) {
+ verify(clusterCommunicator);
+ assertTrue(actualMsg.hasCaptured());
+ assertEquals(sender, actualMsg.getValue().sender());
+ assertEquals(GossipLinkStoreMessageSubjects.LINK_UPDATE,
+ actualMsg.getValue().subject());
+ InternalLinkEvent linkEvent
+ = GossipLinkStore.SERIALIZER.decode(actualMsg.getValue().payload());
+ assertEquals(providerId, linkEvent.providerId());
+ assertLinkDescriptionEquals(src, dst, type, linkEvent.linkDescription().value());
+
+ }
+
+ private static void assertLinkDescriptionEquals(ConnectPoint src,
+ ConnectPoint dst,
+ Type type,
+ LinkDescription actual) {
+ assertEquals(src, actual.src());
+ assertEquals(dst, actual.dst());
+ assertEquals(type, actual.type());
+ // TODO check annotations
+ }
+
+ @Test
+ public final void testCreateOrUpdateLinkAncillary() {
+ ConnectPoint src = new ConnectPoint(DID1, P1);
+ ConnectPoint dst = new ConnectPoint(DID2, P2);
+
+ Capture<ClusterMessage> bcast = new Capture<>();
+
+ // add Ancillary link
+ resetCommunicatorExpectingSingleBroadcast(bcast);
+ LinkEvent event = linkStore.createOrUpdateLink(PIDA,
+ new DefaultLinkDescription(src, dst, INDIRECT, A1));
+ verifyLinkBroadcastMessage(PIDA, NID1, src, dst, INDIRECT, bcast);
+
+ assertNotNull("Ancillary only link is ignored", event);
+
+ // add Primary link
+ resetCommunicatorExpectingSingleBroadcast(bcast);
+ LinkEvent event2 = linkStore.createOrUpdateLink(PID,
+ new DefaultLinkDescription(src, dst, INDIRECT, A2));
+ verifyLinkBroadcastMessage(PID, NID1, src, dst, INDIRECT, bcast);
+
+ assertLink(DID1, P1, DID2, P2, INDIRECT, event2.subject());
+ assertAnnotationsEquals(event2.subject().annotations(), A2, A1);
+ assertEquals(LINK_UPDATED, event2.type());
+
+ // update link type
+ resetCommunicatorExpectingSingleBroadcast(bcast);
+ LinkEvent event3 = linkStore.createOrUpdateLink(PID,
+ new DefaultLinkDescription(src, dst, DIRECT, A2));
+ verifyLinkBroadcastMessage(PID, NID1, src, dst, DIRECT, bcast);
+
+ assertLink(DID1, P1, DID2, P2, DIRECT, event3.subject());
+ assertAnnotationsEquals(event3.subject().annotations(), A2, A1);
+ assertEquals(LINK_UPDATED, event3.type());
+
+
+ // no change
+ resetCommunicatorExpectingNoBroadcast(bcast);
+ LinkEvent event4 = linkStore.createOrUpdateLink(PID,
+ new DefaultLinkDescription(src, dst, DIRECT));
+ verifyNoBroadcastMessage(bcast);
+
+ assertNull("No change event expected", event4);
+
+ // update link annotation (Primary)
+ resetCommunicatorExpectingSingleBroadcast(bcast);
+ LinkEvent event5 = linkStore.createOrUpdateLink(PID,
+ new DefaultLinkDescription(src, dst, DIRECT, A2_2));
+ verifyLinkBroadcastMessage(PID, NID1, src, dst, DIRECT, bcast);
+
+ assertLink(DID1, P1, DID2, P2, DIRECT, event5.subject());
+ assertAnnotationsEquals(event5.subject().annotations(), A2, A2_2, A1);
+ assertEquals(LINK_UPDATED, event5.type());
+
+ // update link annotation (Ancillary)
+ resetCommunicatorExpectingSingleBroadcast(bcast);
+ LinkEvent event6 = linkStore.createOrUpdateLink(PIDA,
+ new DefaultLinkDescription(src, dst, DIRECT, A1_2));
+ verifyLinkBroadcastMessage(PIDA, NID1, src, dst, DIRECT, bcast);
+
+ assertLink(DID1, P1, DID2, P2, DIRECT, event6.subject());
+ assertAnnotationsEquals(event6.subject().annotations(), A2, A2_2, A1, A1_2);
+ assertEquals(LINK_UPDATED, event6.type());
+
+ // update link type (Ancillary) : ignored
+ resetCommunicatorExpectingNoBroadcast(bcast);
+ LinkEvent event7 = linkStore.createOrUpdateLink(PIDA,
+ new DefaultLinkDescription(src, dst, EDGE));
+ verifyNoBroadcastMessage(bcast);
+ assertNull("Ancillary change other than annotation is ignored", event7);
+ }
+
+
+ @Test
+ public final void testRemoveLink() {
+ final ConnectPoint d1P1 = new ConnectPoint(DID1, P1);
+ final ConnectPoint d2P2 = new ConnectPoint(DID2, P2);
+ LinkKey linkId1 = LinkKey.linkKey(d1P1, d2P2);
+ LinkKey linkId2 = LinkKey.linkKey(d2P2, d1P1);
+
+ putLink(linkId1, DIRECT, A1);
+ putLink(linkId2, DIRECT, A2);
+
+ // DID1,P1 => DID2,P2
+ // DID2,P2 => DID1,P1
+ // DID1,P2 => DID2,P3
+
+ LinkEvent event = linkStore.removeLink(d1P1, d2P2);
+ assertEquals(LINK_REMOVED, event.type());
+ assertAnnotationsEquals(event.subject().annotations(), A1);
+ LinkEvent event2 = linkStore.removeLink(d1P1, d2P2);
+ assertNull(event2);
+
+ assertLink(linkId2, DIRECT, linkStore.getLink(d2P2, d1P1));
+ assertAnnotationsEquals(linkStore.getLink(d2P2, d1P1).annotations(), A2);
+
+ // annotations, etc. should not survive remove
+ putLink(linkId1, DIRECT);
+ assertLink(linkId1, DIRECT, linkStore.getLink(d1P1, d2P2));
+ assertAnnotationsEquals(linkStore.getLink(d1P1, d2P2).annotations());
+ }
+
+ @Test
+ public final void testAncillaryVisible() {
+ ConnectPoint src = new ConnectPoint(DID1, P1);
+ ConnectPoint dst = new ConnectPoint(DID2, P2);
+
+ // add Ancillary link
+ linkStore.createOrUpdateLink(PIDA,
+ new DefaultLinkDescription(src, dst, INDIRECT, A1));
+
+ // Ancillary only link should not be visible
+ assertEquals(1, linkStore.getLinkCount());
+ assertNotNull(linkStore.getLink(src, dst));
+ }
+
+ // If Delegates should be called only on remote events,
+ // then Simple* should never call them, thus not test required.
+ @Ignore("Ignore until Delegate spec. is clear.")
+ @Test
+ public final void testEvents() throws InterruptedException {
+
+ final ConnectPoint d1P1 = new ConnectPoint(DID1, P1);
+ final ConnectPoint d2P2 = new ConnectPoint(DID2, P2);
+ final LinkKey linkId1 = LinkKey.linkKey(d1P1, d2P2);
+
+ final CountDownLatch addLatch = new CountDownLatch(1);
+ LinkStoreDelegate checkAdd = new LinkStoreDelegate() {
+ @Override
+ public void notify(LinkEvent event) {
+ assertEquals(LINK_ADDED, event.type());
+ assertLink(linkId1, INDIRECT, event.subject());
+ addLatch.countDown();
+ }
+ };
+ final CountDownLatch updateLatch = new CountDownLatch(1);
+ LinkStoreDelegate checkUpdate = new LinkStoreDelegate() {
+ @Override
+ public void notify(LinkEvent event) {
+ assertEquals(LINK_UPDATED, event.type());
+ assertLink(linkId1, DIRECT, event.subject());
+ updateLatch.countDown();
+ }
+ };
+ final CountDownLatch removeLatch = new CountDownLatch(1);
+ LinkStoreDelegate checkRemove = new LinkStoreDelegate() {
+ @Override
+ public void notify(LinkEvent event) {
+ assertEquals(LINK_REMOVED, event.type());
+ assertLink(linkId1, DIRECT, event.subject());
+ removeLatch.countDown();
+ }
+ };
+
+ linkStore.setDelegate(checkAdd);
+ putLink(linkId1, INDIRECT);
+ assertTrue("Add event fired", addLatch.await(1, TimeUnit.SECONDS));
+
+ linkStore.unsetDelegate(checkAdd);
+ linkStore.setDelegate(checkUpdate);
+ putLink(linkId1, DIRECT);
+ assertTrue("Update event fired", updateLatch.await(1, TimeUnit.SECONDS));
+
+ linkStore.unsetDelegate(checkUpdate);
+ linkStore.setDelegate(checkRemove);
+ linkStore.removeLink(d1P1, d2P2);
+ assertTrue("Remove event fired", removeLatch.await(1, TimeUnit.SECONDS));
+ }
+
+ private static final class TestClusterService extends StaticClusterService {
+
+ public TestClusterService() {
+ localNode = ONOS1;
+ nodes.put(NID1, ONOS1);
+ nodeStates.put(NID1, ACTIVE);
+
+ nodes.put(NID2, ONOS2);
+ nodeStates.put(NID2, ACTIVE);
+ }
+ }
+}
diff --git a/core/store/dist/src/test/java/org/onlab/onos/store/link/impl/LinkFragmentIdTest.java b/core/store/dist/src/test/java/org/onlab/onos/store/link/impl/LinkFragmentIdTest.java
new file mode 100644
index 0000000..5109782
--- /dev/null
+++ b/core/store/dist/src/test/java/org/onlab/onos/store/link/impl/LinkFragmentIdTest.java
@@ -0,0 +1,48 @@
+package org.onlab.onos.store.link.impl;
+
+import static org.onlab.onos.net.DeviceId.deviceId;
+
+import org.junit.Test;
+import org.onlab.onos.net.ConnectPoint;
+import org.onlab.onos.net.DeviceId;
+import org.onlab.onos.net.LinkKey;
+import org.onlab.onos.net.PortNumber;
+import org.onlab.onos.net.provider.ProviderId;
+import com.google.common.testing.EqualsTester;
+
+public class LinkFragmentIdTest {
+
+ private static final ProviderId PID = new ProviderId("of", "foo");
+ private static final ProviderId PIDA = new ProviderId("of", "bar", true);
+
+ private static final DeviceId DID1 = deviceId("of:foo");
+ private static final DeviceId DID2 = deviceId("of:bar");
+
+ private static final PortNumber P1 = PortNumber.portNumber(1);
+ private static final PortNumber P2 = PortNumber.portNumber(2);
+ private static final PortNumber P3 = PortNumber.portNumber(3);
+
+ private static final ConnectPoint CP1 = new ConnectPoint(DID1, P1);
+ private static final ConnectPoint CP2 = new ConnectPoint(DID2, P2);
+
+ private static final ConnectPoint CP3 = new ConnectPoint(DID1, P2);
+ private static final ConnectPoint CP4 = new ConnectPoint(DID2, P3);
+
+ private static final LinkKey L1 = LinkKey.linkKey(CP1, CP2);
+ private static final LinkKey L2 = LinkKey.linkKey(CP3, CP4);
+
+ @Test
+ public void testEquals() {
+ new EqualsTester()
+ .addEqualityGroup(new LinkFragmentId(L1, PID),
+ new LinkFragmentId(L1, PID))
+ .addEqualityGroup(new LinkFragmentId(L2, PID),
+ new LinkFragmentId(L2, PID))
+ .addEqualityGroup(new LinkFragmentId(L1, PIDA),
+ new LinkFragmentId(L1, PIDA))
+ .addEqualityGroup(new LinkFragmentId(L2, PIDA),
+ new LinkFragmentId(L2, PIDA))
+ .testEquals();
+ }
+
+}
diff --git a/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/KryoNamespaces.java b/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/KryoNamespaces.java
index 2a4c10c..80db22e 100644
--- a/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/KryoNamespaces.java
+++ b/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/KryoNamespaces.java
@@ -75,9 +75,15 @@
import org.onlab.onos.net.intent.OpticalPathIntent;
import org.onlab.onos.net.intent.PathIntent;
import org.onlab.onos.net.intent.PointToPointIntent;
+import org.onlab.onos.net.intent.constraint.BandwidthConstraint;
+import org.onlab.onos.net.intent.constraint.BooleanConstraint;
+import org.onlab.onos.net.intent.constraint.LambdaConstraint;
+import org.onlab.onos.net.intent.constraint.LinkTypeConstraint;
import org.onlab.onos.net.link.DefaultLinkDescription;
import org.onlab.onos.net.packet.DefaultOutboundPacket;
import org.onlab.onos.net.provider.ProviderId;
+import org.onlab.onos.net.resource.Bandwidth;
+import org.onlab.onos.net.resource.Lambda;
import org.onlab.onos.net.resource.LinkResourceRequest;
import org.onlab.onos.store.Timestamp;
import org.onlab.packet.ChassisId;
@@ -188,7 +194,13 @@
LinkCollectionIntent.class,
OpticalConnectivityIntent.class,
OpticalPathIntent.class,
- LinkResourceRequest.class
+ LinkResourceRequest.class,
+ Lambda.class,
+ Bandwidth.class,
+ LambdaConstraint.class,
+ BandwidthConstraint.class,
+ LinkTypeConstraint.class,
+ BooleanConstraint.class
)
.register(DefaultApplicationId.class, new DefaultApplicationIdSerializer())
.register(URI.class, new URISerializer())
diff --git a/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/StoreSerializer.java b/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/StoreSerializer.java
index 48fff65..8ebb0e6 100644
--- a/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/StoreSerializer.java
+++ b/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/StoreSerializer.java
@@ -44,6 +44,7 @@
*
* @param bytes bytes to be deserialized
* @return deserialized object
+ * @param <T> decoded type
*/
public <T> T decode(final byte[] bytes);
@@ -52,6 +53,7 @@
*
* @param buffer bytes to be deserialized
* @return deserialized object
+ * @param <T> decoded type
*/
public <T> T decode(final ByteBuffer buffer);
}
diff --git a/core/store/trivial/pom.xml b/core/store/trivial/pom.xml
index 59cf1b8..d1e6bb5 100644
--- a/core/store/trivial/pom.xml
+++ b/core/store/trivial/pom.xml
@@ -36,6 +36,13 @@
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
+
+ <dependency>
+ <groupId>org.onlab.onos</groupId>
+ <artifactId>onos-api</artifactId>
+ <classifier>tests</classifier>
+ <scope>test</scope>
+ </dependency>
</dependencies>
</project>
diff --git a/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/SimpleFlowRuleStore.java b/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/SimpleFlowRuleStore.java
index 49b0c71..b7d26fb 100644
--- a/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/SimpleFlowRuleStore.java
+++ b/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/SimpleFlowRuleStore.java
@@ -16,8 +16,12 @@
package org.onlab.onos.store.trivial.impl;
import com.google.common.base.Function;
+import com.google.common.cache.Cache;
+import com.google.common.cache.CacheBuilder;
import com.google.common.collect.FluentIterable;
import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.SettableFuture;
+
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Deactivate;
@@ -43,13 +47,15 @@
import org.onlab.util.NewConcurrentHashMap;
import org.slf4j.Logger;
-import java.util.Arrays;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
import static org.apache.commons.lang3.concurrent.ConcurrentUtils.createIfAbsentUnchecked;
import static org.onlab.onos.net.flow.FlowRuleEvent.Type.RULE_REMOVED;
@@ -72,6 +78,18 @@
private final ConcurrentMap<DeviceId, ConcurrentMap<FlowId, List<StoredFlowEntry>>>
flowEntries = new ConcurrentHashMap<>();
+ private final AtomicInteger localBatchIdGen = new AtomicInteger();
+
+ // TODO: make this configurable
+ private int pendingFutureTimeoutMinutes = 5;
+
+ private Cache<Integer, SettableFuture<CompletedBatchOperation>> pendingFutures =
+ CacheBuilder.newBuilder()
+ .expireAfterWrite(pendingFutureTimeoutMinutes, TimeUnit.MINUTES)
+ // TODO Explicitly fail the future if expired?
+ //.removalListener(listener)
+ .build();
+
@Activate
public void activate() {
log.info("Started");
@@ -173,10 +191,6 @@
}
// new flow rule added
existing.add(f);
- notifyDelegate(FlowRuleBatchEvent.requested(
- new FlowRuleBatchRequest(1, /* FIXME generate something */
- Arrays.<FlowEntry>asList(f),
- Collections.<FlowEntry>emptyList())));
}
}
@@ -190,11 +204,6 @@
if (entry.equals(rule)) {
synchronized (entry) {
entry.setState(FlowEntryState.PENDING_REMOVE);
- // TODO: Should we notify only if it's "remote" event?
- notifyDelegate(FlowRuleBatchEvent.requested(
- new FlowRuleBatchRequest(1, /* FIXME generate something */
- Collections.<FlowEntry>emptyList(),
- Arrays.<FlowEntry>asList(entry))));
}
}
}
@@ -251,20 +260,47 @@
@Override
public Future<CompletedBatchOperation> storeBatch(
FlowRuleBatchOperation batchOperation) {
+ List<FlowRule> toAdd = new ArrayList<>();
+ List<FlowRule> toRemove = new ArrayList<>();
for (FlowRuleBatchEntry entry : batchOperation.getOperations()) {
+ final FlowRule flowRule = entry.getTarget();
if (entry.getOperator().equals(FlowRuleOperation.ADD)) {
- storeFlowRule(entry.getTarget());
+ if (!getFlowEntries(flowRule.deviceId(), flowRule.id()).contains(flowRule)) {
+ storeFlowRule(flowRule);
+ toAdd.add(flowRule);
+ }
} else if (entry.getOperator().equals(FlowRuleOperation.REMOVE)) {
- deleteFlowRule(entry.getTarget());
+ if (getFlowEntries(flowRule.deviceId(), flowRule.id()).contains(flowRule)) {
+ deleteFlowRule(flowRule);
+ toRemove.add(flowRule);
+ }
} else {
throw new UnsupportedOperationException("Unsupported operation type");
}
}
- return Futures.immediateFuture(new CompletedBatchOperation(true, Collections.<FlowEntry>emptySet()));
+
+ if (toAdd.isEmpty() && toRemove.isEmpty()) {
+ return Futures.immediateFuture(new CompletedBatchOperation(true, Collections.<FlowRule>emptySet()));
+ }
+
+ SettableFuture<CompletedBatchOperation> r = SettableFuture.create();
+ final int batchId = localBatchIdGen.incrementAndGet();
+
+ pendingFutures.put(batchId, r);
+ notifyDelegate(FlowRuleBatchEvent.requested(new FlowRuleBatchRequest(batchId, toAdd, toRemove)));
+
+ return r;
}
@Override
public void batchOperationComplete(FlowRuleBatchEvent event) {
+ final Integer batchId = event.subject().batchId();
+ SettableFuture<CompletedBatchOperation> future
+ = pendingFutures.getIfPresent(batchId);
+ if (future != null) {
+ future.set(event.result());
+ pendingFutures.invalidate(batchId);
+ }
notifyDelegate(event);
}
}
diff --git a/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/SimpleHostStore.java b/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/SimpleHostStore.java
index 6c27ea8..2f8fbd2 100644
--- a/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/SimpleHostStore.java
+++ b/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/SimpleHostStore.java
@@ -15,10 +15,18 @@
*/
package org.onlab.onos.store.trivial.impl;
-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 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 org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Deactivate;
@@ -34,7 +42,6 @@
import org.onlab.onos.net.host.HostEvent;
import org.onlab.onos.net.host.HostStore;
import org.onlab.onos.net.host.HostStoreDelegate;
-import org.onlab.onos.net.host.InterfaceIpAddress;
import org.onlab.onos.net.host.PortAddresses;
import org.onlab.onos.net.provider.ProviderId;
import org.onlab.onos.store.AbstractStore;
@@ -43,13 +50,11 @@
import org.onlab.packet.VlanId;
import org.slf4j.Logger;
-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;
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Multimap;
+import com.google.common.collect.Multimaps;
+import com.google.common.collect.SetMultimap;
// TODO: multi-provider, annotation not supported.
/**
@@ -70,8 +75,9 @@
// Hosts tracked by their location
private final Multimap<ConnectPoint, Host> locations = HashMultimap.create();
- private final Map<ConnectPoint, PortAddresses> portAddresses =
- new ConcurrentHashMap<>();
+ private final SetMultimap<ConnectPoint, PortAddresses> portAddresses =
+ Multimaps.synchronizedSetMultimap(
+ HashMultimap.<ConnectPoint, PortAddresses>create());
@Activate
public void activate() {
@@ -213,77 +219,37 @@
@Override
public void updateAddressBindings(PortAddresses addresses) {
- synchronized (portAddresses) {
- PortAddresses existing = portAddresses.get(addresses.connectPoint());
- if (existing == null) {
- portAddresses.put(addresses.connectPoint(), addresses);
- } else {
- Set<InterfaceIpAddress> union =
- Sets.union(existing.ipAddresses(),
- addresses.ipAddresses()).immutableCopy();
-
- MacAddress newMac = (addresses.mac() == null) ? existing.mac()
- : addresses.mac();
-
- PortAddresses newAddresses =
- new PortAddresses(addresses.connectPoint(), union, newMac);
-
- portAddresses.put(newAddresses.connectPoint(), newAddresses);
- }
- }
+ portAddresses.put(addresses.connectPoint(), addresses);
}
@Override
public void removeAddressBindings(PortAddresses addresses) {
- synchronized (portAddresses) {
- PortAddresses existing = portAddresses.get(addresses.connectPoint());
- if (existing != null) {
- Set<InterfaceIpAddress> difference =
- Sets.difference(existing.ipAddresses(),
- addresses.ipAddresses()).immutableCopy();
-
- // If they removed the existing mac, set the new mac to null.
- // Otherwise, keep the existing mac.
- MacAddress newMac = existing.mac();
- if (addresses.mac() != null && addresses.mac().equals(existing.mac())) {
- newMac = null;
- }
-
- PortAddresses newAddresses =
- new PortAddresses(addresses.connectPoint(), difference, newMac);
-
- portAddresses.put(newAddresses.connectPoint(), newAddresses);
- }
- }
+ portAddresses.remove(addresses.connectPoint(), addresses);
}
@Override
public void clearAddressBindings(ConnectPoint connectPoint) {
- synchronized (portAddresses) {
- portAddresses.remove(connectPoint);
- }
+ portAddresses.removeAll(connectPoint);
}
@Override
public Set<PortAddresses> getAddressBindings() {
synchronized (portAddresses) {
- return new HashSet<>(portAddresses.values());
+ return ImmutableSet.copyOf(portAddresses.values());
}
}
@Override
- public PortAddresses getAddressBindingsForPort(ConnectPoint connectPoint) {
- PortAddresses addresses;
-
+ public Set<PortAddresses> getAddressBindingsForPort(ConnectPoint connectPoint) {
synchronized (portAddresses) {
- addresses = portAddresses.get(connectPoint);
- }
+ Set<PortAddresses> addresses = portAddresses.get(connectPoint);
- if (addresses == null) {
- addresses = new PortAddresses(connectPoint, null, null);
+ if (addresses == null) {
+ return Collections.emptySet();
+ } else {
+ return ImmutableSet.copyOf(addresses);
+ }
}
-
- return addresses;
}
// Auxiliary extension to allow location to mutate.
diff --git a/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/SimpleLinkResourceStore.java b/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/SimpleLinkResourceStore.java
index f97c0ab..5369375 100644
--- a/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/SimpleLinkResourceStore.java
+++ b/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/SimpleLinkResourceStore.java
@@ -195,7 +195,7 @@
@Override
public void releaseResources(LinkResourceAllocations allocations) {
checkNotNull(allocations);
- linkResourceAllocationsMap.remove(allocations);
+ linkResourceAllocationsMap.remove(allocations.intendId());
for (Link link : allocations.links()) {
addFreeResources(link, allocations);
Set<LinkResourceAllocations> linkAllocs = allocatedResources.get(link);
diff --git a/core/store/trivial/src/test/java/org/onlab/onos/store/trivial/impl/SimpleDeviceStoreTest.java b/core/store/trivial/src/test/java/org/onlab/onos/store/trivial/impl/SimpleDeviceStoreTest.java
index f43ae10..cc65052 100644
--- a/core/store/trivial/src/test/java/org/onlab/onos/store/trivial/impl/SimpleDeviceStoreTest.java
+++ b/core/store/trivial/src/test/java/org/onlab/onos/store/trivial/impl/SimpleDeviceStoreTest.java
@@ -23,6 +23,7 @@
import static org.onlab.onos.net.Device.Type.SWITCH;
import static org.onlab.onos.net.DeviceId.deviceId;
import static org.onlab.onos.net.device.DeviceEvent.Type.*;
+import static org.onlab.onos.net.NetTestTools.assertAnnotationsEquals;
import java.util.Arrays;
import java.util.HashMap;
@@ -38,7 +39,6 @@
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test;
-import org.onlab.onos.net.Annotations;
import org.onlab.onos.net.DefaultAnnotations;
import org.onlab.onos.net.Device;
import org.onlab.onos.net.DeviceId;
@@ -56,6 +56,7 @@
import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;
+
import org.onlab.packet.ChassisId;
/**
@@ -146,25 +147,6 @@
assertEquals(SN, device.serialNumber());
}
- // TODO slice this out somewhere
- /**
- * Verifies that Annotations created by merging {@code annotations} is
- * equal to actual Annotations.
- *
- * @param actual Annotations to check
- * @param annotations
- */
- public static void assertAnnotationsEquals(Annotations actual, SparseAnnotations... annotations) {
- DefaultAnnotations expected = DefaultAnnotations.builder().build();
- for (SparseAnnotations a : annotations) {
- expected = DefaultAnnotations.merge(expected, a);
- }
- assertEquals(expected.keys(), actual.keys());
- for (String key : expected.keys()) {
- assertEquals(expected.value(key), actual.value(key));
- }
- }
-
@Test
public final void testGetDeviceCount() {
assertEquals("initialy empty", 0, deviceStore.getDeviceCount());
diff --git a/core/store/trivial/src/test/java/org/onlab/onos/store/trivial/impl/SimpleLinkStoreTest.java b/core/store/trivial/src/test/java/org/onlab/onos/store/trivial/impl/SimpleLinkStoreTest.java
index 043cff3..735f99c 100644
--- a/core/store/trivial/src/test/java/org/onlab/onos/store/trivial/impl/SimpleLinkStoreTest.java
+++ b/core/store/trivial/src/test/java/org/onlab/onos/store/trivial/impl/SimpleLinkStoreTest.java
@@ -16,6 +16,7 @@
package org.onlab.onos.store.trivial.impl;
import com.google.common.collect.Iterables;
+
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
@@ -46,7 +47,7 @@
import static org.onlab.onos.net.DeviceId.deviceId;
import static org.onlab.onos.net.Link.Type.*;
import static org.onlab.onos.net.link.LinkEvent.Type.*;
-import static org.onlab.onos.store.trivial.impl.SimpleDeviceStoreTest.assertAnnotationsEquals;
+import static org.onlab.onos.net.NetTestTools.assertAnnotationsEquals;
/**
* Test of the simple LinkStore implementation.
diff --git a/docs/external.xml b/docs/external.xml
index 7942d80..5b73366 100644
--- a/docs/external.xml
+++ b/docs/external.xml
@@ -49,7 +49,7 @@
<version>2.10.1</version>
<configuration>
<show>package</show>
- <excludePackageNames>org.onlab.thirdparty:*.impl:*.impl.*:org.onlab.onos.provider.*:org.onlab.onos.gui:org.onlab.onos.rest:org.onlab.onos.cli*:org.onlab.onos.tvue:org.onlab.onos.foo:org.onlab.onos.mobility:org.onlab.onos.proxyarp:org.onlab.onos.fwd:org.onlab.onos.ifwd:org.onlab.onos.optical:org.onlab.onos.config:org.onlab.onos.calendar:org.onlab.onos.sdnip*:org.onlab.onos.metrics:org.onlab.onos.store.*:org.onlab.onos.openflow.*</excludePackageNames>
+ <excludePackageNames>org.onlab.thirdparty:*.impl:*.impl.*:org.onlab.onos.provider.*:org.onlab.onos.gui:org.onlab.onos.rest:org.onlab.onos.cli*:org.onlab.onos.tvue:org.onlab.onos.foo:org.onlab.onos.mobility:org.onlab.onos.proxyarp:org.onlab.onos.fwd:org.onlab.onos.ifwd:org.onlab.onos.optical:org.onlab.onos.config:org.onlab.onos.calendar:org.onlab.onos.sdnip*:org.onlab.onos.oecfg:org.onlab.onos.metrics:org.onlab.onos.store.*:org.onlab.onos.openflow.*</excludePackageNames>
<docfilessubdirs>true</docfilessubdirs>
<doctitle>ONOS Java API</doctitle>
<groups>
diff --git a/docs/pom.xml b/docs/pom.xml
index d2581eb..c0d1fcd 100644
--- a/docs/pom.xml
+++ b/docs/pom.xml
@@ -48,7 +48,6 @@
<artifactId>maven-javadoc-plugin</artifactId>
<version>2.10.1</version>
<configuration>
- <!--additionalparam>-Xdoclint:none</additionalparam-->
<show>package</show>
<docfilessubdirs>true</docfilessubdirs>
<doctitle>ONOS Java API</doctitle>
@@ -87,7 +86,13 @@
<group>
<title>Sample Applications</title>
<packages>
- org.onlab.onos.tvue:org.onlab.onos.fwd:org.onlab.onos.ifwd:org.onlab.onos.mobility:org.onlab.onos.proxyarp:org.onlab.onos.foo:org.onlab.onos.calendar:org.onlab.onos.sdnip:org.onlab.onos.sdnip.*:org.onlab.onos.optical:org.onlab.onos.optical.*:org.onlab.onos.metrics.*:org.onlab.onos.config
+ org.onlab.onos.tvue:org.onlab.onos.fwd:org.onlab.onos.ifwd:org.onlab.onos.mobility:org.onlab.onos.proxyarp:org.onlab.onos.foo:org.onlab.onos.calendar:org.onlab.onos.optical.*:org.onlab.onos.sdnip:org.onlab.onos.sdnip.*:org.onlab.onos.config
+ </packages>
+ </group>
+ <group>
+ <title>Test Instrumentation</title>
+ <packages>
+ org.onlab.onos.metrics.*:org.onlab.onos.oecfg
</packages>
</group>
</groups>
diff --git a/features/features.xml b/features/features.xml
index 088e42f..2e0718f 100644
--- a/features/features.xml
+++ b/features/features.xml
@@ -56,6 +56,9 @@
<bundle>mvn:org.codehaus.jackson/jackson-mapper-asl/1.9.13</bundle>
<bundle>mvn:org.onlab.onos/onlab-thirdparty/1.0.0-SNAPSHOT</bundle>
+
+ <!-- FIXME fix the version before release -->
+ <bundle>wrap:mvn:http://mavenrepo.onlab.us:8081/nexus/content/groups/public@id=onlab-temp!net.kuujo.copycat/copycat/0.4.0-SNAPSHOT$Bundle-SymbolicName=net.kuujo.copycat.copycat&Bundle-Version=0.4.0.SNAPSHOT</bundle>
</feature>
<feature name="onos-thirdparty-web" version="1.0.0"
diff --git a/openflow/api/pom.xml b/openflow/api/pom.xml
index de9246b..a6dedd6 100644
--- a/openflow/api/pom.xml
+++ b/openflow/api/pom.xml
@@ -31,15 +31,6 @@
<description>ONOS OpenFlow controller subsystem API</description>
- <repositories>
- <!-- FIXME: for Loxigen + optical experimenter. Decide how to use Loxigen before release. -->
- <repository>
- <id>onlab-temp</id>
- <name>ON.lab temporary repository</name>
- <url>http://mavenrepo.onlab.us:8081/nexus/content/repositories/releases</url>
- </repository>
- </repositories>
-
<dependencies>
<dependency>
<groupId>org.projectfloodlight</groupId>
@@ -50,7 +41,6 @@
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty</artifactId>
- <version>3.9.0.Final</version>
</dependency>
</dependencies>
diff --git a/openflow/api/src/main/java/org/onlab/onos/openflow/controller/OpenFlowEventListener.java b/openflow/api/src/main/java/org/onlab/onos/openflow/controller/OpenFlowEventListener.java
index bc842a0..fc9a872 100644
--- a/openflow/api/src/main/java/org/onlab/onos/openflow/controller/OpenFlowEventListener.java
+++ b/openflow/api/src/main/java/org/onlab/onos/openflow/controller/OpenFlowEventListener.java
@@ -26,6 +26,7 @@
/**
* Handles the message event.
*
+ * @param dpid switch data path identifier
* @param msg the message
*/
public void handleMessage(Dpid dpid, OFMessage msg);
diff --git a/openflow/api/src/main/java/org/onlab/onos/openflow/controller/OpenFlowPacketContext.java b/openflow/api/src/main/java/org/onlab/onos/openflow/controller/OpenFlowPacketContext.java
index 13092b2..0d085dd 100644
--- a/openflow/api/src/main/java/org/onlab/onos/openflow/controller/OpenFlowPacketContext.java
+++ b/openflow/api/src/main/java/org/onlab/onos/openflow/controller/OpenFlowPacketContext.java
@@ -31,6 +31,7 @@
/**
* Blocks further responses (ie. send() calls) on this
* packet in event.
+ * @return true if blocks
*/
public boolean block();
diff --git a/openflow/api/src/main/java/org/onlab/onos/openflow/controller/OpenFlowSwitch.java b/openflow/api/src/main/java/org/onlab/onos/openflow/controller/OpenFlowSwitch.java
index 2d3f890..bd90241 100644
--- a/openflow/api/src/main/java/org/onlab/onos/openflow/controller/OpenFlowSwitch.java
+++ b/openflow/api/src/main/java/org/onlab/onos/openflow/controller/OpenFlowSwitch.java
@@ -133,7 +133,7 @@
* @param requested the role requested by the controller
* @param response the role set at the device
*/
- public void returnRoleReply(RoleState requested, RoleState reponse);
+ public void returnRoleReply(RoleState requested, RoleState response);
/**
* Indicates if this switch is optical.
diff --git a/openflow/api/src/main/java/org/onlab/onos/openflow/controller/OpenFlowSwitchListener.java b/openflow/api/src/main/java/org/onlab/onos/openflow/controller/OpenFlowSwitchListener.java
index 192f045..3b6450b 100644
--- a/openflow/api/src/main/java/org/onlab/onos/openflow/controller/OpenFlowSwitchListener.java
+++ b/openflow/api/src/main/java/org/onlab/onos/openflow/controller/OpenFlowSwitchListener.java
@@ -51,7 +51,8 @@
* Notify that a role imposed on a switch failed to take hold.
*
* @param dpid the switch that failed role assertion
- * @param role the role imposed by the controller
+ * @param requested the role controller requested
+ * @param response role reply from the switch
*/
public void receivedRoleReply(Dpid dpid, RoleState requested, RoleState response);
}
diff --git a/openflow/api/src/main/java/org/onlab/onos/openflow/controller/driver/OpenFlowAgent.java b/openflow/api/src/main/java/org/onlab/onos/openflow/controller/driver/OpenFlowAgent.java
index 6b73efc..468d3ae 100644
--- a/openflow/api/src/main/java/org/onlab/onos/openflow/controller/driver/OpenFlowAgent.java
+++ b/openflow/api/src/main/java/org/onlab/onos/openflow/controller/driver/OpenFlowAgent.java
@@ -95,7 +95,8 @@
* Notifies the controller that role assertion has failed.
*
* @param dpid the switch that failed role assertion
- * @param role the failed role
+ * @param requested the role controller requested
+ * @param response role reply from the switch
*/
public void returnRoleReply(Dpid dpid, RoleState requested, RoleState response);
}
diff --git a/openflow/api/src/main/java/org/onlab/onos/openflow/controller/driver/RoleHandler.java b/openflow/api/src/main/java/org/onlab/onos/openflow/controller/driver/RoleHandler.java
index 8611664..c32d298 100644
--- a/openflow/api/src/main/java/org/onlab/onos/openflow/controller/driver/RoleHandler.java
+++ b/openflow/api/src/main/java/org/onlab/onos/openflow/controller/driver/RoleHandler.java
@@ -58,8 +58,9 @@
* OF1.3 switches, because Role.EQUAL is well defined and we can simulate
* SLAVE behavior by using ASYNC messages.
*
- * @param role
- * @throws IOException
+ * @param role role to request
+ * @param exp expectation
+ * @throws IOException when I/O exception of some sort has occurred
* @return false if and only if the switch does not support role-request
* messages, according to the switch driver; true otherwise.
*/
@@ -70,7 +71,7 @@
* Extract the role information from an OF1.3 Role Reply Message.
* @param rrmsg role reply message
* @return RoleReplyInfo object
- * @throws SwitchStateException
+ * @throws SwitchStateException If unknown role encountered
*/
public RoleReplyInfo extractOFRoleReply(OFRoleReply rrmsg)
throws SwitchStateException;
@@ -89,6 +90,7 @@
*
* @param rri information about role-reply in format that
* controller can understand.
+ * @return result comparing expected and received reply
* @throws SwitchStateException if no request is pending
*/
public RoleRecvStatus deliverRoleReply(RoleReplyInfo rri)
@@ -102,6 +104,9 @@
* Note: since we only keep the last pending request we might get
* error messages for earlier role requests that we won't be able
* to handle
+ * @param error error message
+ * @return result comparing expected and received reply
+ * @throws SwitchStateException if switch did not support requested role
*/
public RoleRecvStatus deliverError(OFErrorMsg error)
throws SwitchStateException;
diff --git a/openflow/api/src/main/java/org/onlab/onos/openflow/controller/driver/RoleReplyInfo.java b/openflow/api/src/main/java/org/onlab/onos/openflow/controller/driver/RoleReplyInfo.java
index b408c44..6efb333 100644
--- a/openflow/api/src/main/java/org/onlab/onos/openflow/controller/driver/RoleReplyInfo.java
+++ b/openflow/api/src/main/java/org/onlab/onos/openflow/controller/driver/RoleReplyInfo.java
@@ -32,9 +32,15 @@
this.genId = genId;
this.xid = xid;
}
- public RoleState getRole() { return role; }
- public U64 getGenId() { return genId; }
- public long getXid() { return xid; }
+ public RoleState getRole() {
+ return role;
+ }
+ public U64 getGenId() {
+ return genId;
+ }
+ public long getXid() {
+ return xid;
+ }
@Override
public String toString() {
return "[Role:" + role + " GenId:" + genId + " Xid:" + xid + "]";
diff --git a/openflow/ctl/src/main/java/org/onlab/onos/openflow/controller/impl/Controller.java b/openflow/ctl/src/main/java/org/onlab/onos/openflow/controller/impl/Controller.java
index d79227a..1d0e4cb 100644
--- a/openflow/ctl/src/main/java/org/onlab/onos/openflow/controller/impl/Controller.java
+++ b/openflow/ctl/src/main/java/org/onlab/onos/openflow/controller/impl/Controller.java
@@ -160,6 +160,7 @@
/**
* Initialize internal data structures.
+ * @param configParams configuration parameters
*/
public void init(Map<String, String> configParams) {
// These data structures are initialized here because other
@@ -192,7 +193,9 @@
/**
* Forward to the driver-manager to get an IOFSwitch instance.
- * @param desc
+ * @param dpid data path id
+ * @param desc switch description
+ * @param ofv OpenFlow version
* @return switch instance
*/
protected OpenFlowSwitchDriver getOFSwitchInstance(long dpid,
diff --git a/openflow/ctl/src/main/java/org/onlab/onos/openflow/controller/impl/OFChannelHandler.java b/openflow/ctl/src/main/java/org/onlab/onos/openflow/controller/impl/OFChannelHandler.java
index 8dbff60..ca5f4f5 100644
--- a/openflow/ctl/src/main/java/org/onlab/onos/openflow/controller/impl/OFChannelHandler.java
+++ b/openflow/ctl/src/main/java/org/onlab/onos/openflow/controller/impl/OFChannelHandler.java
@@ -115,7 +115,7 @@
/**
* Create a new unconnected OFChannelHandler.
- * @param controller
+ * @param controller parent controller
*/
OFChannelHandler(Controller controller) {
this.controller = controller;
@@ -794,7 +794,7 @@
* @param m The PortStatus message we received
* @param doNotify if true switch port changed events will be
* dispatched
- * @throws SwitchStateException
+ * @throws SwitchStateException if the switch is not bound to the channel
*
*/
protected void handlePortStatusMessage(OFChannelHandler h, OFPortStatus m,
@@ -826,8 +826,8 @@
*
* @param h The OFChannelHandler that received the message
* @param m The message we received.
- * @throws SwitchStateException
- * @throws IOException
+ * @throws SwitchStateException if the switch is not bound to the channel
+ * @throws IOException if unable to send message back to the switch
*/
void processOFMessage(OFChannelHandler h, OFMessage m)
throws IOException, SwitchStateException {
diff --git a/openflow/ctl/src/main/java/org/onlab/onos/openflow/controller/impl/RoleManager.java b/openflow/ctl/src/main/java/org/onlab/onos/openflow/controller/impl/RoleManager.java
index 6e5b236..734e0ba 100644
--- a/openflow/ctl/src/main/java/org/onlab/onos/openflow/controller/impl/RoleManager.java
+++ b/openflow/ctl/src/main/java/org/onlab/onos/openflow/controller/impl/RoleManager.java
@@ -347,7 +347,7 @@
RoleState role = null;
OFNiciraControllerRole ncr = nrr.getRole();
- switch(ncr) {
+ switch (ncr) {
case ROLE_MASTER:
role = RoleState.MASTER;
break;
@@ -383,7 +383,7 @@
throws SwitchStateException {
OFControllerRole cr = rrmsg.getRole();
RoleState role = null;
- switch(cr) {
+ switch (cr) {
case ROLE_EQUAL:
role = RoleState.EQUAL;
break;
diff --git a/pom.xml b/pom.xml
index 83ed0f4..dfc86a3 100644
--- a/pom.xml
+++ b/pom.xml
@@ -42,6 +42,20 @@
<netty4.version>4.0.23.Final</netty4.version>
</properties>
+ <repositories>
+ <!-- FIXME: Repository for copycat, Loxigen + optical experimenter.
+ Decide how to deal with these before release. -->
+ <repository>
+ <id>onlab-temp</id>
+ <name>ON.lab temporary repository</name>
+ <url>http://mavenrepo.onlab.us:8081/nexus/content/groups/public</url>
+ <snapshots>
+ <enabled>true</enabled>
+ </snapshots>
+ </repository>
+ </repositories>
+
+
<dependencyManagement>
<dependencies>
<dependency>
@@ -182,7 +196,7 @@
<dependency>
<groupId>org.apache.karaf.shell</groupId>
<artifactId>org.apache.karaf.shell.console</artifactId>
- <version>3.0.1</version>
+ <version>3.0.2</version>
<scope>provided</scope>
</dependency>
@@ -318,6 +332,7 @@
<groupId>io.netty</groupId>
<artifactId>netty-transport-native-epoll</artifactId>
<version>${netty4.version}</version>
+ <classifier>${os.detected.classifier}</classifier>
</dependency>
<dependency>
<groupId>joda-time</groupId>
@@ -360,8 +375,8 @@
<!-- https://jira.codehaus.org/browse/MCOMPILER-205 -->
<version>2.5.1</version>
<configuration>
- <source>1.7</source>
- <target>1.7</target>
+ <source>1.8</source>
+ <target>1.8</target>
</configuration>
</plugin>
@@ -399,14 +414,20 @@
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
- <version>2.3.7</version>
+ <version>2.5.3</version>
<extensions>true</extensions>
</plugin>
<plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>findbugs-maven-plugin</artifactId>
+ <version>3.0.0</version>
+ </plugin>
+
+ <plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-scr-plugin</artifactId>
- <version>1.15.0</version>
+ <version>1.20.0</version>
<executions>
<execution>
<id>generate-scr-srcdescriptor</id>
@@ -472,6 +493,12 @@
<artifactId>onos-build-conf</artifactId>
<version>1.0</version>
</dependency>
+ <!-- For Java 8 lambda support-->
+ <dependency>
+ <groupId>com.puppycrawl.tools</groupId>
+ <artifactId>checkstyle</artifactId>
+ <version>5.9</version>
+ </dependency>
</dependencies>
<configuration>
<configLocation>onos/checkstyle.xml</configLocation>
diff --git a/providers/lldp/src/main/java/org/onlab/onos/provider/lldp/impl/LinkDiscovery.java b/providers/lldp/src/main/java/org/onlab/onos/provider/lldp/impl/LinkDiscovery.java
index a20ab90..db52454 100644
--- a/providers/lldp/src/main/java/org/onlab/onos/provider/lldp/impl/LinkDiscovery.java
+++ b/providers/lldp/src/main/java/org/onlab/onos/provider/lldp/impl/LinkDiscovery.java
@@ -82,7 +82,7 @@
private final PacketService pktService;
private final MastershipService mastershipService;
private Timeout timeout;
- private boolean isStopped;
+ private volatile boolean isStopped;
/**
* Instantiates discovery manager for the given physical switch. Creates a
@@ -90,7 +90,9 @@
* Starts the the timer for the discovery process.
*
* @param device the physical switch
+ * @param pktService packet service
* @param masterService mastership service
+ * @param providerService link provider service
* @param useBDDP flag to also use BDDP for discovery
*/
public LinkDiscovery(Device device, PacketService pktService,
@@ -201,6 +203,8 @@
/**
* Handles an incoming LLDP packet. Creates link in topology and sends ACK
* to port where LLDP originated.
+ * @param context packet context
+ * @return true if handled
*/
public boolean handleLLDP(PacketContext context) {
Ethernet eth = context.inPacket().parsed();
@@ -239,8 +243,10 @@
public void run(final Timeout t) {
boolean isMaster = mastershipService.getLocalRole(device.id()) == MASTER;
if (!isMaster) {
- // reschedule timer
- timeout = Timer.getTimer().newTimeout(this, this.probeRate, MILLISECONDS);
+ if (!isStopped()) {
+ // reschedule timer
+ timeout = Timer.getTimer().newTimeout(this, this.probeRate, MILLISECONDS);
+ }
return;
}
@@ -276,16 +282,18 @@
}
}
- // reschedule timer
- timeout = Timer.getTimer().newTimeout(this, this.probeRate, MILLISECONDS);
+ if (!isStopped()) {
+ // reschedule timer
+ timeout = Timer.getTimer().newTimeout(this, this.probeRate, MILLISECONDS);
+ }
}
- public void stop() {
+ public synchronized void stop() {
timeout.cancel();
isStopped = true;
}
- public void start() {
+ public synchronized void start() {
if (isStopped) {
timeout = Timer.getTimer().newTimeout(this, 0, MILLISECONDS);
isStopped = false;
diff --git a/providers/openflow/flow/src/main/java/org/onlab/onos/provider/of/flow/impl/FlowEntryBuilder.java b/providers/openflow/flow/src/main/java/org/onlab/onos/provider/of/flow/impl/FlowEntryBuilder.java
index 02ec827..e5131df 100644
--- a/providers/openflow/flow/src/main/java/org/onlab/onos/provider/of/flow/impl/FlowEntryBuilder.java
+++ b/providers/openflow/flow/src/main/java/org/onlab/onos/provider/of/flow/impl/FlowEntryBuilder.java
@@ -289,7 +289,10 @@
case OCH_SIGID:
builder.matchLambda(match.get(MatchField.OCH_SIGID).getChannelNumber());
break;
- case OCH_SIGTYPE_BASIC:
+ case OCH_SIGTYPE:
+ builder.matchOpticalSignalType(match.get(MatchField
+ .OCH_SIGTYPE).getValue());
+ break;
case ARP_OP:
case ARP_SHA:
case ARP_SPA:
diff --git a/providers/openflow/flow/src/main/java/org/onlab/onos/provider/of/flow/impl/FlowModBuilder.java b/providers/openflow/flow/src/main/java/org/onlab/onos/provider/of/flow/impl/FlowModBuilder.java
index b191784..0caf06b 100644
--- a/providers/openflow/flow/src/main/java/org/onlab/onos/provider/of/flow/impl/FlowModBuilder.java
+++ b/providers/openflow/flow/src/main/java/org/onlab/onos/provider/of/flow/impl/FlowModBuilder.java
@@ -19,6 +19,7 @@
import org.onlab.onos.net.flow.FlowRule;
import org.onlab.onos.net.flow.TrafficSelector;
+import org.onlab.onos.net.flow.criteria.Criteria;
import org.onlab.onos.net.flow.criteria.Criteria.EthCriterion;
import org.onlab.onos.net.flow.criteria.Criteria.EthTypeCriterion;
import org.onlab.onos.net.flow.criteria.Criteria.IPCriterion;
@@ -46,6 +47,7 @@
import org.projectfloodlight.openflow.types.OFPort;
import org.projectfloodlight.openflow.types.OFVlanVidMatch;
import org.projectfloodlight.openflow.types.TransportPort;
+import org.projectfloodlight.openflow.types.U8;
import org.projectfloodlight.openflow.types.VlanPcp;
import org.projectfloodlight.openflow.types.VlanVid;
import org.slf4j.Logger;
@@ -197,6 +199,12 @@
mBuilder.setExact(MatchField.OCH_SIGID,
new CircuitSignalID((byte) 1, (byte) 2, lc.lambda(), (short) 1));
break;
+ case OCH_SIGTYPE:
+ Criteria.OpticalSignalTypeCriterion sc =
+ (Criteria.OpticalSignalTypeCriterion) c;
+ mBuilder.setExact(MatchField.OCH_SIGTYPE,
+ U8.of(sc.signalType()));
+ break;
case ARP_OP:
case ARP_SHA:
case ARP_SPA:
diff --git a/providers/openflow/link/src/main/java/org/onlab/onos/provider/of/link/impl/LinkDiscovery.java b/providers/openflow/link/src/main/java/org/onlab/onos/provider/of/link/impl/LinkDiscovery.java
index 02176cc..00c7305 100644
--- a/providers/openflow/link/src/main/java/org/onlab/onos/provider/of/link/impl/LinkDiscovery.java
+++ b/providers/openflow/link/src/main/java/org/onlab/onos/provider/of/link/impl/LinkDiscovery.java
@@ -87,7 +87,7 @@
protected final Map<Integer, OFPortDesc> ports;
private Timeout timeout;
- /**
+ /*
* Instantiates discovery manager for the given physical switch. Creates a
* generic LLDP packet that will be customized for the port it is sent out on.
* Starts the the timer for the discovery process.
@@ -281,7 +281,7 @@
return "LinkDiscovery " + this.sw.getStringId();
}
- /**
+ /*
* Handles an incoming LLDP packet. Creates link in topology and sends ACK
* to port where LLDP originated.
*/
diff --git a/tools/build/envDefaults b/tools/build/envDefaults
index 184ad33..bfe9366 100644
--- a/tools/build/envDefaults
+++ b/tools/build/envDefaults
@@ -5,7 +5,7 @@
# M2 repository and Karaf gold bits
export M2_REPO=${M2_REPO:-~/.m2/repository}
-export KARAF_VERSION=${KARAF_VERSION:-3.0.1}
+export KARAF_VERSION=${KARAF_VERSION:-3.0.2}
export KARAF_ZIP=${KARAF_ZIP:-~/Downloads/apache-karaf-$KARAF_VERSION.zip}
export KARAF_TAR=${KARAF_TAR:-~/Downloads/apache-karaf-$KARAF_VERSION.tar.gz}
export KARAF_DIST=$(basename $KARAF_ZIP .zip)
diff --git a/tools/dev/bash_profile b/tools/dev/bash_profile
index 1a0c93f..7b21916 100644
--- a/tools/dev/bash_profile
+++ b/tools/dev/bash_profile
@@ -8,7 +8,9 @@
# Setup some environmental context for developers
if [ -z "${JAVA_HOME}" ]; then
if [ -x /usr/libexec/java_home ]; then
- export JAVA_HOME=$(/usr/libexec/java_home -v 1.7)
+ export JAVA_HOME=$(/usr/libexec/java_home -v 1.8)
+ elif [ -d /usr/lib/jvm/java-8-oracle ]; then
+ export JAVA_HOME="/usr/lib/jvm/java-8-oracle"
elif [ -d /usr/lib/jvm/java-7-openjdk-amd64 ]; then
export JAVA_HOME="/usr/lib/jvm/java-7-openjdk-amd64"
fi
@@ -16,7 +18,7 @@
export MAVEN=${MAVEN:-~/Applications/apache-maven-3.2.2}
-export KARAF_VERSION=${KARAF_VERSION:-3.0.1}
+export KARAF_VERSION=${KARAF_VERSION:-3.0.2}
export KARAF=${KARAF:-~/Applications/apache-karaf-$KARAF_VERSION}
export KARAF_LOG=$KARAF/data/log/karaf.log
@@ -39,6 +41,7 @@
alias ob='onos-build'
alias obi='onos-build -Dmaven.test.failure.ignore=true'
alias obs='onos-build-selective'
+alias obd='onos-build-docs'
alias op='onos-package'
alias ot='onos-test'
alias ol='onos-log'
diff --git a/tools/dev/bin/onos-local-log b/tools/dev/bin/onos-local-log
index 83bdf9c..ea93d7f 100755
--- a/tools/dev/bin/onos-local-log
+++ b/tools/dev/bin/onos-local-log
@@ -2,7 +2,7 @@
# ----------------------------------------------------------------------------
# Continuously watches the Apache Karaf log; survives 'karaf clean'
# ----------------------------------------------------------------------------
-KARAF_LOG=${KARAF_LOG:-~/apache-karaf-3.0.1/data/log/karaf.log}
+KARAF_LOG=${KARAF_LOG:-~/apache-karaf-3.0.2/data/log/karaf.log}
while true; do
[ ! -f $KARAF_LOG ] && sleep 2 && continue
diff --git a/tools/dev/header.txt b/tools/dev/header.txt
new file mode 100644
index 0000000..6c18c92
--- /dev/null
+++ b/tools/dev/header.txt
@@ -0,0 +1,13 @@
+Copyright $today.year Open Networking Laboratory
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
\ No newline at end of file
diff --git a/tools/dev/onos.cshrc b/tools/dev/onos.cshrc
index feff165..17d1f06 100644
--- a/tools/dev/onos.cshrc
+++ b/tools/dev/onos.cshrc
@@ -16,7 +16,9 @@
# Setup some environmental context for developers
if ( ! $?JAVA_HOME ) then
if ( -x /usr/libexec/java_home ) then
- setenv JAVA_HOME `/usr/libexec/java_home -v 1.7`
+ setenv JAVA_HOME `/usr/libexec/java_home -v 1.8`
+ else if ( -d /usr/lib/jvm/java-8-oracle ) then
+ setenv JAVA_HOME /usr/lib/jvm/java-8-oracle
else if ( -d /usr/lib/jvm/java-7-openjdk-amd64 ) then
setenv JAVA_HOME /usr/lib/jvm/java-7-openjdk-amd64
endif
@@ -24,8 +26,11 @@
if ( ! $?MAVEN ) then
setenv MAVEN $HOME/Applications/apache-maven-3.2.2
endif
+if ( ! $?KARAF_VERSION ) then
+ setenv KARAF_VERSION 3.0.2
+endif
if ( ! $?KARAF ) then
- setenv KARAF $HOME/Applications/apache-karaf-3.0.1
+ setenv KARAF $HOME/Applications/apache-karaf-$KARAF_VERSION
endif
setenv KARAF_LOG $KARAF/data/log/karaf.log
diff --git a/tools/package/bin/onos b/tools/package/bin/onos
index c94304e..84a41e0 100755
--- a/tools/package/bin/onos
+++ b/tools/package/bin/onos
@@ -3,7 +3,15 @@
# ONOS command-line client
# -----------------------------------------------------------------------------
-export JAVA_HOME=${JAVA_HOME:-/usr/lib/jvm/java-7-openjdk-amd64/}
+if [ -z "${JAVA_HOME}" ]; then
+ if [ -x /usr/libexec/java_home ]; then
+ export JAVA_HOME=$(/usr/libexec/java_home -v 1.8)
+ elif [ -d /usr/lib/jvm/java-8-oracle ]; then
+ export JAVA_HOME="/usr/lib/jvm/java-8-oracle"
+ elif [ -d /usr/lib/jvm/java-7-openjdk-amd64 ]; then
+ export JAVA_HOME="/usr/lib/jvm/java-7-openjdk-amd64"
+ fi
+fi
cd $(dirname $0)/../apache-karaf-$KARAF_VERSION/bin
./client -h localhost -u karaf "$@"
diff --git a/tools/package/bin/onos-service b/tools/package/bin/onos-service
index 9250d5b..ae6d970 100755
--- a/tools/package/bin/onos-service
+++ b/tools/package/bin/onos-service
@@ -3,7 +3,7 @@
# Starts ONOS Apache Karaf container
# -----------------------------------------------------------------------------
-export JAVA_HOME=${JAVA_HOME:-/usr/lib/jvm/java-7-openjdk-amd64/}
+#export JAVA_HOME=${JAVA_HOME:-/usr/lib/jvm/java-7-openjdk-amd64/}
export JAVA_OPTS="${JAVA_OPTS:--Xms256M -Xmx2048M}"
cd /opt/onos
diff --git a/tools/package/debian/onos.conf b/tools/package/debian/onos.conf
index 1d8f10a..888c02b 100644
--- a/tools/package/debian/onos.conf
+++ b/tools/package/debian/onos.conf
@@ -11,8 +11,8 @@
respawn
env LANG=en_US.UTF-8
-env JAVA_HOME=/usr/lib/jvm/java-7-openjdk-amd64
-env NEW_JAVA_HOME=/usr/lib/jvm/java-8-oracle/
+#env JAVA_HOME=/usr/lib/jvm/java-7-openjdk-amd64
+#env NEW_JAVA_HOME=/usr/lib/jvm/java-8-oracle/
pre-stop script
/opt/onos/bin/onos halt 2>/opt/onos/var/stderr.log
diff --git a/tools/test/bin/onos-log b/tools/test/bin/onos-log
index ff52eee..38ff50c 100755
--- a/tools/test/bin/onos-log
+++ b/tools/test/bin/onos-log
@@ -19,11 +19,11 @@
LOG=$ONOS_INSTALL_DIR/$KARAF_DIST/instances/$instance/data/log/karaf.log || \
LOG=$ONOS_INSTALL_DIR/log/karaf.log
-trap "ssh $remote 'ps -ef | grep \"tail -n 512\" | grep -v grep | cut -c10-15 | xargs kill'" EXIT
if [ $less -eq 1 ]; then
ssh -t $remote "less $LOG"
else
+ trap "ssh $remote 'ps -ef | grep \"tail -n 512\" | grep -v grep | cut -c10-15 | xargs kill'" EXIT
ssh $remote "
while true; do
echo ==================================================================
diff --git a/tools/test/cells/local b/tools/test/cells/local
index e8ca7e9..acf76ef 100644
--- a/tools/test/cells/local
+++ b/tools/test/cells/local
@@ -6,4 +6,4 @@
export OCN="192.168.56.103"
export OCI="${OC1}"
-export ONOS_FEATURES="${ONOS_FEATURES:-webconsole,onos-api,onos-core,onos-cli,onos-openflow,onos-rest,onos-app-fwd,onos-app-proxyarp,onos-app-tvue}"
+export ONOS_FEATURES="${ONOS_FEATURES:-webconsole,onos-api,onos-core,onos-cli,onos-openflow,onos-gui,onos-rest,onos-app-fwd,onos-app-proxyarp,onos-app-tvue}"
diff --git a/tools/test/cells/triple b/tools/test/cells/triple
index 439b837..880dd55 100644
--- a/tools/test/cells/triple
+++ b/tools/test/cells/triple
@@ -7,4 +7,4 @@
export OCN="192.168.56.103"
export OCI="${OC1}"
-export ONOS_FEATURES=""
+export ONOS_FEATURES="${ONOS_FEATURES:-webconsole,onos-api,onos-core,onos-cli,onos-openflow,onos-gui,onos-rest,onos-app-fwd,onos-app-proxyarp,onos-app-tvue}"
diff --git a/tools/test/topos/onos.py b/tools/test/topos/onos.py
index 9566d00..80a8005 100755
--- a/tools/test/topos/onos.py
+++ b/tools/test/topos/onos.py
@@ -36,7 +36,7 @@
#self.checkListening()
self.onosDir = onosDir
- self.karafDir = onosDir + 'apache-karaf-3.0.1/'
+ self.karafDir = onosDir + 'apache-karaf-3.0.2/'
self.instanceDir = self.karafDir
# add default modules
diff --git a/utils/junit/src/test/java/org/onlab/junit/TestUtilsTest.java b/utils/junit/src/test/java/org/onlab/junit/TestUtilsTest.java
index 39712ba..68e407f 100644
--- a/utils/junit/src/test/java/org/onlab/junit/TestUtilsTest.java
+++ b/utils/junit/src/test/java/org/onlab/junit/TestUtilsTest.java
@@ -101,9 +101,9 @@
@Test
public void testSetGetPrivateField() throws TestUtilsException {
- assertEquals(42, TestUtils.getField(test, "privateField"));
+ assertEquals(42, (int) TestUtils.getField(test, "privateField"));
TestUtils.setField(test, "privateField", 0xDEAD);
- assertEquals(0xDEAD, TestUtils.getField(test, "privateField"));
+ assertEquals(0xDEAD, (int) TestUtils.getField(test, "privateField"));
}
/**
@@ -114,9 +114,9 @@
@Test
public void testSetGetProtectedField() throws TestUtilsException {
- assertEquals(2501, TestUtils.getField(test, "protectedField"));
+ assertEquals(2501, (int) TestUtils.getField(test, "protectedField"));
TestUtils.setField(test, "protectedField", 0xBEEF);
- assertEquals(0xBEEF, TestUtils.getField(test, "protectedField"));
+ assertEquals(0xBEEF, (int) TestUtils.getField(test, "protectedField"));
}
/**
diff --git a/utils/misc/src/main/java/org/onlab/graph/KshortestPathSearch.java b/utils/misc/src/main/java/org/onlab/graph/KshortestPathSearch.java
new file mode 100644
index 0000000..c01c0d0
--- /dev/null
+++ b/utils/misc/src/main/java/org/onlab/graph/KshortestPathSearch.java
@@ -0,0 +1,286 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.onlab.graph;
+
+import java.util.ArrayList;
+//import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+//import java.util.Map;
+//import java.util.PriorityQueue;
+import java.util.Set;
+
+/**
+ * K-shortest-path graph search algorithm capable of finding not just one,
+ * but K shortest paths with ascending order between the source and destinations.
+ */
+
+public class KshortestPathSearch<V extends Vertex, E extends Edge<V>> {
+
+ // Define class variables.
+ private Graph<V, E> immutableGraph;
+ private MutableGraph<V, E> mutableGraph;
+ private List<List<E>> pathResults = new ArrayList<List<E>>();
+ private List<List<E>> pathCandidates = new ArrayList<List<E>>();
+ private V source;
+ private V sink;
+ private int numK = 0;
+ private EdgeWeight<V, E> weight = null;
+ // private PriorityQueue<List<E>> pathCandidates = new PriorityQueue<List<E>>();
+
+ // Initialize the graph.
+ public KshortestPathSearch(Graph<V, E> graph) {
+ immutableGraph = graph;
+ mutableGraph = new MutableAdjacencyListsGraph(graph.getVertexes(),
+ graph.getEdges());
+ }
+
+ public List<List<E>> search(V src,
+ V dst,
+ EdgeWeight<V, E> wei,
+ int k) {
+
+ weight = wei;
+ source = src;
+ sink = dst;
+ numK = k;
+ // pathCandidates = new PriorityQueue<List<E>>();
+
+ pathResults.clear();
+ pathCandidates.clear();
+
+ // Double check the parameters
+ checkArguments(immutableGraph, src, dst, numK);
+
+ // DefaultResult result = new DefaultResult(src, dst);
+
+ searchKShortestPaths();
+
+ return pathResults;
+ }
+
+ private void checkArguments(Graph<V, E> graph, V src, V dst, int k) {
+ if (graph == null) {
+ throw new NullPointerException("graph is null");
+ }
+ if (!graph.getVertexes().contains(src)) {
+ throw new NullPointerException("source node does not exist");
+ }
+ if (!graph.getVertexes().contains(dst)) {
+ throw new NullPointerException("target node does not exist");
+ }
+ if (k <= 0) {
+ throw new NullPointerException("K is negative or 0");
+ }
+ if (weight == null) {
+ throw new NullPointerException("the cost matrix is null");
+ }
+ }
+
+ private void searchKShortestPaths() {
+ // Step 1: find the shortest path.
+ List<E> shortestPath = searchShortestPath(immutableGraph, source, sink);
+ // no path exists, exit.
+ if (shortestPath == null) {
+ return;
+ }
+
+ // Step 2: update the results.
+ pathResults.add(shortestPath);
+ // pathCandidates.add(shortestPath);
+
+ // Step 3: find the other K-1 paths.
+ while (/*pathCandidates.size() > 0 &&*/pathResults.size() < numK) {
+ // 3.1 the spur node ranges from the first node to the last node in the previous k-shortest path.
+ List<E> lastPath = pathResults.get(pathResults.size() - 1);
+ for (int i = 0; i < lastPath.size(); i++) {
+ // 3.1.1 convert the graph into mutable.
+ convertGraph();
+ // 3.1.2 transform the graph.
+ List<E> rootPath = createSpurNode(lastPath, i);
+ transformGraph(rootPath);
+ // 3.1.3 find the deviation node.
+ V devNode;
+ devNode = getDevNode(rootPath);
+ List<E> spurPath;
+ // 3.1.4 find the shortest path in the transformed graph.
+ spurPath = searchShortestPath(mutableGraph, devNode, sink);
+ // 3.1.5 update the path candidates.
+ if (spurPath != null) {
+ // totalPath = rootPath + spurPath;
+ rootPath.addAll(spurPath);
+ pathCandidates.add(rootPath);
+ }
+ }
+ // 3.2 if there is no spur path, exit.
+ if (pathCandidates.size() == 0) {
+ break;
+ }
+ // 3.3 add the path into the results.
+ addPathResult();
+ }
+ }
+
+ private List<E> searchShortestPath(Graph<V, E> graph, V src, V dst) {
+ // Determine the shortest path from the source to the destination by using the Dijkstra algorithm.
+ DijkstraGraphSearch dijkstraAlg = new DijkstraGraphSearch();
+ Set<Path> paths = dijkstraAlg.search(graph, src, dst, weight).paths();
+ Iterator<Path> itr = paths.iterator();
+ if (!itr.hasNext()) {
+ return null;
+ }
+ // return the first shortest path only.
+ return (List<E>) itr.next().edges();
+ }
+
+ private void convertGraph() {
+ // clear the mutableGraph first
+ if (mutableGraph != null) {
+ ((MutableAdjacencyListsGraph) mutableGraph).clear();
+ }
+
+ // create a immutableGraph
+ Set<E> copyEa = immutableGraph.getEdges();
+ Set<V> copyVa = immutableGraph.getVertexes();
+ for (V vertex : copyVa) {
+ mutableGraph.addVertex(vertex);
+ }
+ for (E edge : copyEa) {
+ mutableGraph.addEdge(edge);
+ }
+ }
+
+ private V getDevNode(List<E> path) {
+ V srcA;
+ V dstB;
+
+ if (path.size() == 0) {
+ return source;
+ }
+
+ E temp1 = path.get(path.size() - 1);
+ srcA = temp1.src();
+ dstB = temp1.dst();
+
+ if (path.size() == 1) {
+ if (srcA.equals(source)) {
+ return dstB;
+ } else {
+ return srcA;
+ }
+ } else {
+ E temp2 = path.get(path.size() - 2);
+ if (srcA.equals(temp2.src()) || srcA.equals(temp2.dst())) {
+ return dstB;
+ } else {
+ return srcA;
+ }
+ }
+ }
+
+ private List<E> createSpurNode(List<E> path, int n) {
+ List<E> root = new ArrayList<E>();
+
+ for (int i = 0; i < n; i++) {
+ root.add(path.get(i));
+ }
+ return root;
+ }
+
+ private void transformGraph(List<E> rootPath) {
+ List<E> prePath;
+ //remove edges
+ for (int i = 0; i < pathResults.size(); i++) {
+ prePath = pathResults.get(i);
+ if (prePath.size() == 1) {
+ mutableGraph.removeEdge(prePath.get(0));
+ } else if (comparePath(rootPath, prePath)) {
+ for (int j = 0; j <= rootPath.size(); j++) {
+ mutableGraph.removeEdge(prePath.get(j));
+ }
+ }
+ }
+ for (int i = 0; i < pathCandidates.size(); i++) {
+ prePath = pathCandidates.get(i);
+ if (prePath.size() == 1) {
+ mutableGraph.removeEdge(prePath.get(0));
+ } else if (comparePath(rootPath, prePath)) {
+ for (int j = 0; j <= rootPath.size(); j++) {
+ mutableGraph.removeEdge(prePath.get(j));
+ }
+ }
+ }
+
+ if (rootPath.size() == 0) {
+ return;
+ }
+
+ //remove nodes
+ List<V> nodes = new ArrayList<V>();
+ nodes.add(source);
+ V pre = source;
+ V srcA;
+ V dstB;
+ for (int i = 0; i < rootPath.size() - 1; i++) {
+ E temp = rootPath.get(i);
+ srcA = temp.src();
+ dstB = temp.dst();
+
+ if (srcA.equals(pre)) {
+ nodes.add(dstB);
+ pre = dstB;
+ } else {
+ nodes.add(srcA);
+ pre = srcA;
+ }
+ }
+ for (int i = 0; i < nodes.size(); i++) {
+ mutableGraph.removeVertex(nodes.get(i));
+ }
+ }
+
+ private boolean comparePath(List<E> path1, List<E> path2) {
+ if (path1.size() > path2.size()) {
+ return false;
+ }
+ if (path1.size() == 0) {
+ return true;
+ }
+ for (int i = 0; i < path1.size(); i++) {
+ if (path1.get(i) != path2.get(i)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private void addPathResult() {
+ List<E> sp;
+ sp = pathCandidates.get(0);
+ for (int i = 1; i < pathCandidates.size(); i++) {
+ if (sp.size() > pathCandidates.get(i).size()) {
+ sp = pathCandidates.get(i);
+ }
+ }
+ pathResults.add(sp);
+ // Log.info(sp.toString());
+ pathCandidates.remove(sp);
+ }
+
+}
diff --git a/utils/misc/src/main/java/org/onlab/graph/MutableAdjacencyListsGraph.java b/utils/misc/src/main/java/org/onlab/graph/MutableAdjacencyListsGraph.java
new file mode 100644
index 0000000..044a60d
--- /dev/null
+++ b/utils/misc/src/main/java/org/onlab/graph/MutableAdjacencyListsGraph.java
@@ -0,0 +1,165 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.onlab.graph;
+
+import static com.google.common.base.MoreObjects.toStringHelper;
+
+import java.util.HashSet;
+import java.util.Objects;
+import java.util.Set;
+
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.SetMultimap;
+
+public class MutableAdjacencyListsGraph<V extends Vertex, E extends Edge<V>>
+implements MutableGraph<V, E> {
+ private Set<V> vertexes = new HashSet<V>();
+ private Set<E> edges = new HashSet<E>();
+
+ private SetMultimap<V, E> sources = HashMultimap.create();
+ private SetMultimap<V, E> destinations = HashMultimap.create();
+
+ /**
+ * Creates a graph comprising of the specified vertexes and edges.
+ *
+ * @param vertex set of graph vertexes
+ * @param edge set of graph edges
+ */
+ public MutableAdjacencyListsGraph(Set<V> vertex, Set<E> edge) {
+ vertexes.addAll(vertex);
+ edges.addAll(edge);
+ for (E e : edge) {
+ sources.put(e.src(), e);
+ vertexes.add(e.src());
+ destinations.put(e.dst(), e);
+ vertexes.add(e.dst());
+ }
+ }
+
+ @Override
+ public Set<V> getVertexes() {
+ return vertexes;
+ }
+
+ @Override
+ public Set<E> getEdges() {
+ return edges;
+ }
+
+ @Override
+ public Set<E> getEdgesFrom(V src) {
+ return sources.get(src);
+ }
+
+ @Override
+ public Set<E> getEdgesTo(V dst) {
+ return destinations.get(dst);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj instanceof MutableAdjacencyListsGraph) {
+ MutableAdjacencyListsGraph that = (MutableAdjacencyListsGraph) obj;
+ return this.getClass() == that.getClass() &&
+ Objects.equals(this.vertexes, that.vertexes) &&
+ Objects.equals(this.edges, that.edges);
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(vertexes, edges);
+ }
+
+ @Override
+ public String toString() {
+ return toStringHelper(this)
+ .add("vertexes", vertexes)
+ .add("edges", edges)
+ .toString();
+ }
+
+
+ @Override
+ public void addVertex(V vertex) {
+ if (vertexes != null) {
+ if (!vertexes.contains(vertex)) {
+ vertexes.add(vertex);
+ }
+ }
+ }
+
+ @Override
+ public void removeVertex(V vertex) {
+ // TODO Auto-generated method stub
+ if (vertexes != null && edges != null) {
+ if (vertexes.contains(vertex)) {
+ vertexes.remove(vertex);
+ Set<E> srcEdgesList = sources.get(vertex);
+ Set<E> dstEdgesList = destinations.get(vertex);
+ edges.removeAll(srcEdgesList);
+ edges.removeAll(dstEdgesList);
+ sources.remove(vertex, srcEdgesList);
+ sources.remove(vertex, dstEdgesList);
+ }
+ }
+ }
+
+ @Override
+ public void addEdge(E edge) {
+ if (edges != null) {
+ if (!edges.contains(edge)) {
+ edges.add(edge);
+ sources.put(edge.src(), edge);
+ destinations.put(edge.dst(), edge);
+ }
+ }
+ }
+
+ @Override
+ public void removeEdge(E edge) {
+ if (edges != null) {
+ if (edges.contains(edge)) {
+ edges.remove(edge);
+ sources.remove(edge.src(), edge);
+ destinations.remove(edge.dst(), edge);
+ }
+ }
+ }
+
+ @Override
+ public Graph<V, E> toImmutable() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ /**
+ * Clear the graph.
+ */
+ public void clear() {
+ edges.clear();
+ vertexes.clear();
+ sources.clear();
+ destinations.clear();
+ }
+}
diff --git a/utils/misc/src/main/java/org/onlab/packet/ARP.java b/utils/misc/src/main/java/org/onlab/packet/ARP.java
index 91713c1..86fb289 100644
--- a/utils/misc/src/main/java/org/onlab/packet/ARP.java
+++ b/utils/misc/src/main/java/org/onlab/packet/ARP.java
@@ -55,6 +55,7 @@
/**
* @param hwType
* the hardwareType to set
+ * @return this
*/
public ARP setHardwareType(final short hwType) {
this.hardwareType = hwType;
@@ -71,6 +72,7 @@
/**
* @param protoType
* the protocolType to set
+ * @return this
*/
public ARP setProtocolType(final short protoType) {
this.protocolType = protoType;
@@ -87,6 +89,7 @@
/**
* @param hwAddressLength
* the hardwareAddressLength to set
+ * @return this
*/
public ARP setHardwareAddressLength(final byte hwAddressLength) {
this.hardwareAddressLength = hwAddressLength;
@@ -103,6 +106,7 @@
/**
* @param protoAddressLength
* the protocolAddressLength to set
+ * @return this
*/
public ARP setProtocolAddressLength(final byte protoAddressLength) {
this.protocolAddressLength = protoAddressLength;
@@ -119,6 +123,7 @@
/**
* @param op
* the opCode to set
+ * @return this
*/
public ARP setOpCode(final short op) {
this.opCode = op;
@@ -135,6 +140,7 @@
/**
* @param senderHWAddress
* the senderHardwareAddress to set
+ * @return this
*/
public ARP setSenderHardwareAddress(final byte[] senderHWAddress) {
this.senderHardwareAddress = senderHWAddress;
@@ -151,6 +157,7 @@
/**
* @param senderProtoAddress
* the senderProtocolAddress to set
+ * @return this
*/
public ARP setSenderProtocolAddress(final byte[] senderProtoAddress) {
this.senderProtocolAddress = senderProtoAddress;
@@ -173,6 +180,7 @@
/**
* @param targetHWAddress
* the targetHardwareAddress to set
+ * @return this
*/
public ARP setTargetHardwareAddress(final byte[] targetHWAddress) {
this.targetHardwareAddress = targetHWAddress;
@@ -206,6 +214,7 @@
/**
* @param targetProtoAddress
* the targetProtocolAddress to set
+ * @return this
*/
public ARP setTargetProtocolAddress(final byte[] targetProtoAddress) {
this.targetProtocolAddress = targetProtoAddress;
diff --git a/utils/misc/src/main/java/org/onlab/packet/DHCP.java b/utils/misc/src/main/java/org/onlab/packet/DHCP.java
index a250010..8dba13c 100644
--- a/utils/misc/src/main/java/org/onlab/packet/DHCP.java
+++ b/utils/misc/src/main/java/org/onlab/packet/DHCP.java
@@ -95,6 +95,7 @@
/**
* @param opCode
* the opCode to set
+ * @return this
*/
public DHCP setOpCode(final byte opCode) {
this.opCode = opCode;
@@ -111,6 +112,7 @@
/**
* @param hardwareType
* the hardwareType to set
+ * @return this
*/
public DHCP setHardwareType(final byte hardwareType) {
this.hardwareType = hardwareType;
@@ -127,6 +129,7 @@
/**
* @param hardwareAddressLength
* the hardwareAddressLength to set
+ * @return this
*/
public DHCP setHardwareAddressLength(final byte hardwareAddressLength) {
this.hardwareAddressLength = hardwareAddressLength;
@@ -143,6 +146,7 @@
/**
* @param hops
* the hops to set
+ * @return this
*/
public DHCP setHops(final byte hops) {
this.hops = hops;
@@ -159,6 +163,7 @@
/**
* @param transactionId
* the transactionId to set
+ * @return this
*/
public DHCP setTransactionId(final int transactionId) {
this.transactionId = transactionId;
@@ -175,6 +180,7 @@
/**
* @param seconds
* the seconds to set
+ * @return this
*/
public DHCP setSeconds(final short seconds) {
this.seconds = seconds;
@@ -191,6 +197,7 @@
/**
* @param flags
* the flags to set
+ * @return this
*/
public DHCP setFlags(final short flags) {
this.flags = flags;
@@ -207,6 +214,7 @@
/**
* @param clientIPAddress
* the clientIPAddress to set
+ * @return this
*/
public DHCP setClientIPAddress(final int clientIPAddress) {
this.clientIPAddress = clientIPAddress;
@@ -223,6 +231,7 @@
/**
* @param yourIPAddress
* the yourIPAddress to set
+ * @return this
*/
public DHCP setYourIPAddress(final int yourIPAddress) {
this.yourIPAddress = yourIPAddress;
@@ -239,6 +248,7 @@
/**
* @param serverIPAddress
* the serverIPAddress to set
+ * @return this
*/
public DHCP setServerIPAddress(final int serverIPAddress) {
this.serverIPAddress = serverIPAddress;
@@ -255,6 +265,7 @@
/**
* @param gatewayIPAddress
* the gatewayIPAddress to set
+ * @return this
*/
public DHCP setGatewayIPAddress(final int gatewayIPAddress) {
this.gatewayIPAddress = gatewayIPAddress;
@@ -271,6 +282,7 @@
/**
* @param clientHardwareAddress
* the clientHardwareAddress to set
+ * @return this
*/
public DHCP setClientHardwareAddress(final byte[] clientHardwareAddress) {
this.clientHardwareAddress = clientHardwareAddress;
@@ -303,6 +315,7 @@
/**
* @param options
* the options to set
+ * @return this
*/
public DHCP setOptions(final List<DHCPOption> options) {
this.options = options;
@@ -334,6 +347,7 @@
/**
* @param server
* the serverName to set
+ * @return this
*/
public DHCP setServerName(final String server) {
this.serverName = server;
@@ -350,6 +364,7 @@
/**
* @param bootFile
* the bootFileName to set
+ * @return this
*/
public DHCP setBootFileName(final String bootFile) {
this.bootFileName = bootFile;
diff --git a/utils/misc/src/main/java/org/onlab/packet/DHCPOption.java b/utils/misc/src/main/java/org/onlab/packet/DHCPOption.java
index c1493d0..1b6c670 100644
--- a/utils/misc/src/main/java/org/onlab/packet/DHCPOption.java
+++ b/utils/misc/src/main/java/org/onlab/packet/DHCPOption.java
@@ -38,6 +38,7 @@
/**
* @param code
* the code to set
+ * @return this
*/
public DHCPOption setCode(final byte code) {
this.code = code;
@@ -54,6 +55,7 @@
/**
* @param length
* the length to set
+ * @return this
*/
public DHCPOption setLength(final byte length) {
this.length = length;
@@ -70,6 +72,7 @@
/**
* @param data
* the data to set
+ * @return this
*/
public DHCPOption setData(final byte[] data) {
this.data = data;
diff --git a/utils/misc/src/main/java/org/onlab/packet/Data.java b/utils/misc/src/main/java/org/onlab/packet/Data.java
index 311cc93..f3a1092 100644
--- a/utils/misc/src/main/java/org/onlab/packet/Data.java
+++ b/utils/misc/src/main/java/org/onlab/packet/Data.java
@@ -33,7 +33,7 @@
}
/**
- * @param data
+ * @param data the data
*/
public Data(final byte[] data) {
this.data = data;
@@ -49,6 +49,7 @@
/**
* @param data
* the data to set
+ * @return self
*/
public Data setData(final byte[] data) {
this.data = data;
diff --git a/utils/misc/src/main/java/org/onlab/packet/Ethernet.java b/utils/misc/src/main/java/org/onlab/packet/Ethernet.java
index 96ec021..3519338 100644
--- a/utils/misc/src/main/java/org/onlab/packet/Ethernet.java
+++ b/utils/misc/src/main/java/org/onlab/packet/Ethernet.java
@@ -255,6 +255,7 @@
*
* @param pd
* the pad to set
+ * @return this
*/
public Ethernet setPad(final boolean pd) {
this.pad = pd;
@@ -345,7 +346,7 @@
/**
* Checks to see if a string is a valid MAC address.
*
- * @param macAddress
+ * @param macAddress string to test if it is a valid MAC
* @return True if macAddress is a valid MAC, False otherwise
*/
public static boolean isMACAddress(final String macAddress) {
@@ -368,7 +369,7 @@
* matter, and returns a corresponding byte[].
*
* @param macAddress
- * The MAC address to convert into a bye array
+ * The MAC address to convert into a byte array
* @return The macAddress as a byte array
*/
public static byte[] toMACAddress(final String macAddress) {
@@ -379,7 +380,7 @@
* Accepts a MAC address and returns the corresponding long, where the MAC
* bytes are set on the lower order bytes of the long.
*
- * @param macAddress
+ * @param macAddress MAC address as a byte array
* @return a long containing the mac address bytes
*/
public static long toLong(final byte[] macAddress) {
@@ -389,7 +390,7 @@
/**
* Converts a long MAC address to a byte array.
*
- * @param macAddress
+ * @param macAddress MAC address set on the lower order bytes of the long
* @return the bytes of the mac address
*/
public static byte[] toByteArray(final long macAddress) {
diff --git a/utils/misc/src/main/java/org/onlab/packet/ICMP.java b/utils/misc/src/main/java/org/onlab/packet/ICMP.java
index d208727d..6e6dd5f 100644
--- a/utils/misc/src/main/java/org/onlab/packet/ICMP.java
+++ b/utils/misc/src/main/java/org/onlab/packet/ICMP.java
@@ -39,6 +39,7 @@
/**
* @param icmpType
* to set
+ * @return this
*/
public ICMP setIcmpType(final byte icmpType) {
this.icmpType = icmpType;
@@ -55,6 +56,7 @@
/**
* @param icmpCode
* code to set
+ * @return this
*/
public ICMP setIcmpCode(final byte icmpCode) {
this.icmpCode = icmpCode;
@@ -71,6 +73,7 @@
/**
* @param checksum
* the checksum to set
+ * @return this
*/
public ICMP setChecksum(final short checksum) {
this.checksum = checksum;
diff --git a/utils/misc/src/main/java/org/onlab/packet/IPacket.java b/utils/misc/src/main/java/org/onlab/packet/IPacket.java
index f1f01e4..6e2f2a0 100644
--- a/utils/misc/src/main/java/org/onlab/packet/IPacket.java
+++ b/utils/misc/src/main/java/org/onlab/packet/IPacket.java
@@ -64,7 +64,7 @@
/**
* Deserializes this packet layer and all possible payloads.
*
- * @param data
+ * @param data bytes to deserialize
* @param offset
* offset to start deserializing from
* @param length
diff --git a/utils/misc/src/main/java/org/onlab/packet/IPv4.java b/utils/misc/src/main/java/org/onlab/packet/IPv4.java
index 84ddb30..e54e9e7 100644
--- a/utils/misc/src/main/java/org/onlab/packet/IPv4.java
+++ b/utils/misc/src/main/java/org/onlab/packet/IPv4.java
@@ -76,6 +76,7 @@
/**
* @param version
* the version to set
+ * @return this
*/
public IPv4 setVersion(final byte version) {
this.version = version;
@@ -99,6 +100,7 @@
/**
* @param diffServ
* the diffServ to set
+ * @return this
*/
public IPv4 setDiffServ(final byte diffServ) {
this.diffServ = diffServ;
@@ -130,6 +132,7 @@
/**
* @param identification
* the identification to set
+ * @return this
*/
public IPv4 setIdentification(final short identification) {
this.identification = identification;
@@ -146,7 +149,8 @@
/**
* @param flags
* the flags to set
- */
+ * @return this
+s */
public IPv4 setFlags(final byte flags) {
this.flags = flags;
return this;
@@ -162,6 +166,7 @@
/**
* @param fragmentOffset
* the fragmentOffset to set
+ * @return this
*/
public IPv4 setFragmentOffset(final short fragmentOffset) {
this.fragmentOffset = fragmentOffset;
@@ -178,6 +183,7 @@
/**
* @param ttl
* the ttl to set
+ * @return this
*/
public IPv4 setTtl(final byte ttl) {
this.ttl = ttl;
@@ -194,6 +200,7 @@
/**
* @param protocol
* the protocol to set
+ * @return this
*/
public IPv4 setProtocol(final byte protocol) {
this.protocol = protocol;
@@ -210,6 +217,7 @@
/**
* @param checksum
* the checksum to set
+ * @return this
*/
public IPv4 setChecksum(final short checksum) {
this.checksum = checksum;
@@ -232,6 +240,7 @@
/**
* @param sourceAddress
* the sourceAddress to set
+ * @return this
*/
public IPv4 setSourceAddress(final int sourceAddress) {
this.sourceAddress = sourceAddress;
@@ -241,6 +250,7 @@
/**
* @param sourceAddress
* the sourceAddress to set
+ * @return this
*/
public IPv4 setSourceAddress(final String sourceAddress) {
this.sourceAddress = IPv4.toIPv4Address(sourceAddress);
@@ -257,6 +267,7 @@
/**
* @param destinationAddress
* the destinationAddress to set
+ * @return this
*/
public IPv4 setDestinationAddress(final int destinationAddress) {
this.destinationAddress = destinationAddress;
@@ -266,6 +277,7 @@
/**
* @param destinationAddress
* the destinationAddress to set
+ * @return this
*/
public IPv4 setDestinationAddress(final String destinationAddress) {
this.destinationAddress = IPv4.toIPv4Address(destinationAddress);
@@ -282,6 +294,7 @@
/**
* @param options
* the options to set
+ * @return this
*/
public IPv4 setOptions(final byte[] options) {
if (options != null && options.length % 4 > 0) {
diff --git a/utils/misc/src/main/java/org/onlab/packet/Ip4Address.java b/utils/misc/src/main/java/org/onlab/packet/Ip4Address.java
index 7b7b989..114f126 100644
--- a/utils/misc/src/main/java/org/onlab/packet/Ip4Address.java
+++ b/utils/misc/src/main/java/org/onlab/packet/Ip4Address.java
@@ -15,203 +15,160 @@
*/
package org.onlab.packet;
+import java.net.InetAddress;
+import java.net.Inet4Address;
+import java.net.Inet6Address;
import java.nio.ByteBuffer;
-import static com.google.common.base.Preconditions.checkNotNull;
+import java.util.Arrays;
+
+import com.google.common.net.InetAddresses;
/**
- * The class representing an IPv4 address.
+ * A class representing an IPv4 address.
* This class is immutable.
*/
-public final class Ip4Address implements Comparable<Ip4Address> {
- private final int value;
-
- /** The length of the address in bytes (octets). */
- public static final int BYTE_LENGTH = 4;
-
- /** The length of the address in bits. */
- public static final int BIT_LENGTH = BYTE_LENGTH * Byte.SIZE;
+public final class Ip4Address extends IpAddress {
+ public static final IpAddress.Version VERSION = IpAddress.Version.INET;
+ public static final int BYTE_LENGTH = IpAddress.INET_BYTE_LENGTH;
+ public static final int BIT_LENGTH = IpAddress.INET_BIT_LENGTH;
/**
- * Default constructor.
+ * Constructor for given IP address version and address octets.
+ *
+ * @param value the IP address value stored in network byte order
+ * (i.e., the most significant byte first)
+ * @throws IllegalArgumentException if the arguments are invalid
*/
- public Ip4Address() {
- this.value = 0;
+ private Ip4Address(byte[] value) {
+ super(VERSION, value);
}
/**
- * Copy constructor.
+ * Returns the integer value of this IPv4 address.
*
- * @param other the object to copy from
+ * @return the IPv4 address's value as an integer
*/
- public Ip4Address(Ip4Address other) {
- this.value = other.value;
+ public int toInt() {
+ ByteBuffer bb = ByteBuffer.wrap(super.toOctets());
+ return bb.getInt();
}
/**
- * Constructor from an integer value.
+ * Converts an integer into an IPv4 address.
*
- * @param value the value to use
+ * @param value an integer representing an IPv4 address value
+ * @return an IPv4 address
*/
- public Ip4Address(int value) {
- this.value = value;
+ public static Ip4Address valueOf(int value) {
+ byte[] bytes =
+ ByteBuffer.allocate(INET_BYTE_LENGTH).putInt(value).array();
+ return new Ip4Address(bytes);
}
/**
- * Constructor from a byte array with the IPv4 address stored in network
- * byte order (i.e., the most significant byte first).
+ * Converts a byte array into an IPv4 address.
*
- * @param value the value to use
+ * @param value the IPv4 address value stored in network byte order
+ * (i.e., the most significant byte first)
+ * @return an IPv4 address
+ * @throws IllegalArgumentException if the argument is invalid
*/
- public Ip4Address(byte[] value) {
- this(value, 0);
+ public static Ip4Address valueOf(byte[] value) {
+ return new Ip4Address(value);
}
/**
- * Constructor from a byte array with the IPv4 address stored in network
- * byte order (i.e., the most significant byte first), and a given offset
- * from the beginning of the byte array.
- *
+ * Converts a byte array and a given offset from the beginning of the
+ * array into an IPv4 address.
+ * <p>
+ * The IP address is stored in network byte order (i.e., the most
+ * significant byte first).
+ * </p>
* @param value the value to use
* @param offset the offset in bytes from the beginning of the byte array
+ * @return an IPv4 address
+ * @throws IllegalArgumentException if the arguments are invalid
*/
- public Ip4Address(byte[] value, int offset) {
- checkNotNull(value);
-
- // Verify the arguments
- if ((offset < 0) || (offset + BYTE_LENGTH > value.length)) {
- String msg;
- if (value.length < BYTE_LENGTH) {
- msg = "Invalid IPv4 address array: array length: " +
- value.length + ". Must be at least " + BYTE_LENGTH;
- } else {
- msg = "Invalid IPv4 address array: array offset: " +
- offset + ". Must be in the interval [0, " +
- (value.length - BYTE_LENGTH) + "]";
- }
- throw new IllegalArgumentException(msg);
- }
-
- // Read the address
- ByteBuffer bb = ByteBuffer.wrap(value);
- this.value = bb.getInt(offset);
+ public static Ip4Address valueOf(byte[] value, int offset) {
+ IpAddress.checkArguments(VERSION, value, offset);
+ byte[] bc = Arrays.copyOfRange(value, offset, value.length);
+ return Ip4Address.valueOf(bc);
}
/**
- * Constructs an IPv4 address from a string representation of the address.
- *<p>
- * Example: "1.2.3.4"
+ * Converts an InetAddress into an IPv4 address.
*
- * @param value the value to use
+ * @param inetAddress the InetAddress value to use. It must contain an IPv4
+ * address
+ * @return an IPv4 address
+ * @throws IllegalArgumentException if the argument is invalid
*/
- public Ip4Address(String value) {
- checkNotNull(value);
-
- String[] splits = value.split("\\.");
- if (splits.length != 4) {
- final String msg = "Invalid IPv4 address string: " + value;
+ public static Ip4Address valueOf(InetAddress inetAddress) {
+ byte[] bytes = inetAddress.getAddress();
+ if (inetAddress instanceof Inet4Address) {
+ return new Ip4Address(bytes);
+ }
+ if ((inetAddress instanceof Inet6Address) ||
+ (bytes.length == INET6_BYTE_LENGTH)) {
+ final String msg = "Invalid IPv4 version address string: " +
+ inetAddress.toString();
throw new IllegalArgumentException(msg);
}
-
- int result = 0;
- for (int i = 0; i < BYTE_LENGTH; i++) {
- result |= Integer.parseInt(splits[i]) <<
- ((BYTE_LENGTH - (i + 1)) * Byte.SIZE);
+ // Use the number of bytes as a hint
+ if (bytes.length == INET_BYTE_LENGTH) {
+ return new Ip4Address(bytes);
}
- this.value = result;
+ final String msg = "Unrecognized IP version address string: " +
+ inetAddress.toString();
+ throw new IllegalArgumentException(msg);
}
/**
- * Gets the IPv4 address as a byte array.
+ * Converts an IPv4 string literal (e.g., "10.2.3.4") into an IP address.
*
- * @return a byte array with the IPv4 address stored in network byte order
- * (i.e., the most significant byte first).
+ * @param value an IPv4 address value in string form
+ * @return an IPv4 address
+ * @throws IllegalArgumentException if the argument is invalid
*/
- public byte[] toOctets() {
- return ByteBuffer.allocate(BYTE_LENGTH).putInt(value).array();
+ public static Ip4Address valueOf(String value) {
+ InetAddress inetAddress = null;
+ try {
+ inetAddress = InetAddresses.forString(value);
+ } catch (IllegalArgumentException e) {
+ final String msg = "Invalid IP address string: " + value;
+ throw new IllegalArgumentException(msg);
+ }
+ return valueOf(inetAddress);
}
/**
* Creates an IPv4 network mask prefix.
*
- * @param prefixLen the length of the mask prefix. Must be in the interval
- * [0, 32].
+ * @param prefixLength the length of the mask prefix. Must be in the
+ * interval [0, 32]
* @return a new IPv4 address that contains a mask prefix of the
* specified length
+ * @throws IllegalArgumentException if the argument is invalid
*/
- public static Ip4Address makeMaskPrefix(int prefixLen) {
- // Verify the prefix length
- if ((prefixLen < 0) || (prefixLen > Ip4Address.BIT_LENGTH)) {
- final String msg = "Invalid IPv4 prefix length: " + prefixLen +
- ". Must be in the interval [0, 32].";
- throw new IllegalArgumentException(msg);
- }
-
- long v =
- (0xffffffffL << (Ip4Address.BIT_LENGTH - prefixLen)) & 0xffffffffL;
- return new Ip4Address((int) v);
+ public static Ip4Address makeMaskPrefix(int prefixLength) {
+ byte[] mask = IpAddress.makeMaskPrefixArray(VERSION, prefixLength);
+ return new Ip4Address(mask);
}
/**
* Creates an IPv4 address by masking it with a network mask of given
* mask length.
*
- * @param addr the address to mask
- * @param prefixLen the length of the mask prefix. Must be in the interval
- * [0, 32].
+ * @param address the address to mask
+ * @param prefixLength the length of the mask prefix. Must be in the
+ * interval [0, 32]
* @return a new IPv4 address that is masked with a mask prefix of the
* specified length
+ * @throws IllegalArgumentException if the prefix length is invalid
*/
- public static Ip4Address makeMaskedAddress(final Ip4Address addr,
- int prefixLen) {
- Ip4Address mask = Ip4Address.makeMaskPrefix(prefixLen);
- long v = addr.value & mask.value;
-
- return new Ip4Address((int) v);
- }
-
- /**
- * Gets the value of the IPv4 address.
- *
- * @return the value of the IPv4 address
- */
- public int getValue() {
- return value;
- }
-
- /**
- * Converts the IPv4 value to a '.' separated string.
- *
- * @return the IPv4 value as a '.' separated string
- */
- @Override
- public String toString() {
- return ((this.value >> 24) & 0xff) + "." +
- ((this.value >> 16) & 0xff) + "." +
- ((this.value >> 8) & 0xff) + "." +
- (this.value & 0xff);
- }
-
- @Override
- public boolean equals(Object o) {
- if (!(o instanceof Ip4Address)) {
- return false;
- }
- Ip4Address other = (Ip4Address) o;
- if (this.value != other.value) {
- return false;
- }
- return true;
- }
-
- @Override
- public int hashCode() {
- return this.value;
- }
-
- @Override
- public int compareTo(Ip4Address o) {
- Long lv = ((long) this.value) & 0xffffffffL;
- Long rv = ((long) o.value) & 0xffffffffL;
- return lv.compareTo(rv);
+ public static Ip4Address makeMaskedAddress(final Ip4Address address,
+ int prefixLength) {
+ byte[] net = makeMaskedAddressArray(address, prefixLength);
+ return Ip4Address.valueOf(net);
}
}
diff --git a/utils/misc/src/main/java/org/onlab/packet/Ip4Prefix.java b/utils/misc/src/main/java/org/onlab/packet/Ip4Prefix.java
index e3b5246..ca9fc61 100644
--- a/utils/misc/src/main/java/org/onlab/packet/Ip4Prefix.java
+++ b/utils/misc/src/main/java/org/onlab/packet/Ip4Prefix.java
@@ -15,110 +15,90 @@
*/
package org.onlab.packet;
-import java.util.Objects;
-
/**
* The class representing an IPv4 network address.
* This class is immutable.
*/
-public final class Ip4Prefix {
- private final Ip4Address address; // The IPv4 address
- private final short prefixLen; // The prefix length
+public final class Ip4Prefix extends IpPrefix {
+ public static final IpAddress.Version VERSION = IpAddress.Version.INET;
+ // Maximum network mask length
+ public static final int MAX_MASK_LENGTH = IpPrefix.MAX_INET_MASK_LENGTH;
/**
- * Default constructor.
+ * Constructor for given IPv4 address, and a prefix length.
+ *
+ * @param address the IPv4 address
+ * @param prefixLength the prefix length
+ * @throws IllegalArgumentException if the prefix length value is invalid
*/
- public Ip4Prefix() {
- this.address = new Ip4Address();
- this.prefixLen = 0;
+ private Ip4Prefix(Ip4Address address, int prefixLength) {
+ super(address, prefixLength);
}
/**
- * Copy constructor.
+ * Returns the IPv4 address value of the prefix.
*
- * @param other the object to copy from
+ * @return the IPv4 address value of the prefix
*/
- public Ip4Prefix(Ip4Prefix other) {
- this.address = new Ip4Address(other.address);
- this.prefixLen = other.prefixLen;
+ public Ip4Address address() {
+ IpAddress a = super.address();
+ return (Ip4Address) a;
}
/**
- * Constructor for a given address and prefix length.
+ * Converts an integer and a prefix length into an IPv4 prefix.
*
- * @param address the address to use
- * @param prefixLen the prefix length to use
+ * @param address an integer representing the IPv4 address
+ * @param prefixLength the prefix length
+ * @return an IPv4 prefix
+ * @throws IllegalArgumentException if the prefix length value is invalid
*/
- public Ip4Prefix(Ip4Address address, short prefixLen) {
- this.address = Ip4Address.makeMaskedAddress(address, prefixLen);
- this.prefixLen = prefixLen;
+ public static Ip4Prefix valueOf(int address, int prefixLength) {
+ return new Ip4Prefix(Ip4Address.valueOf(address), prefixLength);
}
/**
- * Constructs an IPv4 prefix from a string representation of the
- * prefix.
- *<p>
- * Example: "1.2.0.0/16"
+ * Converts a byte array and a prefix length into an IPv4 prefix.
*
- * @param value the value to use
+ * @param address the IPv4 address value stored in network byte order
+ * @param prefixLength the prefix length
+ * @return an IPv4 prefix
+ * @throws IllegalArgumentException if the prefix length value is invalid
*/
- public Ip4Prefix(String value) {
- String[] splits = value.split("/");
- if (splits.length != 2) {
- throw new IllegalArgumentException("Specified IPv4 prefix must contain an IPv4 " +
- "address and a prefix length separated by '/'");
+ public static Ip4Prefix valueOf(byte[] address, int prefixLength) {
+ return new Ip4Prefix(Ip4Address.valueOf(address), prefixLength);
+ }
+
+ /**
+ * Converts an IPv4 address and a prefix length into an IPv4 prefix.
+ *
+ * @param address the IPv4 address
+ * @param prefixLength the prefix length
+ * @return an IPv4 prefix
+ * @throws IllegalArgumentException if the prefix length value is invalid
+ */
+ public static Ip4Prefix valueOf(Ip4Address address, int prefixLength) {
+ return new Ip4Prefix(address, prefixLength);
+ }
+
+ /**
+ * Converts a CIDR (slash) notation string (e.g., "10.1.0.0/16")
+ * into an IPv4 prefix.
+ *
+ * @param address an IP prefix in string form (e.g., "10.1.0.0/16")
+ * @return an IPv4 prefix
+ * @throws IllegalArgumentException if the arguments are invalid
+ */
+ public static Ip4Prefix valueOf(String address) {
+ final String[] parts = address.split("/");
+ if (parts.length != 2) {
+ String msg = "Malformed IPv4 prefix string: " + address + "." +
+ "Address must take form \"x.x.x.x/y\"";
+ throw new IllegalArgumentException(msg);
}
- this.prefixLen = Short.decode(splits[1]);
- this.address = Ip4Address.makeMaskedAddress(new Ip4Address(splits[0]),
- this.prefixLen);
- }
+ Ip4Address ipAddress = Ip4Address.valueOf(parts[0]);
+ int prefixLength = Integer.parseInt(parts[1]);
- /**
- * Gets the address value of the IPv4 prefix.
- *
- * @return the address value of the IPv4 prefix
- */
- public Ip4Address getAddress() {
- return address;
- }
-
- /**
- * Gets the prefix length value of the IPv4 prefix.
- *
- * @return the prefix length value of the IPv4 prefix
- */
- public short getPrefixLen() {
- return prefixLen;
- }
-
- /**
- * Converts the IPv4 prefix value to an "address/prefixLen" string.
- *
- * @return the IPv4 prefix value as an "address/prefixLen" string
- */
- @Override
- public String toString() {
- return this.address.toString() + "/" + this.prefixLen;
- }
-
- @Override
- public boolean equals(Object other) {
- if (other == this) {
- return true;
- }
-
- if (!(other instanceof Ip4Prefix)) {
- return false;
- }
-
- Ip4Prefix otherIp4Prefix = (Ip4Prefix) other;
-
- return Objects.equals(this.address, otherIp4Prefix.address)
- && this.prefixLen == otherIp4Prefix.prefixLen;
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(address, prefixLen);
+ return new Ip4Prefix(ipAddress, prefixLength);
}
}
diff --git a/utils/misc/src/main/java/org/onlab/packet/Ip6Address.java b/utils/misc/src/main/java/org/onlab/packet/Ip6Address.java
index 8ad9112..d353422 100644
--- a/utils/misc/src/main/java/org/onlab/packet/Ip6Address.java
+++ b/utils/misc/src/main/java/org/onlab/packet/Ip6Address.java
@@ -16,260 +16,137 @@
package org.onlab.packet;
import java.net.InetAddress;
-import java.net.UnknownHostException;
-import java.nio.ByteBuffer;
-import java.util.Objects;
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.util.Arrays;
import com.google.common.net.InetAddresses;
-import com.google.common.primitives.UnsignedLongs;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-import static com.google.common.base.Preconditions.checkState;
/**
- * The class representing an IPv6 address.
+ * A class representing an IPv6 address.
* This class is immutable.
*/
-public final class Ip6Address implements Comparable<Ip6Address> {
- private final long valueHigh; // The higher (more significant) 64 bits
- private final long valueLow; // The lower (less significant) 64 bits
-
- /** The length of the address in bytes (octets). */
- public static final int BYTE_LENGTH = 16;
-
- /** The length of the address in bits. */
- public static final int BIT_LENGTH = BYTE_LENGTH * Byte.SIZE;
+public final class Ip6Address extends IpAddress {
+ public static final IpAddress.Version VERSION = IpAddress.Version.INET6;
+ public static final int BYTE_LENGTH = IpAddress.INET6_BYTE_LENGTH;
+ public static final int BIT_LENGTH = IpAddress.INET6_BIT_LENGTH;
/**
- * Default constructor.
+ * Constructor for given IP address version and address octets.
+ *
+ * @param value the IP address value stored in network byte order
+ * (i.e., the most significant byte first)
+ * @throws IllegalArgumentException if the arguments are invalid
*/
- public Ip6Address() {
- this.valueHigh = 0;
- this.valueLow = 0;
+ private Ip6Address(byte[] value) {
+ super(VERSION, value);
}
/**
- * Copy constructor.
+ * Converts a byte array into an IPv6 address.
*
- * @param other the object to copy from
+ * @param value the IPv6 address value stored in network byte order
+ * (i.e., the most significant byte first)
+ * @return an IPv6 address
+ * @throws IllegalArgumentException if the argument is invalid
*/
- public Ip6Address(Ip6Address other) {
- this.valueHigh = other.valueHigh;
- this.valueLow = other.valueLow;
+ public static Ip6Address valueOf(byte[] value) {
+ return new Ip6Address(value);
}
/**
- * Constructor from integer values.
- *
- * @param valueHigh the higher (more significant) 64 bits of the address
- * @param valueLow the lower (less significant) 64 bits of the address
- */
- public Ip6Address(long valueHigh, long valueLow) {
- this.valueHigh = valueHigh;
- this.valueLow = valueLow;
- }
-
- /**
- * Constructor from a byte array with the IPv6 address stored in network
- * byte order (i.e., the most significant byte first).
- *
- * @param value the value to use
- */
- public Ip6Address(byte[] value) {
- this(value, 0);
- }
-
- /**
- * Constructor from a byte array with the IPv6 address stored in network
- * byte order (i.e., the most significant byte first), and a given offset
- * from the beginning of the byte array.
- *
+ * Converts a byte array and a given offset from the beginning of the
+ * array into an IPv6 address.
+ * <p>
+ * The IP address is stored in network byte order (i.e., the most
+ * significant byte first).
+ * </p>
* @param value the value to use
* @param offset the offset in bytes from the beginning of the byte array
+ * @return an IPv6 address
+ * @throws IllegalArgumentException if the arguments are invalid
*/
- public Ip6Address(byte[] value, int offset) {
- checkNotNull(value);
-
- // Verify the arguments
- if ((offset < 0) || (offset + BYTE_LENGTH > value.length)) {
- String msg;
- if (value.length < BYTE_LENGTH) {
- msg = "Invalid IPv6 address array: array length: " +
- value.length + ". Must be at least " + BYTE_LENGTH;
- } else {
- msg = "Invalid IPv6 address array: array offset: " +
- offset + ". Must be in the interval [0, " +
- (value.length - BYTE_LENGTH) + "]";
- }
- throw new IllegalArgumentException(msg);
- }
-
- // Read the address
- ByteBuffer bb = ByteBuffer.wrap(value);
- bb.position(offset);
- this.valueHigh = bb.getLong();
- this.valueLow = bb.getLong();
+ public static Ip6Address valueOf(byte[] value, int offset) {
+ IpAddress.checkArguments(VERSION, value, offset);
+ byte[] bc = Arrays.copyOfRange(value, offset, value.length);
+ return Ip6Address.valueOf(bc);
}
/**
- * Constructs an IPv6 address from a string representation of the address.
- *<p>
- * Example: "1111:2222::8888"
+ * Converts an InetAddress into an IPv6 address.
*
- * @param value the value to use
+ * @param inetAddress the InetAddress value to use. It must contain an IPv6
+ * address
+ * @return an IPv6 address
+ * @throws IllegalArgumentException if the argument is invalid
*/
- public Ip6Address(String value) {
- checkNotNull(value);
-
- if (value.isEmpty()) {
- final String msg = "Specified IPv6 cannot be an empty string";
+ public static Ip6Address valueOf(InetAddress inetAddress) {
+ byte[] bytes = inetAddress.getAddress();
+ if (inetAddress instanceof Inet6Address) {
+ return new Ip6Address(bytes);
+ }
+ if ((inetAddress instanceof Inet4Address) ||
+ (bytes.length == INET_BYTE_LENGTH)) {
+ final String msg = "Invalid IPv6 version address string: " +
+ inetAddress.toString();
throw new IllegalArgumentException(msg);
}
- InetAddress addr = null;
+ // Use the number of bytes as a hint
+ if (bytes.length == INET6_BYTE_LENGTH) {
+ return new Ip6Address(bytes);
+ }
+ final String msg = "Unrecognized IP version address string: " +
+ inetAddress.toString();
+ throw new IllegalArgumentException(msg);
+ }
+
+ /**
+ * Converts an IPv6 string literal (e.g., "1111:2222::8888") into an IP
+ * address.
+ *
+ * @param value an IPv6 address value in string form
+ * @return an IPv6 address
+ * @throws IllegalArgumentException if the argument is invalid
+ */
+ public static Ip6Address valueOf(String value) {
+ InetAddress inetAddress = null;
try {
- addr = InetAddresses.forString(value);
+ inetAddress = InetAddresses.forString(value);
} catch (IllegalArgumentException e) {
- final String msg = "Invalid IPv6 address string: " + value;
+ final String msg = "Invalid IP address string: " + value;
throw new IllegalArgumentException(msg);
}
- byte[] bytes = addr.getAddress();
- ByteBuffer bb = ByteBuffer.wrap(bytes);
- this.valueHigh = bb.getLong();
- this.valueLow = bb.getLong();
- }
-
- /**
- * Gets the IPv6 address as a byte array.
- *
- * @return a byte array with the IPv6 address stored in network byte order
- * (i.e., the most significant byte first).
- */
- public byte[] toOctets() {
- return ByteBuffer.allocate(BYTE_LENGTH)
- .putLong(valueHigh).putLong(valueLow).array();
+ return valueOf(inetAddress);
}
/**
* Creates an IPv6 network mask prefix.
*
- * @param prefixLen the length of the mask prefix. Must be in the interval
- * [0, 128].
+ * @param prefixLength the length of the mask prefix. Must be in the
+ * interval [0, 128]
* @return a new IPv6 address that contains a mask prefix of the
* specified length
+ * @throws IllegalArgumentException if the arguments are invalid
*/
- public static Ip6Address makeMaskPrefix(int prefixLen) {
- long vh, vl;
-
- // Verify the prefix length
- if ((prefixLen < 0) || (prefixLen > Ip6Address.BIT_LENGTH)) {
- final String msg = "Invalid IPv6 prefix length: " + prefixLen +
- ". Must be in the interval [0, 128].";
- throw new IllegalArgumentException(msg);
- }
-
- if (prefixLen == 0) {
- //
- // NOTE: Apparently, the result of "<< 64" shifting to the left
- // results in all 1s instead of all 0s, hence we handle it as
- // a special case.
- //
- vh = 0;
- vl = 0;
- } else if (prefixLen <= 64) {
- vh = (0xffffffffffffffffL << (64 - prefixLen)) & 0xffffffffffffffffL;
- vl = 0;
- } else {
- vh = -1L; // All 1s
- vl = (0xffffffffffffffffL << (128 - prefixLen)) & 0xffffffffffffffffL;
- }
- return new Ip6Address(vh, vl);
+ public static Ip6Address makeMaskPrefix(int prefixLength) {
+ byte[] mask = IpAddress.makeMaskPrefixArray(VERSION, prefixLength);
+ return new Ip6Address(mask);
}
/**
* Creates an IPv6 address by masking it with a network mask of given
* mask length.
*
- * @param addr the address to mask
- * @param prefixLen the length of the mask prefix. Must be in the interval
- * [0, 128].
+ * @param address the address to mask
+ * @param prefixLength the length of the mask prefix. Must be in the
+ * interval [0, 128]
* @return a new IPv6 address that is masked with a mask prefix of the
* specified length
+ * @throws IllegalArgumentException if the prefix length is invalid
*/
- public static Ip6Address makeMaskedAddress(final Ip6Address addr,
- int prefixLen) {
- Ip6Address mask = Ip6Address.makeMaskPrefix(prefixLen);
- long vh = addr.valueHigh & mask.valueHigh;
- long vl = addr.valueLow & mask.valueLow;
-
- return new Ip6Address(vh, vl);
- }
-
- /**
- * Gets the value of the higher (more significant) 64 bits of the address.
- *
- * @return the value of the higher (more significant) 64 bits of the
- * address
- */
- public long getValueHigh() {
- return valueHigh;
- }
-
- /**
- * Gets the value of the lower (less significant) 64 bits of the address.
- *
- * @return the value of the lower (less significant) 64 bits of the
- * address
- */
- public long getValueLow() {
- return valueLow;
- }
-
- /**
- * Converts the IPv6 value to a ':' separated string.
- *
- * @return the IPv6 value as a ':' separated string
- */
- @Override
- public String toString() {
- ByteBuffer bb = ByteBuffer.allocate(Ip6Address.BYTE_LENGTH);
- bb.putLong(valueHigh);
- bb.putLong(valueLow);
- InetAddress inetAddr = null;
- try {
- inetAddr = InetAddress.getByAddress(bb.array());
- } catch (UnknownHostException e) {
- // Should never happen
- checkState(false, "Internal error: Ip6Address.toString()");
- return "::";
- }
- return InetAddresses.toAddrString(inetAddr);
- }
-
- @Override
- public boolean equals(Object o) {
- if (!(o instanceof Ip6Address)) {
- return false;
- }
- Ip6Address other = (Ip6Address) o;
- return this.valueHigh == other.valueHigh
- && this.valueLow == other.valueLow;
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(valueHigh, valueLow);
- }
-
- @Override
- public int compareTo(Ip6Address o) {
- // Compare the high-order 64-bit value
- if (this.valueHigh != o.valueHigh) {
- return UnsignedLongs.compare(this.valueHigh, o.valueHigh);
- }
- // Compare the low-order 64-bit value
- if (this.valueLow != o.valueLow) {
- return UnsignedLongs.compare(this.valueLow, o.valueLow);
- }
- return 0;
+ public static Ip6Address makeMaskedAddress(final Ip6Address address,
+ int prefixLength) {
+ byte[] net = makeMaskedAddressArray(address, prefixLength);
+ return Ip6Address.valueOf(net);
}
}
diff --git a/utils/misc/src/main/java/org/onlab/packet/Ip6Prefix.java b/utils/misc/src/main/java/org/onlab/packet/Ip6Prefix.java
index 5422ae1..ca9985e 100644
--- a/utils/misc/src/main/java/org/onlab/packet/Ip6Prefix.java
+++ b/utils/misc/src/main/java/org/onlab/packet/Ip6Prefix.java
@@ -15,110 +15,79 @@
*/
package org.onlab.packet;
-import java.util.Objects;
-
/**
* The class representing an IPv6 network address.
* This class is immutable.
*/
-public final class Ip6Prefix {
- private final Ip6Address address; // The IPv6 address
- private final short prefixLen; // The prefix length
+public final class Ip6Prefix extends IpPrefix {
+ public static final IpAddress.Version VERSION = IpAddress.Version.INET6;
+ // Maximum network mask length
+ public static final int MAX_MASK_LENGTH = IpPrefix.MAX_INET6_MASK_LENGTH;
/**
- * Default constructor.
+ * Constructor for given IPv6 address, and a prefix length.
+ *
+ * @param address the IPv6 address
+ * @param prefixLength the prefix length
+ * @throws IllegalArgumentException if the prefix length value is invalid
*/
- public Ip6Prefix() {
- this.address = new Ip6Address();
- this.prefixLen = 0;
+ private Ip6Prefix(Ip6Address address, int prefixLength) {
+ super(address, prefixLength);
}
/**
- * Copy constructor.
+ * Returns the IPv6 address value of the prefix.
*
- * @param other the object to copy from
+ * @return the IPv6 address value of the prefix
*/
- public Ip6Prefix(Ip6Prefix other) {
- this.address = new Ip6Address(other.address);
- this.prefixLen = other.prefixLen;
+ public Ip6Address address() {
+ IpAddress a = super.address();
+ return (Ip6Address) a;
}
/**
- * Constructor for a given address and prefix length.
+ * Converts a byte array and a prefix length into an IPv6 prefix.
*
- * @param address the address to use
- * @param prefixLen the prefix length to use
+ * @param address the IPv6 address value stored in network byte order
+ * @param prefixLength the prefix length
+ * @return an IPv6 prefix
+ * @throws IllegalArgumentException if the prefix length value is invalid
*/
- public Ip6Prefix(Ip6Address address, short prefixLen) {
- this.address = Ip6Address.makeMaskedAddress(address, prefixLen);
- this.prefixLen = prefixLen;
+ public static Ip6Prefix valueOf(byte[] address, int prefixLength) {
+ return new Ip6Prefix(Ip6Address.valueOf(address), prefixLength);
}
/**
- * Constructs an IPv6 prefix from a string representation of the
- * prefix.
- *<p>
- * Example: "1111:2222::/32"
+ * Converts an IPv6 address and a prefix length into an IPv6 prefix.
*
- * @param value the value to use
+ * @param address the IPv6 address
+ * @param prefixLength the prefix length
+ * @return an IPv6 prefix
+ * @throws IllegalArgumentException if the prefix length value is invalid
*/
- public Ip6Prefix(String value) {
- String[] splits = value.split("/");
- if (splits.length != 2) {
- throw new IllegalArgumentException("Specified IPv6 prefix must contain an IPv6 " +
- "address and a prefix length separated by '/'");
+ public static Ip6Prefix valueOf(Ip6Address address, int prefixLength) {
+ return new Ip6Prefix(address, prefixLength);
+ }
+
+ /**
+ * Converts a CIDR (slash) notation string (e.g., "1111:2222::/64")
+ * into an IPv6 prefix.
+ *
+ * @param address an IP prefix in string form (e.g.,"1111:2222::/64")
+ * @return an IPv6 prefix
+ * @throws IllegalArgumentException if the arguments are invalid
+ */
+ public static Ip6Prefix valueOf(String address) {
+ final String[] parts = address.split("/");
+ if (parts.length != 2) {
+ String msg = "Malformed IPv6 prefix string: " + address + "." +
+ "Address must take form " +
+ "\"xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx/y\"";
+ throw new IllegalArgumentException(msg);
}
- this.prefixLen = Short.decode(splits[1]);
- this.address = Ip6Address.makeMaskedAddress(new Ip6Address(splits[0]),
- this.prefixLen);
- }
+ Ip6Address ipAddress = Ip6Address.valueOf(parts[0]);
+ int prefixLength = Integer.parseInt(parts[1]);
- /**
- * Gets the address value of the IPv6 prefix.
- *
- * @return the address value of the IPv6 prefix
- */
- public Ip6Address getAddress() {
- return address;
- }
-
- /**
- * Gets the prefix length value of the IPv6 prefix.
- *
- * @return the prefix length value of the IPv6 prefix
- */
- public short getPrefixLen() {
- return prefixLen;
- }
-
- /**
- * Converts the IPv6 prefix value to an "address/prefixLen" string.
- *
- * @return the IPv6 prefix value as an "address/prefixLen" string
- */
- @Override
- public String toString() {
- return this.address.toString() + "/" + this.prefixLen;
- }
-
- @Override
- public boolean equals(Object other) {
- if (other == this) {
- return true;
- }
-
- if (!(other instanceof Ip6Prefix)) {
- return false;
- }
-
- Ip6Prefix otherIp6Prefix = (Ip6Prefix) other;
-
- return Objects.equals(this.address, otherIp6Prefix.address)
- && this.prefixLen == otherIp6Prefix.prefixLen;
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(address, prefixLen);
+ return new Ip6Prefix(ipAddress, prefixLength);
}
}
diff --git a/utils/misc/src/main/java/org/onlab/packet/IpAddress.java b/utils/misc/src/main/java/org/onlab/packet/IpAddress.java
index 77ba9b0..2906798 100644
--- a/utils/misc/src/main/java/org/onlab/packet/IpAddress.java
+++ b/utils/misc/src/main/java/org/onlab/packet/IpAddress.java
@@ -30,9 +30,9 @@
/**
* A class representing an IP address.
- * TODO: Add support for IPv6 as well.
+ * This class is immutable.
*/
-public final class IpAddress implements Comparable<IpAddress> {
+public class IpAddress implements Comparable<IpAddress> {
// IP Versions
public enum Version { INET, INET6 };
@@ -53,7 +53,7 @@
* (i.e., the most significant byte first)
* @throws IllegalArgumentException if the arguments are invalid
*/
- private IpAddress(Version version, byte[] value) {
+ protected IpAddress(Version version, byte[] value) {
checkArguments(version, value, 0);
this.version = version;
switch (version) {
@@ -80,6 +80,42 @@
}
/**
+ * Gets the {@link Ip4Address} view of the IP address.
+ *
+ * @return the {@link Ip4Address} view of the IP address if it is IPv4,
+ * otherwise null
+ */
+ public Ip4Address getIp4Address() {
+ if (version() != Ip4Address.VERSION) {
+ return null;
+ }
+
+ // Return this object itself if it is already instance of Ip4Address
+ if (this instanceof Ip4Address) {
+ return (Ip4Address) this;
+ }
+ return Ip4Address.valueOf(octets);
+ }
+
+ /**
+ * Gets the {@link Ip6Address} view of the IP address.
+ *
+ * @return the {@link Ip6Address} view of the IP address if it is IPv6,
+ * otherwise null
+ */
+ public Ip6Address getIp6Address() {
+ if (version() != Ip6Address.VERSION) {
+ return null;
+ }
+
+ // Return this object itself if it is already instance of Ip6Address
+ if (this instanceof Ip6Address) {
+ return (Ip6Address) this;
+ }
+ return Ip6Address.valueOf(octets);
+ }
+
+ /**
* Returns the IP address as a byte array.
*
* @return a byte array
@@ -89,7 +125,7 @@
}
/**
- * Returns the integral value of this IP address.
+ * Returns the integer value of this IP address.
* TODO: This method should be moved to Ip4Address.
*
* @return the IP address's value as an integer
@@ -220,31 +256,7 @@
* @throws IllegalArgumentException if the arguments are invalid
*/
public static IpAddress makeMaskPrefix(Version version, int prefixLength) {
- int addrByteLength = byteLength(version);
- int addrBitLength = addrByteLength * Byte.SIZE;
-
- // Verify the prefix length
- if ((prefixLength < 0) || (prefixLength > addrBitLength)) {
- final String msg = "Invalid IP prefix length: " + prefixLength +
- ". Must be in the interval [0, " + addrBitLength + "].";
- throw new IllegalArgumentException(msg);
- }
-
- // Number of bytes and extra bits that should be all 1s
- int maskBytes = prefixLength / Byte.SIZE;
- int maskBits = prefixLength % Byte.SIZE;
- byte[] mask = new byte[addrByteLength];
-
- // Set the bytes and extra bits to 1s
- for (int i = 0; i < maskBytes; i++) {
- mask[i] = (byte) 0xff; // Set mask bytes to 1s
- }
- for (int i = maskBytes; i < addrByteLength; i++) {
- mask[i] = 0; // Set remaining bytes to 0s
- }
- if (maskBits > 0) {
- mask[maskBytes] = (byte) (0xff << (Byte.SIZE - maskBits));
- }
+ byte[] mask = makeMaskPrefixArray(version, prefixLength);
return new IpAddress(version, mask);
}
@@ -252,24 +264,26 @@
* Creates an IP address by masking it with a network mask of given
* mask length.
*
- * @param addr the address to mask
+ * @param address the address to mask
* @param prefixLength the length of the mask prefix. Must be in the
* interval [0, 32] for IPv4, or [0, 128] for IPv6
* @return a new IP address that is masked with a mask prefix of the
* specified length
* @throws IllegalArgumentException if the prefix length is invalid
*/
- public static IpAddress makeMaskedAddress(final IpAddress addr,
+ public static IpAddress makeMaskedAddress(final IpAddress address,
int prefixLength) {
- IpAddress mask = IpAddress.makeMaskPrefix(addr.version(),
- prefixLength);
- byte[] net = new byte[mask.octets.length];
-
- // Mask each byte
- for (int i = 0; i < net.length; i++) {
- net[i] = (byte) (addr.octets[i] & mask.octets[i]);
+ // TODO: The code below should go away and replaced with generics
+ if (address instanceof Ip4Address) {
+ Ip4Address ip4a = (Ip4Address) address;
+ return Ip4Address.makeMaskedAddress(ip4a, prefixLength);
+ } else if (address instanceof Ip6Address) {
+ Ip6Address ip6a = (Ip6Address) address;
+ return Ip6Address.makeMaskedAddress(ip6a, prefixLength);
+ } else {
+ byte[] net = makeMaskedAddressArray(address, prefixLength);
+ return IpAddress.valueOf(address.version(), net);
}
- return IpAddress.valueOf(addr.version(), net);
}
@Override
@@ -353,8 +367,7 @@
* array with the address
* @throws IllegalArgumentException if any of the arguments is invalid
*/
- private static void checkArguments(Version version, byte[] value,
- int offset) {
+ static void checkArguments(Version version, byte[] value, int offset) {
// Check the offset and byte array length
int addrByteLength = byteLength(version);
if ((offset < 0) || (offset + addrByteLength > value.length)) {
@@ -372,4 +385,67 @@
throw new IllegalArgumentException(msg);
}
}
+
+ /**
+ * Creates a byte array for IP network mask prefix.
+ *
+ * @param version the IP address version
+ * @param prefixLength the length of the mask prefix. Must be in the
+ * interval [0, 32] for IPv4, or [0, 128] for IPv6
+ * @return a byte array that contains a mask prefix of the
+ * specified length
+ * @throws IllegalArgumentException if the arguments are invalid
+ */
+ static byte[] makeMaskPrefixArray(Version version, int prefixLength) {
+ int addrByteLength = byteLength(version);
+ int addrBitLength = addrByteLength * Byte.SIZE;
+
+ // Verify the prefix length
+ if ((prefixLength < 0) || (prefixLength > addrBitLength)) {
+ final String msg = "Invalid IP prefix length: " + prefixLength +
+ ". Must be in the interval [0, " + addrBitLength + "].";
+ throw new IllegalArgumentException(msg);
+ }
+
+ // Number of bytes and extra bits that should be all 1s
+ int maskBytes = prefixLength / Byte.SIZE;
+ int maskBits = prefixLength % Byte.SIZE;
+ byte[] mask = new byte[addrByteLength];
+
+ // Set the bytes and extra bits to 1s
+ for (int i = 0; i < maskBytes; i++) {
+ mask[i] = (byte) 0xff; // Set mask bytes to 1s
+ }
+ for (int i = maskBytes; i < addrByteLength; i++) {
+ mask[i] = 0; // Set remaining bytes to 0s
+ }
+ if (maskBits > 0) {
+ mask[maskBytes] = (byte) (0xff << (Byte.SIZE - maskBits));
+ }
+ return mask;
+ }
+
+ /**
+ * Creates a byte array that represents an IP address masked with
+ * a network mask of given mask length.
+ *
+ * @param addr the address to mask
+ * @param prefixLength the length of the mask prefix. Must be in the
+ * interval [0, 32] for IPv4, or [0, 128] for IPv6
+ * @return a byte array that represents the IP address masked with
+ * a mask prefix of the specified length
+ * @throws IllegalArgumentException if the prefix length is invalid
+ */
+ static byte[] makeMaskedAddressArray(final IpAddress addr,
+ int prefixLength) {
+ byte[] mask = IpAddress.makeMaskPrefixArray(addr.version(),
+ prefixLength);
+ byte[] net = new byte[mask.length];
+
+ // Mask each byte
+ for (int i = 0; i < net.length; i++) {
+ net[i] = (byte) (addr.octets[i] & mask[i]);
+ }
+ return net;
+ }
}
diff --git a/utils/misc/src/main/java/org/onlab/packet/IpPrefix.java b/utils/misc/src/main/java/org/onlab/packet/IpPrefix.java
index 3cde79d..c2fc175 100644
--- a/utils/misc/src/main/java/org/onlab/packet/IpPrefix.java
+++ b/utils/misc/src/main/java/org/onlab/packet/IpPrefix.java
@@ -17,17 +17,16 @@
import java.util.Objects;
-// TODO: Add support for IPv6 as well.
-
/**
* A class representing an IP prefix. A prefix consists of an IP address and
* a subnet mask.
+ * This class is immutable.
* <p>
* NOTE: The stored IP address in the result IP prefix is masked to
* contain zeroes in all bits after the prefix length.
* </p>
*/
-public final class IpPrefix {
+public class IpPrefix {
// Maximum network mask length
public static final int MAX_INET_MASK_LENGTH = IpAddress.INET_BIT_LENGTH;
public static final int MAX_INET6_MASK_LENGTH = IpAddress.INET6_BIT_LENGTH;
@@ -40,82 +39,12 @@
*
* @param address the IP address
* @param prefixLength the prefix length
- */
- private IpPrefix(IpAddress address, int prefixLength) {
- checkPrefixLength(prefixLength);
- this.address = IpAddress.makeMaskedAddress(address, prefixLength);
- this.prefixLength = (short) prefixLength;
- }
-
- /**
- * Checks whether the prefix length is valid.
- *
- * @param prefixLength the prefix length value to check
* @throws IllegalArgumentException if the prefix length value is invalid
*/
- private static void checkPrefixLength(int prefixLength) {
- if ((prefixLength < 0) || (prefixLength > MAX_INET_MASK_LENGTH)) {
- String msg = "Invalid prefix length " + prefixLength + ". " +
- "The value must be in the interval [0, " +
- MAX_INET_MASK_LENGTH + "]";
- throw new IllegalArgumentException(msg);
- }
- }
-
- /**
- * Converts an integer and a prefix length into an IPv4 prefix.
- *
- * @param address an integer representing the IPv4 address
- * @param prefixLength the prefix length
- * @return an IP prefix
- */
- public static IpPrefix valueOf(int address, int prefixLength) {
- return new IpPrefix(IpAddress.valueOf(address), prefixLength);
- }
-
- /**
- * Converts a byte array and a prefix length into an IP prefix.
- *
- * @param version the IP address version
- * @param address the IP address value stored in network byte order
- * @param prefixLength the prefix length
- * @return an IP prefix
- */
- public static IpPrefix valueOf(IpAddress.Version version, byte[] address,
- int prefixLength) {
- return new IpPrefix(IpAddress.valueOf(version, address),
- prefixLength);
- }
-
- /**
- * Converts an IP address and a prefix length into IP prefix.
- *
- * @param address the IP address
- * @param prefixLength the prefix length
- * @return an IP prefix
- */
- public static IpPrefix valueOf(IpAddress address, int prefixLength) {
- return new IpPrefix(address, prefixLength);
- }
-
- /**
- * Converts a CIDR (slash) notation string (e.g., "10.1.0.0/16") into an
- * IP prefix.
- *
- * @param address an IP prefix in string form, e.g. "10.1.0.0/16"
- * @return an IP prefix
- */
- public static IpPrefix valueOf(String address) {
- final String[] parts = address.split("/");
- if (parts.length != 2) {
- String msg = "Malformed IP prefix string: " + address + "." +
- "Address must take form \"x.x.x.x/y\"";
- throw new IllegalArgumentException(msg);
- }
- IpAddress ipAddress = IpAddress.valueOf(parts[0]);
- int prefixLength = Integer.parseInt(parts[1]);
-
- return new IpPrefix(ipAddress, prefixLength);
+ protected IpPrefix(IpAddress address, int prefixLength) {
+ checkPrefixLength(address.version(), prefixLength);
+ this.address = IpAddress.makeMaskedAddress(address, prefixLength);
+ this.prefixLength = (short) prefixLength;
}
/**
@@ -146,6 +75,103 @@
}
/**
+ * Gets the {@link Ip4Prefix} view of the IP prefix.
+ *
+ * @return the {@link Ip4Prefix} view of the IP prefix if it is IPv4,
+ * otherwise null
+ */
+ public Ip4Prefix getIp4Prefix() {
+ if (version() != Ip4Prefix.VERSION) {
+ return null;
+ }
+
+ // Return this object itself if it is already instance of Ip4Prefix
+ if (this instanceof Ip4Prefix) {
+ return (Ip4Prefix) this;
+ }
+ return Ip4Prefix.valueOf(address.getIp4Address(), prefixLength);
+ }
+
+ /**
+ * Gets the {@link Ip6Prefix} view of the IP prefix.
+ *
+ * @return the {@link Ip6Prefix} view of the IP prefix if it is IPv6,
+ * otherwise null
+ */
+ public Ip6Prefix getIp6Prefix() {
+ if (version() != Ip6Prefix.VERSION) {
+ return null;
+ }
+
+ // Return this object itself if it is already instance of Ip6Prefix
+ if (this instanceof Ip6Prefix) {
+ return (Ip6Prefix) this;
+ }
+ return Ip6Prefix.valueOf(address.getIp6Address(), prefixLength);
+ }
+
+ /**
+ * Converts an integer and a prefix length into an IPv4 prefix.
+ *
+ * @param address an integer representing the IPv4 address
+ * @param prefixLength the prefix length
+ * @return an IP prefix
+ * @throws IllegalArgumentException if the prefix length value is invalid
+ */
+ public static IpPrefix valueOf(int address, int prefixLength) {
+ return new IpPrefix(IpAddress.valueOf(address), prefixLength);
+ }
+
+ /**
+ * Converts a byte array and a prefix length into an IP prefix.
+ *
+ * @param version the IP address version
+ * @param address the IP address value stored in network byte order
+ * @param prefixLength the prefix length
+ * @return an IP prefix
+ * @throws IllegalArgumentException if the prefix length value is invalid
+ */
+ public static IpPrefix valueOf(IpAddress.Version version, byte[] address,
+ int prefixLength) {
+ return new IpPrefix(IpAddress.valueOf(version, address), prefixLength);
+ }
+
+ /**
+ * Converts an IP address and a prefix length into an IP prefix.
+ *
+ * @param address the IP address
+ * @param prefixLength the prefix length
+ * @return an IP prefix
+ * @throws IllegalArgumentException if the prefix length value is invalid
+ */
+ public static IpPrefix valueOf(IpAddress address, int prefixLength) {
+ return new IpPrefix(address, prefixLength);
+ }
+
+ /**
+ * Converts a CIDR (slash) notation string (e.g., "10.1.0.0/16" or
+ * "1111:2222::/64") into an IP prefix.
+ *
+ * @param address an IP prefix in string form (e.g. "10.1.0.0/16" or
+ * "1111:2222::/64")
+ * @return an IP prefix
+ * @throws IllegalArgumentException if the arguments are invalid
+ */
+ public static IpPrefix valueOf(String address) {
+ final String[] parts = address.split("/");
+ if (parts.length != 2) {
+ String msg = "Malformed IP prefix string: " + address + "." +
+ "Address must take form \"x.x.x.x/y\" or " +
+ "\"xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx/y\"";
+ throw new IllegalArgumentException(msg);
+ }
+ IpAddress ipAddress = IpAddress.valueOf(parts[0]);
+ int prefixLength = Integer.parseInt(parts[1]);
+
+ return new IpPrefix(ipAddress, prefixLength);
+ }
+
+ /**
* Determines whether a given IP prefix is contained within this prefix.
*
* @param other the IP prefix to test
@@ -217,4 +243,35 @@
builder.append(String.format("%d", prefixLength));
return builder.toString();
}
+
+ /**
+ * Checks whether the prefix length is valid.
+ *
+ * @param version the IP address version
+ * @param prefixLength the prefix length value to check
+ * @throws IllegalArgumentException if the prefix length value is invalid
+ */
+ private static void checkPrefixLength(IpAddress.Version version,
+ int prefixLength) {
+ int maxPrefixLen = 0;
+
+ switch (version) {
+ case INET:
+ maxPrefixLen = MAX_INET_MASK_LENGTH;
+ break;
+ case INET6:
+ maxPrefixLen = MAX_INET6_MASK_LENGTH;
+ break;
+ default:
+ String msg = "Invalid IP version " + version;
+ throw new IllegalArgumentException(msg);
+ }
+
+ if ((prefixLength < 0) || (prefixLength > maxPrefixLen)) {
+ String msg = "Invalid prefix length " + prefixLength + ". " +
+ "The value must be in the interval [0, " +
+ maxPrefixLen + "]";
+ throw new IllegalArgumentException(msg);
+ }
+ }
}
diff --git a/utils/misc/src/main/java/org/onlab/packet/LLDP.java b/utils/misc/src/main/java/org/onlab/packet/LLDP.java
index 3928068..339bc3a 100644
--- a/utils/misc/src/main/java/org/onlab/packet/LLDP.java
+++ b/utils/misc/src/main/java/org/onlab/packet/LLDP.java
@@ -48,6 +48,7 @@
/**
* @param chassis
* the chassisId to set
+ * @return this
*/
public LLDP setChassisId(final LLDPTLV chassis) {
this.chassisId = chassis;
@@ -64,6 +65,7 @@
/**
* @param portId
* the portId to set
+ * @return this
*/
public LLDP setPortId(final LLDPTLV portId) {
this.portId = portId;
@@ -80,6 +82,7 @@
/**
* @param ttl
* the ttl to set
+ * @return this
*/
public LLDP setTtl(final LLDPTLV ttl) {
this.ttl = ttl;
@@ -96,6 +99,7 @@
/**
* @param optionalTLVList
* the optionalTLVList to set
+ * @return this
*/
public LLDP setOptionalTLVList(final List<LLDPTLV> optionalTLVList) {
this.optionalTLVList = optionalTLVList;
diff --git a/utils/misc/src/main/java/org/onlab/packet/LLDPTLV.java b/utils/misc/src/main/java/org/onlab/packet/LLDPTLV.java
index 21bd55f..b5fe833 100644
--- a/utils/misc/src/main/java/org/onlab/packet/LLDPTLV.java
+++ b/utils/misc/src/main/java/org/onlab/packet/LLDPTLV.java
@@ -40,6 +40,7 @@
/**
* @param type
* the type to set
+ * @return this
*/
public LLDPTLV setType(final byte type) {
this.type = type;
@@ -56,6 +57,7 @@
/**
* @param length
* the length to set
+ * @return this
*/
public LLDPTLV setLength(final short length) {
this.length = length;
@@ -72,6 +74,7 @@
/**
* @param value
* the value to set
+ * @return this
*/
public LLDPTLV setValue(final byte[] value) {
this.value = value;
diff --git a/utils/misc/src/main/java/org/onlab/packet/TCP.java b/utils/misc/src/main/java/org/onlab/packet/TCP.java
index 9185cd5..5e0cfb2 100644
--- a/utils/misc/src/main/java/org/onlab/packet/TCP.java
+++ b/utils/misc/src/main/java/org/onlab/packet/TCP.java
@@ -46,6 +46,7 @@
/**
* @param sourcePort
* the sourcePort to set
+ * @return this
*/
public TCP setSourcePort(final short sourcePort) {
this.sourcePort = sourcePort;
@@ -62,6 +63,7 @@
/**
* @param destinationPort
* the destinationPort to set
+ * @return this
*/
public TCP setDestinationPort(final short destinationPort) {
this.destinationPort = destinationPort;
@@ -157,6 +159,7 @@
/**
* @param checksum
* the checksum to set
+ * @return this
*/
public TCP setChecksum(final short checksum) {
this.checksum = checksum;
diff --git a/utils/misc/src/main/java/org/onlab/packet/UDP.java b/utils/misc/src/main/java/org/onlab/packet/UDP.java
index 5d9c645..73a196a 100644
--- a/utils/misc/src/main/java/org/onlab/packet/UDP.java
+++ b/utils/misc/src/main/java/org/onlab/packet/UDP.java
@@ -57,6 +57,7 @@
/**
* @param sourcePort
* the sourcePort to set
+ * @return this
*/
public UDP setSourcePort(final short sourcePort) {
this.sourcePort = sourcePort;
@@ -73,6 +74,7 @@
/**
* @param destinationPort
* the destinationPort to set
+ * @return this
*/
public UDP setDestinationPort(final short destinationPort) {
this.destinationPort = destinationPort;
@@ -96,6 +98,7 @@
/**
* @param checksum
* the checksum to set
+ * @return this
*/
public UDP setChecksum(final short checksum) {
this.checksum = checksum;
diff --git a/utils/misc/src/main/java/org/onlab/util/HexString.java b/utils/misc/src/main/java/org/onlab/util/HexString.java
index 4e46a25..3e9ae03 100644
--- a/utils/misc/src/main/java/org/onlab/util/HexString.java
+++ b/utils/misc/src/main/java/org/onlab/util/HexString.java
@@ -24,7 +24,7 @@
/**
* Convert a string of bytes to a ':' separated hex string.
*
- * @param bytes
+ * @param bytes string of bytes to convert
* @return "0f:ca:fe:de:ad:be:ef"
*/
public static String toHexString(final byte[] bytes) {
diff --git a/utils/misc/src/test/java/org/onlab/graph/KshortestPathSearchTest.java b/utils/misc/src/test/java/org/onlab/graph/KshortestPathSearchTest.java
new file mode 100644
index 0000000..3ef2fcf
--- /dev/null
+++ b/utils/misc/src/test/java/org/onlab/graph/KshortestPathSearchTest.java
@@ -0,0 +1,200 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.onlab.graph;
+
+import static com.google.common.collect.ImmutableSet.of;
+import static org.junit.Assert.*;
+
+import java.io.ByteArrayOutputStream;
+//import java.io.PrintStream;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+public class KshortestPathSearchTest extends BreadthFirstSearchTest {
+
+ private final ByteArrayOutputStream outContent = new ByteArrayOutputStream();
+
+ @Test
+ public void noPath() {
+ graph = new AdjacencyListsGraph<>(of(A, B, C, D),
+ of(new TestEdge(A, B, 1),
+ new TestEdge(B, A, 1),
+ new TestEdge(C, D, 1),
+ new TestEdge(D, C, 1)));
+ KshortestPathSearch<TestVertex, TestEdge> gs = new KshortestPathSearch<TestVertex, TestEdge>(graph);
+ List<List<TestEdge>> result = gs.search(A, D, weight, 1);
+ List<Path> paths = new ArrayList<>();
+ Iterator<List<TestEdge>> itr = result.iterator();
+ while (itr.hasNext()) {
+ System.out.println(itr.next().toString());
+ }
+ assertEquals("incorrect paths count", 0, result.size());
+ }
+
+ @Test
+ public void test2Path() {
+ graph = new AdjacencyListsGraph<>(of(A, B, C, D),
+ of(new TestEdge(A, B, 1),
+ new TestEdge(B, A, 1),
+ new TestEdge(B, D, 1),
+ new TestEdge(D, B, 1),
+ new TestEdge(A, C, 1),
+ new TestEdge(C, A, 1),
+ new TestEdge(C, D, 1),
+ new TestEdge(D, C, 1)));
+ KshortestPathSearch<TestVertex, TestEdge> gs = new KshortestPathSearch<TestVertex, TestEdge>(graph);
+ List<List<TestEdge>> result = gs.search(A, D, weight, 2);
+ List<Path> paths = new ArrayList<>();
+ Iterator<List<TestEdge>> itr = result.iterator();
+ while (itr.hasNext()) {
+ System.out.println(itr.next().toString());
+ }
+ assertEquals("incorrect paths count", 2, result.size());
+ // assertEquals("printing the paths", outContent.toString());
+ }
+
+ @Test
+ public void test3Path() {
+ graph = new AdjacencyListsGraph<>(of(A, B, C, D),
+ of(new TestEdge(A, B, 1),
+ new TestEdge(B, A, 1),
+ new TestEdge(A, D, 1),
+ new TestEdge(D, A, 1),
+ new TestEdge(B, D, 1),
+ new TestEdge(D, B, 1),
+ new TestEdge(A, C, 1),
+ new TestEdge(C, A, 1),
+ new TestEdge(C, D, 1),
+ new TestEdge(D, C, 1)));
+ KshortestPathSearch<TestVertex, TestEdge> gs = new KshortestPathSearch<TestVertex, TestEdge>(graph);
+ List<List<TestEdge>> result = gs.search(A, D, weight, 3);
+ List<Path> paths = new ArrayList<>();
+ Iterator<List<TestEdge>> itr = result.iterator();
+ while (itr.hasNext()) {
+ System.out.println(itr.next().toString());
+ }
+ assertEquals("incorrect paths count", 3, result.size());
+ // assertEquals("printing the paths", outContent.toString());
+ }
+
+ @Test
+ public void test4Path() {
+ graph = new AdjacencyListsGraph<>(of(A, B, C, D, E, F),
+ of(new TestEdge(A, B, 1),
+ new TestEdge(B, A, 1),
+ new TestEdge(A, C, 1),
+ new TestEdge(C, A, 1),
+ new TestEdge(B, D, 1),
+ new TestEdge(D, B, 1),
+ new TestEdge(C, E, 1),
+ new TestEdge(E, C, 1),
+ new TestEdge(D, F, 1),
+ new TestEdge(F, D, 1),
+ new TestEdge(F, E, 1),
+ new TestEdge(E, F, 1),
+ new TestEdge(C, D, 1),
+ new TestEdge(D, C, 1)));
+ KshortestPathSearch<TestVertex, TestEdge> gs = new KshortestPathSearch<TestVertex, TestEdge>(graph);
+ List<List<TestEdge>> result = gs.search(A, F, weight, 4);
+ List<Path> paths = new ArrayList<>();
+ Iterator<List<TestEdge>> itr = result.iterator();
+ while (itr.hasNext()) {
+ System.out.println(itr.next().toString());
+ }
+ assertEquals("incorrect paths count", 4, result.size());
+ // assertEquals("printing the paths", outContent.toString());
+ }
+
+ @Test
+ public void test6Path() {
+ graph = new AdjacencyListsGraph<>(of(A, B, C, D, E, F),
+ of(new TestEdge(A, B, 1),
+ new TestEdge(B, A, 1),
+ new TestEdge(A, C, 1),
+ new TestEdge(C, A, 1),
+ new TestEdge(B, D, 1),
+ new TestEdge(D, B, 1),
+ new TestEdge(B, C, 1),
+ new TestEdge(C, B, 1),
+ new TestEdge(D, E, 1),
+ new TestEdge(E, D, 1),
+ new TestEdge(C, E, 1),
+ new TestEdge(E, C, 1),
+ new TestEdge(D, F, 1),
+ new TestEdge(F, D, 1),
+ new TestEdge(E, F, 1),
+ new TestEdge(F, E, 1)));
+ KshortestPathSearch<TestVertex, TestEdge> gs = new KshortestPathSearch<TestVertex, TestEdge>(graph);
+ List<List<TestEdge>> result = gs.search(A, F, weight, 6);
+ List<Path> paths = new ArrayList<>();
+ Iterator<List<TestEdge>> itr = result.iterator();
+ while (itr.hasNext()) {
+ System.out.println(itr.next().toString());
+ }
+ assertEquals("incorrect paths count", 6, result.size());
+ // assertEquals("printing the paths", outContent.toString());
+ }
+
+ @Test
+ public void dualEdgePath() {
+ graph = new AdjacencyListsGraph<>(of(A, B, C, D, E, F, G, H),
+ of(new TestEdge(A, B, 1), new TestEdge(A, C, 3),
+ new TestEdge(B, D, 2), new TestEdge(B, C, 1),
+ new TestEdge(B, E, 4), new TestEdge(C, E, 1),
+ new TestEdge(D, H, 5), new TestEdge(D, E, 1),
+ new TestEdge(E, F, 1), new TestEdge(F, D, 1),
+ new TestEdge(F, G, 1), new TestEdge(F, H, 1),
+ new TestEdge(A, E, 3), new TestEdge(B, D, 1)));
+ KshortestPathSearch<TestVertex, TestEdge> gs = new KshortestPathSearch<TestVertex, TestEdge>(graph);
+ List<List<TestEdge>> result = gs.search(A, G, weight, 6);
+ List<Path> paths = new ArrayList<>();
+ Iterator<List<TestEdge>> itr = result.iterator();
+ while (itr.hasNext()) {
+ System.out.println(itr.next().toString());
+ }
+ assertEquals("incorrect paths count", 6, result.size());
+ // assertEquals("printing the paths", outContent.toString());
+ }
+
+ @BeforeClass
+ public static void setUpBeforeClass() throws Exception {
+ }
+
+ @AfterClass
+ public static void tearDownAfterClass() throws Exception {
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ // System.setOut(new PrintStream(outContent));
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ // System.setOut(null);
+ }
+
+}
diff --git a/utils/misc/src/test/java/org/onlab/packet/Ip4AddressTest.java b/utils/misc/src/test/java/org/onlab/packet/Ip4AddressTest.java
index 36463cf..3bbf000 100644
--- a/utils/misc/src/test/java/org/onlab/packet/Ip4AddressTest.java
+++ b/utils/misc/src/test/java/org/onlab/packet/Ip4AddressTest.java
@@ -15,10 +15,13 @@
*/
package org.onlab.packet;
+import com.google.common.net.InetAddresses;
+import com.google.common.testing.EqualsTester;
import org.junit.Test;
+import java.net.InetAddress;
+
import static org.hamcrest.Matchers.is;
-import static org.hamcrest.Matchers.not;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.onlab.junit.ImmutableClassChecker.assertThatClassIsImmutable;
@@ -36,10 +39,18 @@
}
/**
+ * Tests the IPv4 address version constant.
+ */
+ @Test
+ public void testAddressVersion() {
+ assertThat(Ip4Address.VERSION, is(IpAddress.Version.INET));
+ }
+
+ /**
* Tests the length of the address in bytes (octets).
*/
@Test
- public void testAddrBytelen() {
+ public void testAddrByteLength() {
assertThat(Ip4Address.BYTE_LENGTH, is(4));
}
@@ -47,299 +58,375 @@
* Tests the length of the address in bits.
*/
@Test
- public void testAddrBitlen() {
+ public void testAddrBitLength() {
assertThat(Ip4Address.BIT_LENGTH, is(32));
}
/**
- * Tests default class constructor.
+ * Tests returning the IP address version.
*/
@Test
- public void testDefaultConstructor() {
- Ip4Address ip4Address = new Ip4Address();
- assertThat(ip4Address.toString(), is("0.0.0.0"));
+ public void testVersion() {
+ Ip4Address ipAddress;
+
+ // IPv4
+ ipAddress = Ip4Address.valueOf("0.0.0.0");
+ assertThat(ipAddress.version(), is(IpAddress.Version.INET));
}
/**
- * Tests valid class copy constructor.
+ * Tests returning an IPv4 address as a byte array.
*/
@Test
- public void testCopyConstructor() {
- Ip4Address fromAddr = new Ip4Address("1.2.3.4");
- Ip4Address ip4Address = new Ip4Address(fromAddr);
- assertThat(ip4Address.toString(), is("1.2.3.4"));
+ public void testAddressToOctetsIPv4() {
+ Ip4Address ipAddress;
+ byte[] value;
- fromAddr = new Ip4Address("0.0.0.0");
- ip4Address = new Ip4Address(fromAddr);
- assertThat(ip4Address.toString(), is("0.0.0.0"));
+ value = new byte[] {1, 2, 3, 4};
+ ipAddress = Ip4Address.valueOf("1.2.3.4");
+ assertThat(ipAddress.toOctets(), is(value));
- fromAddr = new Ip4Address("255.255.255.255");
- ip4Address = new Ip4Address(fromAddr);
- assertThat(ip4Address.toString(), is("255.255.255.255"));
+ value = new byte[] {0, 0, 0, 0};
+ ipAddress = Ip4Address.valueOf("0.0.0.0");
+ assertThat(ipAddress.toOctets(), is(value));
+
+ value = new byte[] {(byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff};
+ ipAddress = Ip4Address.valueOf("255.255.255.255");
+ assertThat(ipAddress.toOctets(), is(value));
}
/**
- * Tests invalid class copy constructor for a null object to copy from.
+ * Tests returning an IPv4 address as an integer.
+ */
+ @Test
+ public void testToInt() {
+ Ip4Address ipAddress;
+
+ ipAddress = Ip4Address.valueOf("1.2.3.4");
+ assertThat(ipAddress.toInt(), is(0x01020304));
+
+ ipAddress = Ip4Address.valueOf("0.0.0.0");
+ assertThat(ipAddress.toInt(), is(0));
+
+ ipAddress = Ip4Address.valueOf("255.255.255.255");
+ assertThat(ipAddress.toInt(), is(-1));
+ }
+
+ /**
+ * Tests valueOf() converter for IPv4 integer value.
+ */
+ @Test
+ public void testValueOfForIntegerIPv4() {
+ Ip4Address ipAddress;
+
+ ipAddress = Ip4Address.valueOf(0x01020304);
+ assertThat(ipAddress.toString(), is("1.2.3.4"));
+
+ ipAddress = Ip4Address.valueOf(0);
+ assertThat(ipAddress.toString(), is("0.0.0.0"));
+
+ ipAddress = Ip4Address.valueOf(0xffffffff);
+ assertThat(ipAddress.toString(), is("255.255.255.255"));
+ }
+
+ /**
+ * Tests valueOf() converter for IPv4 byte array.
+ */
+ @Test
+ public void testValueOfByteArrayIPv4() {
+ Ip4Address ipAddress;
+ byte[] value;
+
+ value = new byte[] {1, 2, 3, 4};
+ ipAddress = Ip4Address.valueOf(value);
+ assertThat(ipAddress.toString(), is("1.2.3.4"));
+
+ value = new byte[] {0, 0, 0, 0};
+ ipAddress = Ip4Address.valueOf(value);
+ assertThat(ipAddress.toString(), is("0.0.0.0"));
+
+ value = new byte[] {(byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff};
+ ipAddress = Ip4Address.valueOf(value);
+ assertThat(ipAddress.toString(), is("255.255.255.255"));
+ }
+
+ /**
+ * Tests invalid valueOf() converter for a null array for IPv4.
*/
@Test(expected = NullPointerException.class)
- public void testInvalidConstructorNullObject() {
- Ip4Address fromAddr = null;
- Ip4Address ip4Address = new Ip4Address(fromAddr);
+ public void testInvalidValueOfNullArrayIPv4() {
+ Ip4Address ipAddress;
+ byte[] value;
+
+ value = null;
+ ipAddress = Ip4Address.valueOf(value);
}
/**
- * Tests valid class constructor for an integer value.
- */
- @Test
- public void testConstructorForInteger() {
- Ip4Address ip4Address = new Ip4Address(0x01020304);
- assertThat(ip4Address.toString(), is("1.2.3.4"));
-
- ip4Address = new Ip4Address(0);
- assertThat(ip4Address.toString(), is("0.0.0.0"));
-
- ip4Address = new Ip4Address(0xffffffff);
- assertThat(ip4Address.toString(), is("255.255.255.255"));
- }
-
- /**
- * Tests valid class constructor for an array value.
- */
- @Test
- public void testConstructorForArray() {
- final byte[] value1 = new byte[] {1, 2, 3, 4};
- Ip4Address ip4Address = new Ip4Address(value1);
- assertThat(ip4Address.toString(), is("1.2.3.4"));
-
- final byte[] value2 = new byte[] {0, 0, 0, 0};
- ip4Address = new Ip4Address(value2);
- assertThat(ip4Address.toString(), is("0.0.0.0"));
-
- final byte[] value3 = new byte[] {(byte) 0xff, (byte) 0xff,
- (byte) 0xff, (byte) 0xff};
- ip4Address = new Ip4Address(value3);
- assertThat(ip4Address.toString(), is("255.255.255.255"));
- }
-
- /**
- * Tests valid class constructor for an array value and an offset.
- */
- @Test
- public void testConstructorForArrayAndOffset() {
- final byte[] value1 = new byte[] {11, 22, 33, // Preamble
- 1, 2, 3, 4,
- 44, 55}; // Extra bytes
- Ip4Address ip4Address = new Ip4Address(value1, 3);
- assertThat(ip4Address.toString(), is("1.2.3.4"));
-
- final byte[] value2 = new byte[] {11, 22, // Preamble
- 0, 0, 0, 0,
- 33}; // Extra bytes
- ip4Address = new Ip4Address(value2, 2);
- assertThat(ip4Address.toString(), is("0.0.0.0"));
-
- final byte[] value3 = new byte[] {11, 22, // Preamble
- (byte) 0xff, (byte) 0xff,
- (byte) 0xff, (byte) 0xff,
- 33}; // Extra bytes
- ip4Address = new Ip4Address(value3, 2);
- assertThat(ip4Address.toString(), is("255.255.255.255"));
- }
-
- /**
- * Tests invalid class constructor for a null array.
- */
- @Test(expected = NullPointerException.class)
- public void testInvalidConstructorNullArray() {
- final byte[] fromArray = null;
- Ip4Address ip4Address = new Ip4Address(fromArray);
- }
-
- /**
- * Tests invalid class constructor for an array that is too short.
+ * Tests invalid valueOf() converger for an array that is too short for
+ * IPv4.
*/
@Test(expected = IllegalArgumentException.class)
- public void testInvalidConstructorShortArray() {
- final byte[] fromArray = new byte[] {1, 2, 3};
- Ip4Address ip4Address = new Ip4Address(fromArray);
+ public void testInvalidValueOfShortArrayIPv4() {
+ Ip4Address ipAddress;
+ byte[] value;
+
+ value = new byte[] {1, 2, 3};
+ ipAddress = Ip4Address.valueOf(value);
}
/**
- * Tests invalid class constructor for an array and an invalid offset.
- */
- @Test(expected = IllegalArgumentException.class)
- public void testInvalidConstructorArrayInvalidOffset() {
- final byte[] value1 = new byte[] {11, 22, 33, // Preamble
- 1, 2, 3, 4,
- 44, 55}; // Extra bytes
- Ip4Address ip4Address = new Ip4Address(value1, 6);
- }
-
- /**
- * Tests valid class constructor for a string.
+ * Tests valueOf() converter for IPv4 byte array and an offset.
*/
@Test
- public void testConstructorForString() {
- Ip4Address ip4Address = new Ip4Address("1.2.3.4");
- assertThat(ip4Address.toString(), is("1.2.3.4"));
+ public void testValueOfByteArrayOffsetIPv4() {
+ Ip4Address ipAddress;
+ byte[] value;
- ip4Address = new Ip4Address("0.0.0.0");
- assertThat(ip4Address.toString(), is("0.0.0.0"));
+ value = new byte[] {11, 22, 33, // Preamble
+ 1, 2, 3, 4,
+ 44, 55}; // Extra bytes
+ ipAddress = Ip4Address.valueOf(value, 3);
+ assertThat(ipAddress.toString(), is("1.2.3.4"));
- ip4Address = new Ip4Address("255.255.255.255");
- assertThat(ip4Address.toString(), is("255.255.255.255"));
+ value = new byte[] {11, 22, // Preamble
+ 0, 0, 0, 0,
+ 33}; // Extra bytes
+ ipAddress = Ip4Address.valueOf(value, 2);
+ assertThat(ipAddress.toString(), is("0.0.0.0"));
+
+ value = new byte[] {11, 22, // Preamble
+ (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff,
+ 33}; // Extra bytes
+ ipAddress = Ip4Address.valueOf(value, 2);
+ assertThat(ipAddress.toString(), is("255.255.255.255"));
}
/**
- * Tests invalid class constructor for a null string.
+ * Tests invalid valueOf() converger for an array and an invalid offset
+ * for IPv4.
+ */
+ @Test(expected = IllegalArgumentException.class)
+ public void testInvalidValueOfArrayInvalidOffsetIPv4() {
+ Ip4Address ipAddress;
+ byte[] value;
+
+ value = new byte[] {11, 22, 33, // Preamble
+ 1, 2, 3, 4,
+ 44, 55}; // Extra bytes
+ ipAddress = Ip4Address.valueOf(value, 6);
+ }
+
+ /**
+ * Tests valueOf() converter for IPv4 InetAddress.
+ */
+ @Test
+ public void testValueOfInetAddressIPv4() {
+ Ip4Address ipAddress;
+ InetAddress inetAddress;
+
+ inetAddress = InetAddresses.forString("1.2.3.4");
+ ipAddress = Ip4Address.valueOf(inetAddress);
+ assertThat(ipAddress.toString(), is("1.2.3.4"));
+
+ inetAddress = InetAddresses.forString("0.0.0.0");
+ ipAddress = Ip4Address.valueOf(inetAddress);
+ assertThat(ipAddress.toString(), is("0.0.0.0"));
+
+ inetAddress = InetAddresses.forString("255.255.255.255");
+ ipAddress = Ip4Address.valueOf(inetAddress);
+ assertThat(ipAddress.toString(), is("255.255.255.255"));
+ }
+
+ /**
+ * Tests valueOf() converter for IPv4 string.
+ */
+ @Test
+ public void testValueOfStringIPv4() {
+ Ip4Address ipAddress;
+
+ ipAddress = Ip4Address.valueOf("1.2.3.4");
+ assertThat(ipAddress.toString(), is("1.2.3.4"));
+
+ ipAddress = Ip4Address.valueOf("0.0.0.0");
+ assertThat(ipAddress.toString(), is("0.0.0.0"));
+
+ ipAddress = Ip4Address.valueOf("255.255.255.255");
+ assertThat(ipAddress.toString(), is("255.255.255.255"));
+ }
+
+ /**
+ * Tests invalid valueOf() converter for a null string.
*/
@Test(expected = NullPointerException.class)
- public void testInvalidConstructorNullString() {
+ public void testInvalidValueOfNullString() {
+ Ip4Address ipAddress;
+
String fromString = null;
- Ip4Address ip4Address = new Ip4Address(fromString);
+ ipAddress = Ip4Address.valueOf(fromString);
}
/**
- * Tests invalid class constructor for an empty string.
+ * Tests invalid valueOf() converter for an empty string.
*/
@Test(expected = IllegalArgumentException.class)
- public void testInvalidConstructors() {
- // Check constructor for invalid ID: empty string
- Ip4Address ip4Address = new Ip4Address("");
+ public void testInvalidValueOfEmptyString() {
+ Ip4Address ipAddress;
+
+ String fromString = "";
+ ipAddress = Ip4Address.valueOf(fromString);
}
/**
- * Tests returning the address as a byte array.
+ * Tests invalid valueOf() converter for an incorrect string.
*/
- @Test
- public void testAddressToOctets() {
- final byte[] value1 = new byte[] {1, 2, 3, 4};
- Ip4Address ip4Address = new Ip4Address("1.2.3.4");
- assertThat(ip4Address.toOctets(), is(value1));
+ @Test(expected = IllegalArgumentException.class)
+ public void testInvalidValueOfIncorrectString() {
+ Ip4Address ipAddress;
- final byte[] value2 = new byte[] {0, 0, 0, 0};
- ip4Address = new Ip4Address("0.0.0.0");
- assertThat(ip4Address.toOctets(), is(value2));
-
- final byte[] value3 = new byte[] {(byte) 0xff, (byte) 0xff,
- (byte) 0xff, (byte) 0xff};
- ip4Address = new Ip4Address("255.255.255.255");
- assertThat(ip4Address.toOctets(), is(value3));
+ String fromString = "NoSuchIpAddress";
+ ipAddress = Ip4Address.valueOf(fromString);
}
/**
- * Tests making a mask prefix for a given prefix length.
+ * Tests making a mask prefix for a given prefix length for IPv4.
*/
@Test
- public void testMakeMaskPrefix() {
- Ip4Address ip4Address = Ip4Address.makeMaskPrefix(25);
- assertThat(ip4Address.toString(), is("255.255.255.128"));
+ public void testMakeMaskPrefixIPv4() {
+ Ip4Address ipAddress;
- ip4Address = Ip4Address.makeMaskPrefix(0);
- assertThat(ip4Address.toString(), is("0.0.0.0"));
+ ipAddress = Ip4Address.makeMaskPrefix(25);
+ assertThat(ipAddress.toString(), is("255.255.255.128"));
- ip4Address = Ip4Address.makeMaskPrefix(32);
- assertThat(ip4Address.toString(), is("255.255.255.255"));
+ ipAddress = Ip4Address.makeMaskPrefix(0);
+ assertThat(ipAddress.toString(), is("0.0.0.0"));
+
+ ipAddress = Ip4Address.makeMaskPrefix(32);
+ assertThat(ipAddress.toString(), is("255.255.255.255"));
}
/**
- * Tests making of a masked address.
+ * Tests making a mask prefix for an invalid prefix length for IPv4:
+ * negative prefix length.
*/
- @Test
- public void testMakeMaskedAddress() {
- Ip4Address ip4Address = new Ip4Address("1.2.3.5");
- Ip4Address ip4AddressMasked =
- Ip4Address.makeMaskedAddress(ip4Address, 24);
- assertThat(ip4AddressMasked.toString(), is("1.2.3.0"));
+ @Test(expected = IllegalArgumentException.class)
+ public void testInvalidMakeNegativeMaskPrefixIPv4() {
+ Ip4Address ipAddress;
- ip4AddressMasked = Ip4Address.makeMaskedAddress(ip4Address, 0);
- assertThat(ip4AddressMasked.toString(), is("0.0.0.0"));
-
- ip4AddressMasked = Ip4Address.makeMaskedAddress(ip4Address, 32);
- assertThat(ip4AddressMasked.toString(), is("1.2.3.5"));
+ ipAddress = Ip4Address.makeMaskPrefix(-1);
}
/**
- * Tests getting the value of an address.
+ * Tests making a mask prefix for an invalid prefix length for IPv4:
+ * too long prefix length.
*/
- @Test
- public void testGetValue() {
- Ip4Address ip4Address = new Ip4Address("1.2.3.4");
- assertThat(ip4Address.getValue(), is(0x01020304));
+ @Test(expected = IllegalArgumentException.class)
+ public void testInvalidMakeTooLongMaskPrefixIPv4() {
+ Ip4Address ipAddress;
- ip4Address = new Ip4Address("0.0.0.0");
- assertThat(ip4Address.getValue(), is(0));
-
- ip4Address = new Ip4Address("255.255.255.255");
- assertThat(ip4Address.getValue(), is(-1));
+ ipAddress = Ip4Address.makeMaskPrefix(33);
}
/**
- * Tests equality of {@link Ip4Address}.
+ * Tests making of a masked address for IPv4.
*/
@Test
- public void testEquality() {
- Ip4Address addr1 = new Ip4Address("1.2.3.4");
- Ip4Address addr2 = new Ip4Address("1.2.3.4");
- assertThat(addr1, is(addr2));
+ public void testMakeMaskedAddressIPv4() {
+ Ip4Address ipAddress = Ip4Address.valueOf("1.2.3.5");
+ Ip4Address ipAddressMasked;
- addr1 = new Ip4Address("0.0.0.0");
- addr2 = new Ip4Address("0.0.0.0");
- assertThat(addr1, is(addr2));
+ ipAddressMasked = Ip4Address.makeMaskedAddress(ipAddress, 24);
+ assertThat(ipAddressMasked.toString(), is("1.2.3.0"));
- addr1 = new Ip4Address("255.255.255.255");
- addr2 = new Ip4Address("255.255.255.255");
- assertThat(addr1, is(addr2));
+ ipAddressMasked = Ip4Address.makeMaskedAddress(ipAddress, 0);
+ assertThat(ipAddressMasked.toString(), is("0.0.0.0"));
+
+ ipAddressMasked = Ip4Address.makeMaskedAddress(ipAddress, 32);
+ assertThat(ipAddressMasked.toString(), is("1.2.3.5"));
}
/**
- * Tests non-equality of {@link Ip4Address}.
+ * Tests making of a masked address for invalid prefix length for IPv4:
+ * negative prefix length.
*/
- @Test
- public void testNonEquality() {
- Ip4Address addr1 = new Ip4Address("1.2.3.4");
- Ip4Address addr2 = new Ip4Address("1.2.3.5");
- Ip4Address addr3 = new Ip4Address("0.0.0.0");
- Ip4Address addr4 = new Ip4Address("255.255.255.255");
- assertThat(addr1, is(not(addr2)));
- assertThat(addr3, is(not(addr2)));
- assertThat(addr4, is(not(addr2)));
+ @Test(expected = IllegalArgumentException.class)
+ public void testInvalidMakeNegativeMaskedAddressIPv4() {
+ Ip4Address ipAddress = Ip4Address.valueOf("1.2.3.5");
+ Ip4Address ipAddressMasked;
+
+ ipAddressMasked = Ip4Address.makeMaskedAddress(ipAddress, -1);
}
/**
- * Tests comparison of {@link Ip4Address}.
+ * Tests making of a masked address for an invalid prefix length for IPv4:
+ * too long prefix length.
+ */
+ @Test(expected = IllegalArgumentException.class)
+ public void testInvalidMakeTooLongMaskedAddressIPv4() {
+ Ip4Address ipAddress = Ip4Address.valueOf("1.2.3.5");
+ Ip4Address ipAddressMasked;
+
+ ipAddressMasked = Ip4Address.makeMaskedAddress(ipAddress, 33);
+ }
+
+ /**
+ * Tests comparison of {@link Ip4Address} for IPv4.
*/
@Test
- public void testComparison() {
- Ip4Address addr1 = new Ip4Address("1.2.3.4");
- Ip4Address addr2 = new Ip4Address("1.2.3.4");
- Ip4Address addr3 = new Ip4Address("1.2.3.3");
- Ip4Address addr4 = new Ip4Address("1.2.3.5");
+ public void testComparisonIPv4() {
+ Ip4Address addr1, addr2, addr3, addr4;
+
+ addr1 = Ip4Address.valueOf("1.2.3.4");
+ addr2 = Ip4Address.valueOf("1.2.3.4");
+ addr3 = Ip4Address.valueOf("1.2.3.3");
+ addr4 = Ip4Address.valueOf("1.2.3.5");
assertTrue(addr1.compareTo(addr2) == 0);
assertTrue(addr1.compareTo(addr3) > 0);
assertTrue(addr1.compareTo(addr4) < 0);
- addr1 = new Ip4Address("255.2.3.4");
- addr2 = new Ip4Address("255.2.3.4");
- addr3 = new Ip4Address("255.2.3.3");
- addr4 = new Ip4Address("255.2.3.5");
+ addr1 = Ip4Address.valueOf("255.2.3.4");
+ addr2 = Ip4Address.valueOf("255.2.3.4");
+ addr3 = Ip4Address.valueOf("255.2.3.3");
+ addr4 = Ip4Address.valueOf("255.2.3.5");
assertTrue(addr1.compareTo(addr2) == 0);
assertTrue(addr1.compareTo(addr3) > 0);
assertTrue(addr1.compareTo(addr4) < 0);
}
/**
- * Tests object string representation.
+ * Tests equality of {@link Ip4Address} for IPv4.
*/
@Test
- public void testToString() {
- Ip4Address ip4Address = new Ip4Address("1.2.3.4");
- assertThat(ip4Address.toString(), is("1.2.3.4"));
+ public void testEqualityIPv4() {
+ new EqualsTester()
+ .addEqualityGroup(Ip4Address.valueOf("1.2.3.4"),
+ Ip4Address.valueOf("1.2.3.4"))
+ .addEqualityGroup(Ip4Address.valueOf("1.2.3.5"),
+ Ip4Address.valueOf("1.2.3.5"))
+ .addEqualityGroup(Ip4Address.valueOf("0.0.0.0"),
+ Ip4Address.valueOf("0.0.0.0"))
+ .addEqualityGroup(Ip4Address.valueOf("255.255.255.255"),
+ Ip4Address.valueOf("255.255.255.255"))
+ .testEquals();
+ }
- ip4Address = new Ip4Address("0.0.0.0");
- assertThat(ip4Address.toString(), is("0.0.0.0"));
+ /**
+ * Tests object string representation for IPv4.
+ */
+ @Test
+ public void testToStringIPv4() {
+ Ip4Address ipAddress;
- ip4Address = new Ip4Address("255.255.255.255");
- assertThat(ip4Address.toString(), is("255.255.255.255"));
+ ipAddress = Ip4Address.valueOf("1.2.3.4");
+ assertThat(ipAddress.toString(), is("1.2.3.4"));
+
+ ipAddress = Ip4Address.valueOf("0.0.0.0");
+ assertThat(ipAddress.toString(), is("0.0.0.0"));
+
+ ipAddress = Ip4Address.valueOf("255.255.255.255");
+ assertThat(ipAddress.toString(), is("255.255.255.255"));
}
}
diff --git a/utils/misc/src/test/java/org/onlab/packet/Ip4PrefixTest.java b/utils/misc/src/test/java/org/onlab/packet/Ip4PrefixTest.java
index 84ef0b3..c731ed6 100644
--- a/utils/misc/src/test/java/org/onlab/packet/Ip4PrefixTest.java
+++ b/utils/misc/src/test/java/org/onlab/packet/Ip4PrefixTest.java
@@ -15,12 +15,14 @@
*/
package org.onlab.packet;
+import com.google.common.testing.EqualsTester;
import org.junit.Test;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;
-import static org.hamcrest.Matchers.not;
import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
import static org.onlab.junit.ImmutableClassChecker.assertThatClassIsImmutable;
/**
@@ -36,175 +38,497 @@
}
/**
- * Tests default class constructor.
+ * Tests the IPv4 prefix address version constant.
*/
@Test
- public void testDefaultConstructor() {
- Ip4Prefix ip4prefix = new Ip4Prefix();
- assertThat(ip4prefix.toString(), is("0.0.0.0/0"));
+ public void testAddressVersion() {
+ assertThat(Ip4Prefix.VERSION, is(IpAddress.Version.INET));
}
/**
- * Tests valid class copy constructor.
+ * Tests the maximum mask length.
*/
@Test
- public void testCopyConstructor() {
- Ip4Prefix fromAddr = new Ip4Prefix("1.2.3.0/24");
- Ip4Prefix ip4prefix = new Ip4Prefix(fromAddr);
- assertThat(ip4prefix.toString(), is("1.2.3.0/24"));
-
- fromAddr = new Ip4Prefix("0.0.0.0/0");
- ip4prefix = new Ip4Prefix(fromAddr);
- assertThat(ip4prefix.toString(), is("0.0.0.0/0"));
-
- fromAddr = new Ip4Prefix("255.255.255.255/32");
- ip4prefix = new Ip4Prefix(fromAddr);
- assertThat(ip4prefix.toString(), is("255.255.255.255/32"));
+ public void testMaxMaskLength() {
+ assertThat(Ip4Prefix.MAX_MASK_LENGTH, is(32));
}
/**
- * Tests invalid class copy constructor for a null object to copy from.
- */
- @Test(expected = NullPointerException.class)
- public void testInvalidConstructorNullObject() {
- Ip4Prefix fromAddr = null;
- Ip4Prefix ip4prefix = new Ip4Prefix(fromAddr);
- }
-
- /**
- * Tests valid class constructor for an address and prefix length.
+ * Tests returning the IP version of the prefix.
*/
@Test
- public void testConstructorForAddressAndPrefixLength() {
- Ip4Prefix ip4prefix =
- new Ip4Prefix(new Ip4Address("1.2.3.0"), (short) 24);
- assertThat(ip4prefix.toString(), is("1.2.3.0/24"));
+ public void testVersion() {
+ Ip4Prefix ipPrefix;
- ip4prefix = new Ip4Prefix(new Ip4Address("1.2.3.4"), (short) 24);
- assertThat(ip4prefix.toString(), is("1.2.3.0/24"));
-
- ip4prefix = new Ip4Prefix(new Ip4Address("1.2.3.5"), (short) 32);
- assertThat(ip4prefix.toString(), is("1.2.3.5/32"));
-
- ip4prefix = new Ip4Prefix(new Ip4Address("0.0.0.0"), (short) 0);
- assertThat(ip4prefix.toString(), is("0.0.0.0/0"));
-
- ip4prefix =
- new Ip4Prefix(new Ip4Address("255.255.255.255"), (short) 32);
- assertThat(ip4prefix.toString(), is("255.255.255.255/32"));
+ // IPv4
+ ipPrefix = Ip4Prefix.valueOf("0.0.0.0/0");
+ assertThat(ipPrefix.version(), is(IpAddress.Version.INET));
}
/**
- * Tests valid class constructor for a string.
+ * Tests returning the IP address value and IP address prefix length of
+ * an IPv4 prefix.
*/
@Test
- public void testConstructorForString() {
- Ip4Prefix ip4prefix = new Ip4Prefix("1.2.3.0/24");
- assertThat(ip4prefix.toString(), is("1.2.3.0/24"));
+ public void testAddressAndPrefixLengthIPv4() {
+ Ip4Prefix ipPrefix;
- ip4prefix = new Ip4Prefix("1.2.3.4/24");
- assertThat(ip4prefix.toString(), is("1.2.3.0/24"));
+ ipPrefix = Ip4Prefix.valueOf("1.2.3.0/24");
+ assertThat(ipPrefix.address(), equalTo(Ip4Address.valueOf("1.2.3.0")));
+ assertThat(ipPrefix.prefixLength(), is(24));
- ip4prefix = new Ip4Prefix("1.2.3.5/32");
- assertThat(ip4prefix.toString(), is("1.2.3.5/32"));
+ ipPrefix = Ip4Prefix.valueOf("1.2.3.4/24");
+ assertThat(ipPrefix.address(), equalTo(Ip4Address.valueOf("1.2.3.0")));
+ assertThat(ipPrefix.prefixLength(), is(24));
- ip4prefix = new Ip4Prefix("0.0.0.0/0");
- assertThat(ip4prefix.toString(), is("0.0.0.0/0"));
+ ipPrefix = Ip4Prefix.valueOf("1.2.3.4/32");
+ assertThat(ipPrefix.address(), equalTo(Ip4Address.valueOf("1.2.3.4")));
+ assertThat(ipPrefix.prefixLength(), is(32));
- ip4prefix = new Ip4Prefix("255.255.255.255/32");
- assertThat(ip4prefix.toString(), is("255.255.255.255/32"));
+ ipPrefix = Ip4Prefix.valueOf("1.2.3.5/32");
+ assertThat(ipPrefix.address(), equalTo(Ip4Address.valueOf("1.2.3.5")));
+ assertThat(ipPrefix.prefixLength(), is(32));
+
+ ipPrefix = Ip4Prefix.valueOf("0.0.0.0/0");
+ assertThat(ipPrefix.address(), equalTo(Ip4Address.valueOf("0.0.0.0")));
+ assertThat(ipPrefix.prefixLength(), is(0));
+
+ ipPrefix = Ip4Prefix.valueOf("255.255.255.255/32");
+ assertThat(ipPrefix.address(),
+ equalTo(Ip4Address.valueOf("255.255.255.255")));
+ assertThat(ipPrefix.prefixLength(), is(32));
}
/**
- * Tests invalid class constructor for a null string.
+ * Tests valueOf() converter for IPv4 integer value.
*/
- @Test(expected = NullPointerException.class)
- public void testInvalidConstructorNullString() {
- String fromString = null;
- Ip4Prefix ip4prefix = new Ip4Prefix(fromString);
+ @Test
+ public void testValueOfForIntegerIPv4() {
+ Ip4Prefix ipPrefix;
+
+ ipPrefix = Ip4Prefix.valueOf(0x01020304, 24);
+ assertThat(ipPrefix.toString(), is("1.2.3.0/24"));
+
+ ipPrefix = Ip4Prefix.valueOf(0x01020304, 32);
+ assertThat(ipPrefix.toString(), is("1.2.3.4/32"));
+
+ ipPrefix = Ip4Prefix.valueOf(0x01020305, 32);
+ assertThat(ipPrefix.toString(), is("1.2.3.5/32"));
+
+ ipPrefix = Ip4Prefix.valueOf(0, 0);
+ assertThat(ipPrefix.toString(), is("0.0.0.0/0"));
+
+ ipPrefix = Ip4Prefix.valueOf(0, 32);
+ assertThat(ipPrefix.toString(), is("0.0.0.0/32"));
+
+ ipPrefix = Ip4Prefix.valueOf(0xffffffff, 0);
+ assertThat(ipPrefix.toString(), is("0.0.0.0/0"));
+
+ ipPrefix = Ip4Prefix.valueOf(0xffffffff, 16);
+ assertThat(ipPrefix.toString(), is("255.255.0.0/16"));
+
+ ipPrefix = Ip4Prefix.valueOf(0xffffffff, 32);
+ assertThat(ipPrefix.toString(), is("255.255.255.255/32"));
}
/**
- * Tests invalid class constructor for an empty string.
+ * Tests invalid valueOf() converter for IPv4 integer value and
+ * negative prefix length.
*/
@Test(expected = IllegalArgumentException.class)
- public void testInvalidConstructors() {
- // Check constructor for invalid ID: empty string
- Ip4Prefix ip4prefix = new Ip4Prefix("");
+ public void testInvalidValueOfIntegerNegativePrefixLengthIPv4() {
+ Ip4Prefix ipPrefix;
+
+ ipPrefix = Ip4Prefix.valueOf(0x01020304, -1);
}
/**
- * Tests getting the value of an address.
+ * Tests invalid valueOf() converter for IPv4 integer value and
+ * too long prefix length.
*/
- @Test
- public void testGetValue() {
- Ip4Prefix ip4prefix = new Ip4Prefix("1.2.3.0/24");
- assertThat(ip4prefix.getAddress(), equalTo(new Ip4Address("1.2.3.0")));
- assertThat(ip4prefix.getPrefixLen(), is((short) 24));
+ @Test(expected = IllegalArgumentException.class)
+ public void testInvalidValueOfIntegerTooLongPrefixLengthIPv4() {
+ Ip4Prefix ipPrefix;
- ip4prefix = new Ip4Prefix("0.0.0.0/0");
- assertThat(ip4prefix.getAddress(), equalTo(new Ip4Address("0.0.0.0")));
- assertThat(ip4prefix.getPrefixLen(), is((short) 0));
-
- ip4prefix = new Ip4Prefix("255.255.255.255/32");
- assertThat(ip4prefix.getAddress(),
- equalTo(new Ip4Address("255.255.255.255")));
- assertThat(ip4prefix.getPrefixLen(), is((short) 32));
+ ipPrefix = Ip4Prefix.valueOf(0x01020304, 33);
}
/**
- * Tests equality of {@link Ip4Address}.
+ * Tests valueOf() converter for IPv4 byte array.
*/
@Test
- public void testEquality() {
- Ip4Prefix addr1net = new Ip4Prefix("1.2.3.0/24");
- Ip4Prefix addr2net = new Ip4Prefix("1.2.3.0/24");
- assertThat(addr1net, is(addr2net));
+ public void testValueOfByteArrayIPv4() {
+ Ip4Prefix ipPrefix;
+ byte[] value;
- addr1net = new Ip4Prefix("1.2.3.0/24");
- addr2net = new Ip4Prefix("1.2.3.4/24");
- assertThat(addr1net, is(addr2net));
+ value = new byte[] {1, 2, 3, 4};
+ ipPrefix = Ip4Prefix.valueOf(value, 24);
+ assertThat(ipPrefix.toString(), is("1.2.3.0/24"));
- addr1net = new Ip4Prefix("0.0.0.0/0");
- addr2net = new Ip4Prefix("0.0.0.0/0");
- assertThat(addr1net, is(addr2net));
+ ipPrefix = Ip4Prefix.valueOf(value, 32);
+ assertThat(ipPrefix.toString(), is("1.2.3.4/32"));
- addr1net = new Ip4Prefix("255.255.255.255/32");
- addr2net = new Ip4Prefix("255.255.255.255/32");
- assertThat(addr1net, is(addr2net));
+ value = new byte[] {1, 2, 3, 5};
+ ipPrefix = Ip4Prefix.valueOf(value, 32);
+ assertThat(ipPrefix.toString(), is("1.2.3.5/32"));
+
+ value = new byte[] {0, 0, 0, 0};
+ ipPrefix = Ip4Prefix.valueOf(value, 0);
+ assertThat(ipPrefix.toString(), is("0.0.0.0/0"));
+
+ ipPrefix = Ip4Prefix.valueOf(value, 32);
+ assertThat(ipPrefix.toString(), is("0.0.0.0/32"));
+
+ value = new byte[] {(byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff};
+ ipPrefix = Ip4Prefix.valueOf(value, 0);
+ assertThat(ipPrefix.toString(), is("0.0.0.0/0"));
+
+ ipPrefix = Ip4Prefix.valueOf(value, 16);
+ assertThat(ipPrefix.toString(), is("255.255.0.0/16"));
+
+ ipPrefix = Ip4Prefix.valueOf(value, 32);
+ assertThat(ipPrefix.toString(), is("255.255.255.255/32"));
}
/**
- * Tests non-equality of {@link Ip4Address}.
+ * Tests invalid valueOf() converter for a null array for IPv4.
*/
- @Test
- public void testNonEquality() {
- Ip4Prefix addr1net = new Ip4Prefix("1.2.0.0/16");
- Ip4Prefix addr2net = new Ip4Prefix("1.3.0.0/16");
- Ip4Prefix addr3net = new Ip4Prefix("1.3.0.0/24");
- Ip4Prefix addr4net = new Ip4Prefix("0.0.0.0/0");
- Ip4Prefix addr5net = new Ip4Prefix("255.255.255.255/32");
- assertThat(addr1net, is(not(addr2net)));
- assertThat(addr3net, is(not(addr2net)));
- assertThat(addr4net, is(not(addr2net)));
- assertThat(addr5net, is(not(addr2net)));
+ @Test(expected = NullPointerException.class)
+ public void testInvalidValueOfNullArrayIPv4() {
+ Ip4Prefix ipPrefix;
+ byte[] value;
+
+ value = null;
+ ipPrefix = Ip4Prefix.valueOf(value, 24);
}
/**
- * Tests object string representation.
+ * Tests invalid valueOf() converter for a short array for IPv4.
+ */
+ @Test(expected = IllegalArgumentException.class)
+ public void testInvalidValueOfShortArrayIPv4() {
+ Ip4Prefix ipPrefix;
+ byte[] value;
+
+ value = new byte[] {1, 2, 3};
+ ipPrefix = Ip4Prefix.valueOf(value, 24);
+ }
+
+ /**
+ * Tests invalid valueOf() converter for IPv4 byte array and
+ * negative prefix length.
+ */
+ @Test(expected = IllegalArgumentException.class)
+ public void testInvalidValueOfByteArrayNegativePrefixLengthIPv4() {
+ Ip4Prefix ipPrefix;
+ byte[] value;
+
+ value = new byte[] {1, 2, 3, 4};
+ ipPrefix = Ip4Prefix.valueOf(value, -1);
+ }
+
+ /**
+ * Tests invalid valueOf() converter for IPv4 byte array and
+ * too long prefix length.
+ */
+ @Test(expected = IllegalArgumentException.class)
+ public void testInvalidValueOfByteArrayTooLongPrefixLengthIPv4() {
+ Ip4Prefix ipPrefix;
+ byte[] value;
+
+ value = new byte[] {1, 2, 3, 4};
+ ipPrefix = Ip4Prefix.valueOf(value, 33);
+ }
+
+ /**
+ * Tests valueOf() converter for IPv4 address.
*/
@Test
- public void testToString() {
- Ip4Prefix ip4prefix = new Ip4Prefix("1.2.3.0/24");
- assertThat(ip4prefix.toString(), is("1.2.3.0/24"));
+ public void testValueOfAddressIPv4() {
+ Ip4Address ipAddress;
+ Ip4Prefix ipPrefix;
- ip4prefix = new Ip4Prefix("1.2.3.4/24");
- assertThat(ip4prefix.toString(), is("1.2.3.0/24"));
+ ipAddress = Ip4Address.valueOf("1.2.3.4");
+ ipPrefix = Ip4Prefix.valueOf(ipAddress, 24);
+ assertThat(ipPrefix.toString(), is("1.2.3.0/24"));
- ip4prefix = new Ip4Prefix("0.0.0.0/0");
- assertThat(ip4prefix.toString(), is("0.0.0.0/0"));
+ ipPrefix = Ip4Prefix.valueOf(ipAddress, 32);
+ assertThat(ipPrefix.toString(), is("1.2.3.4/32"));
- ip4prefix = new Ip4Prefix("255.255.255.255/32");
- assertThat(ip4prefix.toString(), is("255.255.255.255/32"));
+ ipAddress = Ip4Address.valueOf("1.2.3.5");
+ ipPrefix = Ip4Prefix.valueOf(ipAddress, 32);
+ assertThat(ipPrefix.toString(), is("1.2.3.5/32"));
+
+ ipAddress = Ip4Address.valueOf("0.0.0.0");
+ ipPrefix = Ip4Prefix.valueOf(ipAddress, 0);
+ assertThat(ipPrefix.toString(), is("0.0.0.0/0"));
+
+ ipPrefix = Ip4Prefix.valueOf(ipAddress, 32);
+ assertThat(ipPrefix.toString(), is("0.0.0.0/32"));
+
+ ipAddress = Ip4Address.valueOf("255.255.255.255");
+ ipPrefix = Ip4Prefix.valueOf(ipAddress, 0);
+ assertThat(ipPrefix.toString(), is("0.0.0.0/0"));
+
+ ipPrefix = Ip4Prefix.valueOf(ipAddress, 16);
+ assertThat(ipPrefix.toString(), is("255.255.0.0/16"));
+
+ ipPrefix = Ip4Prefix.valueOf(ipAddress, 32);
+ assertThat(ipPrefix.toString(), is("255.255.255.255/32"));
+ }
+
+ /**
+ * Tests invalid valueOf() converter for a null IP address.
+ */
+ @Test(expected = NullPointerException.class)
+ public void testInvalidValueOfNullAddress() {
+ Ip4Address ipAddress;
+ Ip4Prefix ipPrefix;
+
+ ipAddress = null;
+ ipPrefix = Ip4Prefix.valueOf(ipAddress, 24);
+ }
+
+ /**
+ * Tests invalid valueOf() converter for IPv4 address and
+ * negative prefix length.
+ */
+ @Test(expected = IllegalArgumentException.class)
+ public void testInvalidValueOfAddressNegativePrefixLengthIPv4() {
+ Ip4Address ipAddress;
+ Ip4Prefix ipPrefix;
+
+ ipAddress = Ip4Address.valueOf("1.2.3.4");
+ ipPrefix = Ip4Prefix.valueOf(ipAddress, -1);
+ }
+
+ /**
+ * Tests invalid valueOf() converter for IPv4 address and
+ * too long prefix length.
+ */
+ @Test(expected = IllegalArgumentException.class)
+ public void testInvalidValueOfAddressTooLongPrefixLengthIPv4() {
+ Ip4Address ipAddress;
+ Ip4Prefix ipPrefix;
+
+ ipAddress = Ip4Address.valueOf("1.2.3.4");
+ ipPrefix = Ip4Prefix.valueOf(ipAddress, 33);
+ }
+
+ /**
+ * Tests valueOf() converter for IPv4 string.
+ */
+ @Test
+ public void testValueOfStringIPv4() {
+ Ip4Prefix ipPrefix;
+
+ ipPrefix = Ip4Prefix.valueOf("1.2.3.4/24");
+ assertThat(ipPrefix.toString(), is("1.2.3.0/24"));
+
+ ipPrefix = Ip4Prefix.valueOf("1.2.3.4/32");
+ assertThat(ipPrefix.toString(), is("1.2.3.4/32"));
+
+ ipPrefix = Ip4Prefix.valueOf("1.2.3.5/32");
+ assertThat(ipPrefix.toString(), is("1.2.3.5/32"));
+
+ ipPrefix = Ip4Prefix.valueOf("0.0.0.0/0");
+ assertThat(ipPrefix.toString(), is("0.0.0.0/0"));
+
+ ipPrefix = Ip4Prefix.valueOf("0.0.0.0/32");
+ assertThat(ipPrefix.toString(), is("0.0.0.0/32"));
+
+ ipPrefix = Ip4Prefix.valueOf("255.255.255.255/0");
+ assertThat(ipPrefix.toString(), is("0.0.0.0/0"));
+
+ ipPrefix = Ip4Prefix.valueOf("255.255.255.255/16");
+ assertThat(ipPrefix.toString(), is("255.255.0.0/16"));
+
+ ipPrefix = Ip4Prefix.valueOf("255.255.255.255/32");
+ assertThat(ipPrefix.toString(), is("255.255.255.255/32"));
+ }
+
+ /**
+ * Tests invalid valueOf() converter for a null string.
+ */
+ @Test(expected = NullPointerException.class)
+ public void testInvalidValueOfNullString() {
+ Ip4Prefix ipPrefix;
+ String fromString;
+
+ fromString = null;
+ ipPrefix = Ip4Prefix.valueOf(fromString);
+ }
+
+ /**
+ * Tests invalid valueOf() converter for an empty string.
+ */
+ @Test(expected = IllegalArgumentException.class)
+ public void testInvalidValueOfEmptyString() {
+ Ip4Prefix ipPrefix;
+ String fromString;
+
+ fromString = "";
+ ipPrefix = Ip4Prefix.valueOf(fromString);
+ }
+
+ /**
+ * Tests invalid valueOf() converter for an incorrect string.
+ */
+ @Test(expected = IllegalArgumentException.class)
+ public void testInvalidValueOfIncorrectString() {
+ Ip4Prefix ipPrefix;
+ String fromString;
+
+ fromString = "NoSuchIpPrefix";
+ ipPrefix = Ip4Prefix.valueOf(fromString);
+ }
+
+ /**
+ * Tests invalid valueOf() converter for IPv4 string and
+ * negative prefix length.
+ */
+ @Test(expected = IllegalArgumentException.class)
+ public void testInvalidValueOfStringNegativePrefixLengthIPv4() {
+ Ip4Prefix ipPrefix;
+
+ ipPrefix = Ip4Prefix.valueOf("1.2.3.4/-1");
+ }
+
+ /**
+ * Tests invalid valueOf() converter for IPv4 string and
+ * too long prefix length.
+ */
+ @Test(expected = IllegalArgumentException.class)
+ public void testInvalidValueOfStringTooLongPrefixLengthIPv4() {
+ Ip4Prefix ipPrefix;
+
+ ipPrefix = Ip4Prefix.valueOf("1.2.3.4/33");
+ }
+
+ /**
+ * Tests IP prefix contains another IP prefix for IPv4.
+ */
+ @Test
+ public void testContainsIpPrefixIPv4() {
+ Ip4Prefix ipPrefix;
+
+ ipPrefix = Ip4Prefix.valueOf("1.2.0.0/24");
+ assertTrue(ipPrefix.contains(Ip4Prefix.valueOf("1.2.0.0/24")));
+ assertTrue(ipPrefix.contains(Ip4Prefix.valueOf("1.2.0.0/32")));
+ assertTrue(ipPrefix.contains(Ip4Prefix.valueOf("1.2.0.4/32")));
+ assertFalse(ipPrefix.contains(Ip4Prefix.valueOf("1.2.0.0/16")));
+ assertFalse(ipPrefix.contains(Ip4Prefix.valueOf("1.3.0.0/24")));
+ assertFalse(ipPrefix.contains(Ip4Prefix.valueOf("0.0.0.0/16")));
+ assertFalse(ipPrefix.contains(Ip4Prefix.valueOf("0.0.0.0/0")));
+ assertFalse(ipPrefix.contains(Ip4Prefix.valueOf("255.255.255.255/32")));
+
+ ipPrefix = Ip4Prefix.valueOf("1.2.0.0/32");
+ assertFalse(ipPrefix.contains(Ip4Prefix.valueOf("1.2.0.0/24")));
+ assertTrue(ipPrefix.contains(Ip4Prefix.valueOf("1.2.0.0/32")));
+ assertFalse(ipPrefix.contains(Ip4Prefix.valueOf("1.2.0.4/32")));
+ assertFalse(ipPrefix.contains(Ip4Prefix.valueOf("1.2.0.0/16")));
+ assertFalse(ipPrefix.contains(Ip4Prefix.valueOf("1.3.0.0/24")));
+ assertFalse(ipPrefix.contains(Ip4Prefix.valueOf("0.0.0.0/16")));
+ assertFalse(ipPrefix.contains(Ip4Prefix.valueOf("0.0.0.0/0")));
+ assertFalse(ipPrefix.contains(Ip4Prefix.valueOf("255.255.255.255/32")));
+
+ ipPrefix = Ip4Prefix.valueOf("0.0.0.0/0");
+ assertTrue(ipPrefix.contains(Ip4Prefix.valueOf("1.2.0.0/24")));
+ assertTrue(ipPrefix.contains(Ip4Prefix.valueOf("1.2.0.0/32")));
+ assertTrue(ipPrefix.contains(Ip4Prefix.valueOf("1.2.0.4/32")));
+ assertTrue(ipPrefix.contains(Ip4Prefix.valueOf("1.2.0.0/16")));
+ assertTrue(ipPrefix.contains(Ip4Prefix.valueOf("1.3.0.0/24")));
+ assertTrue(ipPrefix.contains(Ip4Prefix.valueOf("0.0.0.0/16")));
+ assertTrue(ipPrefix.contains(Ip4Prefix.valueOf("0.0.0.0/0")));
+ assertTrue(ipPrefix.contains(Ip4Prefix.valueOf("255.255.255.255/32")));
+
+ ipPrefix = Ip4Prefix.valueOf("255.255.255.255/32");
+ assertFalse(ipPrefix.contains(Ip4Prefix.valueOf("1.2.0.0/24")));
+ assertFalse(ipPrefix.contains(Ip4Prefix.valueOf("1.2.0.0/32")));
+ assertFalse(ipPrefix.contains(Ip4Prefix.valueOf("1.2.0.4/32")));
+ assertFalse(ipPrefix.contains(Ip4Prefix.valueOf("1.2.0.0/16")));
+ assertFalse(ipPrefix.contains(Ip4Prefix.valueOf("1.3.0.0/24")));
+ assertFalse(ipPrefix.contains(Ip4Prefix.valueOf("0.0.0.0/16")));
+ assertFalse(ipPrefix.contains(Ip4Prefix.valueOf("0.0.0.0/0")));
+ assertTrue(ipPrefix.contains(Ip4Prefix.valueOf("255.255.255.255/32")));
+ }
+
+ /**
+ * Tests IP prefix contains IP address for IPv4.
+ */
+ @Test
+ public void testContainsIpAddressIPv4() {
+ Ip4Prefix ipPrefix;
+
+ ipPrefix = Ip4Prefix.valueOf("1.2.0.0/24");
+ assertTrue(ipPrefix.contains(Ip4Address.valueOf("1.2.0.0")));
+ assertTrue(ipPrefix.contains(Ip4Address.valueOf("1.2.0.4")));
+ assertFalse(ipPrefix.contains(Ip4Address.valueOf("1.3.0.0")));
+ assertFalse(ipPrefix.contains(Ip4Address.valueOf("0.0.0.0")));
+ assertFalse(ipPrefix.contains(Ip4Address.valueOf("255.255.255.255")));
+
+ ipPrefix = Ip4Prefix.valueOf("1.2.0.0/32");
+ assertTrue(ipPrefix.contains(Ip4Address.valueOf("1.2.0.0")));
+ assertFalse(ipPrefix.contains(Ip4Address.valueOf("1.2.0.4")));
+ assertFalse(ipPrefix.contains(Ip4Address.valueOf("1.3.0.0")));
+ assertFalse(ipPrefix.contains(Ip4Address.valueOf("0.0.0.0")));
+ assertFalse(ipPrefix.contains(Ip4Address.valueOf("255.255.255.255")));
+
+ ipPrefix = Ip4Prefix.valueOf("0.0.0.0/0");
+ assertTrue(ipPrefix.contains(Ip4Address.valueOf("1.2.0.0")));
+ assertTrue(ipPrefix.contains(Ip4Address.valueOf("1.2.0.4")));
+ assertTrue(ipPrefix.contains(Ip4Address.valueOf("1.3.0.0")));
+ assertTrue(ipPrefix.contains(Ip4Address.valueOf("0.0.0.0")));
+ assertTrue(ipPrefix.contains(Ip4Address.valueOf("255.255.255.255")));
+
+ ipPrefix = Ip4Prefix.valueOf("255.255.255.255/32");
+ assertFalse(ipPrefix.contains(Ip4Address.valueOf("1.2.0.0")));
+ assertFalse(ipPrefix.contains(Ip4Address.valueOf("1.2.0.4")));
+ assertFalse(ipPrefix.contains(Ip4Address.valueOf("1.3.0.0")));
+ assertFalse(ipPrefix.contains(Ip4Address.valueOf("0.0.0.0")));
+ assertTrue(ipPrefix.contains(Ip4Address.valueOf("255.255.255.255")));
+ }
+
+ /**
+ * Tests equality of {@link Ip4Prefix} for IPv4.
+ */
+ @Test
+ public void testEqualityIPv4() {
+ new EqualsTester()
+ .addEqualityGroup(Ip4Prefix.valueOf("1.2.0.0/24"),
+ Ip4Prefix.valueOf("1.2.0.0/24"),
+ Ip4Prefix.valueOf("1.2.0.4/24"))
+ .addEqualityGroup(Ip4Prefix.valueOf("1.2.0.0/16"),
+ Ip4Prefix.valueOf("1.2.0.0/16"))
+ .addEqualityGroup(Ip4Prefix.valueOf("1.2.0.0/32"),
+ Ip4Prefix.valueOf("1.2.0.0/32"))
+ .addEqualityGroup(Ip4Prefix.valueOf("1.3.0.0/24"),
+ Ip4Prefix.valueOf("1.3.0.0/24"))
+ .addEqualityGroup(Ip4Prefix.valueOf("0.0.0.0/0"),
+ Ip4Prefix.valueOf("0.0.0.0/0"))
+ .addEqualityGroup(Ip4Prefix.valueOf("255.255.255.255/32"),
+ Ip4Prefix.valueOf("255.255.255.255/32"))
+ .testEquals();
+ }
+
+ /**
+ * Tests object string representation for IPv4.
+ */
+ @Test
+ public void testToStringIPv4() {
+ Ip4Prefix ipPrefix;
+
+ ipPrefix = Ip4Prefix.valueOf("1.2.3.0/24");
+ assertThat(ipPrefix.toString(), is("1.2.3.0/24"));
+
+ ipPrefix = Ip4Prefix.valueOf("1.2.3.4/24");
+ assertThat(ipPrefix.toString(), is("1.2.3.0/24"));
+
+ ipPrefix = Ip4Prefix.valueOf("0.0.0.0/0");
+ assertThat(ipPrefix.toString(), is("0.0.0.0/0"));
+
+ ipPrefix = Ip4Prefix.valueOf("255.255.255.255/32");
+ assertThat(ipPrefix.toString(), is("255.255.255.255/32"));
}
}
diff --git a/utils/misc/src/test/java/org/onlab/packet/Ip6AddressTest.java b/utils/misc/src/test/java/org/onlab/packet/Ip6AddressTest.java
index 9170461..e7d017d 100644
--- a/utils/misc/src/test/java/org/onlab/packet/Ip6AddressTest.java
+++ b/utils/misc/src/test/java/org/onlab/packet/Ip6AddressTest.java
@@ -15,10 +15,13 @@
*/
package org.onlab.packet;
+import com.google.common.net.InetAddresses;
+import com.google.common.testing.EqualsTester;
import org.junit.Test;
+import java.net.InetAddress;
+
import static org.hamcrest.Matchers.is;
-import static org.hamcrest.Matchers.not;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.onlab.junit.ImmutableClassChecker.assertThatClassIsImmutable;
@@ -36,10 +39,18 @@
}
/**
+ * Tests the IPv4 address version constant.
+ */
+ @Test
+ public void testAddressVersion() {
+ assertThat(Ip6Address.VERSION, is(IpAddress.Version.INET6));
+ }
+
+ /**
* Tests the length of the address in bytes (octets).
*/
@Test
- public void testAddrBytelen() {
+ public void testAddrByteLength() {
assertThat(Ip6Address.BYTE_LENGTH, is(16));
}
@@ -47,406 +58,442 @@
* Tests the length of the address in bits.
*/
@Test
- public void testAddrBitlen() {
+ public void testAddrBitLength() {
assertThat(Ip6Address.BIT_LENGTH, is(128));
}
/**
- * Tests default class constructor.
+ * Tests returning the IP address version.
*/
@Test
- public void testDefaultConstructor() {
- Ip6Address ip6Address = new Ip6Address();
- assertThat(ip6Address.toString(), is("::"));
+ public void testVersion() {
+ IpAddress ipAddress;
+
+ // IPv6
+ ipAddress = IpAddress.valueOf("::");
+ assertThat(ipAddress.version(), is(IpAddress.Version.INET6));
}
/**
- * Tests valid class copy constructor.
+ * Tests returning an IPv6 address as a byte array.
*/
@Test
- public void testCopyConstructor() {
- Ip6Address fromAddr =
- new Ip6Address("1111:2222:3333:4444:5555:6666:7777:8888");
- Ip6Address ip6Address = new Ip6Address(fromAddr);
- assertThat(ip6Address.toString(),
+ public void testAddressToOctetsIPv6() {
+ Ip6Address ipAddress;
+ byte[] value;
+
+ value = new byte[] {0x11, 0x11, 0x22, 0x22,
+ 0x33, 0x33, 0x44, 0x44,
+ 0x55, 0x55, 0x66, 0x66,
+ 0x77, 0x77,
+ (byte) 0x88, (byte) 0x88};
+ ipAddress =
+ Ip6Address.valueOf("1111:2222:3333:4444:5555:6666:7777:8888");
+ assertThat(ipAddress.toOctets(), is(value));
+
+ value = new byte[] {0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00};
+ ipAddress = Ip6Address.valueOf("::");
+ assertThat(ipAddress.toOctets(), is(value));
+
+ value = new byte[] {(byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff};
+ ipAddress =
+ Ip6Address.valueOf("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff");
+ assertThat(ipAddress.toOctets(), is(value));
+ }
+
+ /**
+ * Tests valueOf() converter for IPv6 byte array.
+ */
+ @Test
+ public void testValueOfByteArrayIPv6() {
+ Ip6Address ipAddress;
+ byte[] value;
+
+ value = new byte[] {0x11, 0x11, 0x22, 0x22,
+ 0x33, 0x33, 0x44, 0x44,
+ 0x55, 0x55, 0x66, 0x66,
+ 0x77, 0x77,
+ (byte) 0x88, (byte) 0x88};
+ ipAddress = Ip6Address.valueOf(value);
+ assertThat(ipAddress.toString(),
is("1111:2222:3333:4444:5555:6666:7777:8888"));
- fromAddr = new Ip6Address("::");
- ip6Address = new Ip6Address(fromAddr);
- assertThat(ip6Address.toString(), is("::"));
+ value = new byte[] {0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00};
+ ipAddress = Ip6Address.valueOf(value);
+ assertThat(ipAddress.toString(), is("::"));
- fromAddr = new Ip6Address("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff");
- ip6Address = new Ip6Address(fromAddr);
- assertThat(ip6Address.toString(),
+ value = new byte[] {(byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff};
+ ipAddress = Ip6Address.valueOf(value);
+ assertThat(ipAddress.toString(),
is("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"));
}
/**
- * Tests invalid class copy constructor for a null object to copy from.
+ * Tests invalid valueOf() converter for a null array for IPv6.
*/
@Test(expected = NullPointerException.class)
- public void testInvalidConstructorNullObject() {
- Ip6Address fromAddr = null;
- Ip6Address ip6Address = new Ip6Address(fromAddr);
+ public void testInvalidValueOfNullArrayIPv6() {
+ Ip6Address ipAddress;
+ byte[] value;
+
+ value = null;
+ ipAddress = Ip6Address.valueOf(value);
}
/**
- * Tests valid class constructor for integer values.
- */
- @Test
- public void testConstructorForInteger() {
- Ip6Address ip6Address =
- new Ip6Address(0x1111222233334444L, 0x5555666677778888L);
- assertThat(ip6Address.toString(),
- is("1111:2222:3333:4444:5555:6666:7777:8888"));
-
- ip6Address = new Ip6Address(0L, 0L);
- assertThat(ip6Address.toString(), is("::"));
-
- ip6Address = new Ip6Address(-1L, -1L);
- assertThat(ip6Address.toString(),
- is("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"));
- }
-
- /**
- * Tests valid class constructor for an array value.
- */
- @Test
- public void testConstructorForArray() {
- final byte[] value1 = new byte[] {0x11, 0x11, 0x22, 0x22,
- 0x33, 0x33, 0x44, 0x44,
- 0x55, 0x55, 0x66, 0x66,
- 0x77, 0x77,
- (byte) 0x88, (byte) 0x88};
- Ip6Address ip6Address = new Ip6Address(value1);
- assertThat(ip6Address.toString(),
- is("1111:2222:3333:4444:5555:6666:7777:8888"));
-
- final byte[] value2 = new byte[] {0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00};
- ip6Address = new Ip6Address(value2);
- assertThat(ip6Address.toString(), is("::"));
-
- final byte[] value3 = new byte[] {(byte) 0xff, (byte) 0xff,
- (byte) 0xff, (byte) 0xff,
- (byte) 0xff, (byte) 0xff,
- (byte) 0xff, (byte) 0xff,
- (byte) 0xff, (byte) 0xff,
- (byte) 0xff, (byte) 0xff,
- (byte) 0xff, (byte) 0xff,
- (byte) 0xff, (byte) 0xff};
- ip6Address = new Ip6Address(value3);
- assertThat(ip6Address.toString(),
- is("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"));
- }
-
- /**
- * Tests valid class constructor for an array value and an offset.
- */
- @Test
- public void testConstructorForArrayAndOffset() {
- final byte[] value1 = new byte[] {11, 22, 33, // Preamble
- 0x11, 0x11, 0x22, 0x22,
- 0x33, 0x33, 0x44, 0x44,
- 0x55, 0x55, 0x66, 0x66,
- 0x77, 0x77,
- (byte) 0x88, (byte) 0x88,
- 44, 55}; // Extra bytes
- Ip6Address ip6Address = new Ip6Address(value1, 3);
- assertThat(ip6Address.toString(),
- is("1111:2222:3333:4444:5555:6666:7777:8888"));
-
- final byte[] value2 = new byte[] {11, 22, // Preamble
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
- 33}; // Extra bytes
- ip6Address = new Ip6Address(value2, 2);
- assertThat(ip6Address.toString(), is("::"));
-
- final byte[] value3 = new byte[] {11, 22, // Preamble
- (byte) 0xff, (byte) 0xff,
- (byte) 0xff, (byte) 0xff,
- (byte) 0xff, (byte) 0xff,
- (byte) 0xff, (byte) 0xff,
- (byte) 0xff, (byte) 0xff,
- (byte) 0xff, (byte) 0xff,
- (byte) 0xff, (byte) 0xff,
- (byte) 0xff, (byte) 0xff,
- 33}; // Extra bytes
- ip6Address = new Ip6Address(value3, 2);
- assertThat(ip6Address.toString(),
- is("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"));
- }
-
- /**
- * Tests invalid class constructor for a null array.
- */
- @Test(expected = NullPointerException.class)
- public void testInvalidConstructorNullArray() {
- final byte[] fromArray = null;
- Ip6Address ip6Address = new Ip6Address(fromArray);
- }
-
- /**
- * Tests invalid class constructor for an array that is too short.
+ * Tests invalid valueOf() converger for an array that is too short for
+ * IPv6.
*/
@Test(expected = IllegalArgumentException.class)
- public void testInvalidConstructorShortArray() {
- final byte[] fromArray = new byte[] {1, 2, 3, 4, 5, 6, 7, 8, 9};
- Ip6Address ip6Address = new Ip6Address(fromArray);
+ public void testInvalidValueOfShortArrayIPv6() {
+ Ip6Address ipAddress;
+ byte[] value;
+
+ value = new byte[] {1, 2, 3, 4, 5, 6, 7, 8, 9};
+ ipAddress = Ip6Address.valueOf(value);
}
/**
- * Tests invalid class constructor for an array and an invalid offset.
- */
- @Test(expected = IllegalArgumentException.class)
- public void testInvalidConstructorArrayInvalidOffset() {
- final byte[] value1 = new byte[] {11, 22, 33, // Preamble
- 0x11, 0x11, 0x22, 0x22,
- 0x33, 0x33, 0x44, 0x44,
- 0x55, 0x55, 0x66, 0x66,
- 0x77, 0x77,
- (byte) 0x88, (byte) 0x88,
- 44, 55}; // Extra bytes
- Ip6Address ip6Address = new Ip6Address(value1, 6);
- }
-
- /**
- * Tests valid class constructor for a string.
+ * Tests valueOf() converter for IPv6 byte array and an offset.
*/
@Test
- public void testConstructorForString() {
- Ip6Address ip6Address =
- new Ip6Address("1111:2222:3333:4444:5555:6666:7777:8888");
- assertThat(ip6Address.toString(),
+ public void testValueOfByteArrayOffsetIPv6() {
+ Ip6Address ipAddress;
+ byte[] value;
+
+ value = new byte[] {11, 22, 33, // Preamble
+ 0x11, 0x11, 0x22, 0x22,
+ 0x33, 0x33, 0x44, 0x44,
+ 0x55, 0x55, 0x66, 0x66,
+ 0x77, 0x77,
+ (byte) 0x88, (byte) 0x88,
+ 44, 55}; // Extra bytes
+ ipAddress = Ip6Address.valueOf(value, 3);
+ assertThat(ipAddress.toString(),
is("1111:2222:3333:4444:5555:6666:7777:8888"));
- ip6Address = new Ip6Address("::");
- assertThat(ip6Address.toString(), is("::"));
+ value = new byte[] {11, 22, // Preamble
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 33}; // Extra bytes
+ ipAddress = Ip6Address.valueOf(value, 2);
+ assertThat(ipAddress.toString(), is("::"));
- ip6Address = new Ip6Address("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff");
- assertThat(ip6Address.toString(),
+ value = new byte[] {11, 22, // Preamble
+ (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff,
+ 33}; // Extra bytes
+ ipAddress = Ip6Address.valueOf(value, 2);
+ assertThat(ipAddress.toString(),
is("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"));
}
/**
- * Tests invalid class constructor for a null string.
+ * Tests invalid valueOf() converger for an array and an invalid offset
+ * for IPv6.
+ */
+ @Test(expected = IllegalArgumentException.class)
+ public void testInvalidValueOfArrayInvalidOffsetIPv6() {
+ Ip6Address ipAddress;
+ byte[] value;
+
+ value = new byte[] {11, 22, 33, // Preamble
+ 0x11, 0x11, 0x22, 0x22,
+ 0x33, 0x33, 0x44, 0x44,
+ 0x55, 0x55, 0x66, 0x66,
+ 0x77, 0x77,
+ (byte) 0x88, (byte) 0x88,
+ 44, 55}; // Extra bytes
+ ipAddress = Ip6Address.valueOf(value, 6);
+ }
+
+ /**
+ * Tests valueOf() converter for IPv6 InetAddress.
+ */
+ @Test
+ public void testValueOfInetAddressIPv6() {
+ Ip6Address ipAddress;
+ InetAddress inetAddress;
+
+ inetAddress =
+ InetAddresses.forString("1111:2222:3333:4444:5555:6666:7777:8888");
+ ipAddress = Ip6Address.valueOf(inetAddress);
+ assertThat(ipAddress.toString(),
+ is("1111:2222:3333:4444:5555:6666:7777:8888"));
+
+ inetAddress = InetAddresses.forString("::");
+ ipAddress = Ip6Address.valueOf(inetAddress);
+ assertThat(ipAddress.toString(), is("::"));
+
+ inetAddress =
+ InetAddresses.forString("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff");
+ ipAddress = Ip6Address.valueOf(inetAddress);
+ assertThat(ipAddress.toString(),
+ is("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"));
+ }
+
+ /**
+ * Tests valueOf() converter for IPv6 string.
+ */
+ @Test
+ public void testValueOfStringIPv6() {
+ Ip6Address ipAddress;
+
+ ipAddress =
+ Ip6Address.valueOf("1111:2222:3333:4444:5555:6666:7777:8888");
+ assertThat(ipAddress.toString(),
+ is("1111:2222:3333:4444:5555:6666:7777:8888"));
+
+ ipAddress = Ip6Address.valueOf("::");
+ assertThat(ipAddress.toString(), is("::"));
+
+ ipAddress =
+ Ip6Address.valueOf("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff");
+ assertThat(ipAddress.toString(),
+ is("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"));
+ }
+
+ /**
+ * Tests invalid valueOf() converter for a null string.
*/
@Test(expected = NullPointerException.class)
- public void testInvalidConstructorNullString() {
+ public void testInvalidValueOfNullString() {
+ Ip6Address ipAddress;
+
String fromString = null;
- Ip6Address ip6Address = new Ip6Address(fromString);
+ ipAddress = Ip6Address.valueOf(fromString);
}
/**
- * Tests invalid class constructor for an empty string.
+ * Tests invalid valueOf() converter for an empty string.
*/
@Test(expected = IllegalArgumentException.class)
- public void testInvalidConstructors() {
- // Check constructor for invalid ID: empty string
- Ip6Address ip6Address = new Ip6Address("");
+ public void testInvalidValueOfEmptyString() {
+ Ip6Address ipAddress;
+
+ String fromString = "";
+ ipAddress = Ip6Address.valueOf(fromString);
}
/**
- * Tests returning the address as a byte array.
+ * Tests invalid valueOf() converter for an incorrect string.
*/
- @Test
- public void testAddressToOctets() {
- final byte[] value1 = new byte[] {0x11, 0x11, 0x22, 0x22,
- 0x33, 0x33, 0x44, 0x44,
- 0x55, 0x55, 0x66, 0x66,
- 0x77, 0x77,
- (byte) 0x88, (byte) 0x88};
- Ip6Address ip6Address =
- new Ip6Address("1111:2222:3333:4444:5555:6666:7777:8888");
- assertThat(ip6Address.toOctets(), is(value1));
+ @Test(expected = IllegalArgumentException.class)
+ public void testInvalidValueOfIncorrectString() {
+ Ip6Address ipAddress;
- final byte[] value2 = new byte[] {0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00};
- ip6Address = new Ip6Address("::");
- assertThat(ip6Address.toOctets(), is(value2));
-
- final byte[] value3 = new byte[] {(byte) 0xff, (byte) 0xff,
- (byte) 0xff, (byte) 0xff,
- (byte) 0xff, (byte) 0xff,
- (byte) 0xff, (byte) 0xff,
- (byte) 0xff, (byte) 0xff,
- (byte) 0xff, (byte) 0xff,
- (byte) 0xff, (byte) 0xff,
- (byte) 0xff, (byte) 0xff};
- ip6Address = new Ip6Address("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff");
- assertThat(ip6Address.toOctets(), is(value3));
+ String fromString = "NoSuchIpAddress";
+ ipAddress = Ip6Address.valueOf(fromString);
}
/**
- * Tests making a mask prefix for a given prefix length.
+ * Tests making a mask prefix for a given prefix length for IPv6.
*/
@Test
- public void testMakeMaskPrefix() {
- Ip6Address ip6Address = Ip6Address.makeMaskPrefix(8);
- assertThat(ip6Address.toString(), is("ff00::"));
+ public void testMakeMaskPrefixIPv6() {
+ Ip6Address ipAddress;
- ip6Address = Ip6Address.makeMaskPrefix(120);
- assertThat(ip6Address.toString(),
+ ipAddress = Ip6Address.makeMaskPrefix(8);
+ assertThat(ipAddress.toString(), is("ff00::"));
+
+ ipAddress = Ip6Address.makeMaskPrefix(120);
+ assertThat(ipAddress.toString(),
is("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ff00"));
- ip6Address = Ip6Address.makeMaskPrefix(0);
- assertThat(ip6Address.toString(), is("::"));
+ ipAddress = Ip6Address.makeMaskPrefix(0);
+ assertThat(ipAddress.toString(), is("::"));
- ip6Address = Ip6Address.makeMaskPrefix(128);
- assertThat(ip6Address.toString(),
+ ipAddress = Ip6Address.makeMaskPrefix(128);
+ assertThat(ipAddress.toString(),
is("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"));
- ip6Address = Ip6Address.makeMaskPrefix(64);
- assertThat(ip6Address.toString(), is("ffff:ffff:ffff:ffff::"));
+ ipAddress = Ip6Address.makeMaskPrefix(64);
+ assertThat(ipAddress.toString(), is("ffff:ffff:ffff:ffff::"));
}
/**
- * Tests making of a masked address.
+ * Tests making a mask prefix for an invalid prefix length for IPv6:
+ * negative prefix length.
+ */
+ @Test(expected = IllegalArgumentException.class)
+ public void testInvalidMakeNegativeMaskPrefixIPv6() {
+ Ip6Address ipAddress;
+
+ ipAddress = Ip6Address.makeMaskPrefix(-1);
+ }
+
+ /**
+ * Tests making a mask prefix for an invalid prefix length for IPv6:
+ * too long prefix length.
+ */
+ @Test(expected = IllegalArgumentException.class)
+ public void testInvalidMakeTooLongMaskPrefixIPv6() {
+ Ip6Address ipAddress;
+
+ ipAddress = Ip6Address.makeMaskPrefix(129);
+ }
+
+ /**
+ * Tests making of a masked address for IPv6.
*/
@Test
- public void testMakeMaskedAddress() {
- Ip6Address ip6Address =
- new Ip6Address("1111:2222:3333:4444:5555:6666:7777:8885");
- Ip6Address ip6AddressMasked =
- Ip6Address.makeMaskedAddress(ip6Address, 8);
- assertThat(ip6AddressMasked.toString(), is("1100::"));
+ public void testMakeMaskedAddressIPv6() {
+ Ip6Address ipAddress =
+ Ip6Address.valueOf("1111:2222:3333:4444:5555:6666:7777:8885");
+ Ip6Address ipAddressMasked;
- ip6AddressMasked = Ip6Address.makeMaskedAddress(ip6Address, 120);
- assertThat(ip6AddressMasked.toString(),
+ ipAddressMasked = Ip6Address.makeMaskedAddress(ipAddress, 8);
+ assertThat(ipAddressMasked.toString(), is("1100::"));
+
+ ipAddressMasked = Ip6Address.makeMaskedAddress(ipAddress, 120);
+ assertThat(ipAddressMasked.toString(),
is("1111:2222:3333:4444:5555:6666:7777:8800"));
- ip6AddressMasked = Ip6Address.makeMaskedAddress(ip6Address, 0);
- assertThat(ip6AddressMasked.toString(), is("::"));
+ ipAddressMasked = Ip6Address.makeMaskedAddress(ipAddress, 0);
+ assertThat(ipAddressMasked.toString(), is("::"));
- ip6AddressMasked = Ip6Address.makeMaskedAddress(ip6Address, 128);
- assertThat(ip6AddressMasked.toString(),
+ ipAddressMasked = Ip6Address.makeMaskedAddress(ipAddress, 128);
+ assertThat(ipAddressMasked.toString(),
is("1111:2222:3333:4444:5555:6666:7777:8885"));
- ip6AddressMasked = Ip6Address.makeMaskedAddress(ip6Address, 64);
- assertThat(ip6AddressMasked.toString(), is("1111:2222:3333:4444::"));
+ ipAddressMasked = Ip6Address.makeMaskedAddress(ipAddress, 64);
+ assertThat(ipAddressMasked.toString(), is("1111:2222:3333:4444::"));
}
/**
- * Tests getting the value of an address.
+ * Tests making of a masked address for invalid prefix length for IPv6:
+ * negative prefix length.
*/
- @Test
- public void testGetValue() {
- Ip6Address ip6Address =
- new Ip6Address("1111:2222:3333:4444:5555:6666:7777:8888");
- assertThat(ip6Address.getValueHigh(), is(0x1111222233334444L));
- assertThat(ip6Address.getValueLow(), is(0x5555666677778888L));
+ @Test(expected = IllegalArgumentException.class)
+ public void testInvalidMakeNegativeMaskedAddressIPv6() {
+ Ip6Address ipAddress =
+ Ip6Address.valueOf("1111:2222:3333:4444:5555:6666:7777:8885");
+ Ip6Address ipAddressMasked;
- ip6Address = new Ip6Address(0, 0);
- assertThat(ip6Address.getValueHigh(), is(0L));
- assertThat(ip6Address.getValueLow(), is(0L));
-
- ip6Address = new Ip6Address(-1L, -1L);
- assertThat(ip6Address.getValueHigh(), is(-1L));
- assertThat(ip6Address.getValueLow(), is(-1L));
+ ipAddressMasked = Ip6Address.makeMaskedAddress(ipAddress, -1);
}
/**
- * Tests equality of {@link Ip6Address}.
+ * Tests making of a masked address for an invalid prefix length for IPv6:
+ * too long prefix length.
*/
- @Test
- public void testEquality() {
- Ip6Address addr1 =
- new Ip6Address("1111:2222:3333:4444:5555:6666:7777:8888");
- Ip6Address addr2 =
- new Ip6Address("1111:2222:3333:4444:5555:6666:7777:8888");
- assertThat(addr1, is(addr2));
+ @Test(expected = IllegalArgumentException.class)
+ public void testInvalidMakeTooLongMaskedAddressIPv6() {
+ Ip6Address ipAddress =
+ Ip6Address.valueOf("1111:2222:3333:4444:5555:6666:7777:8885");
+ Ip6Address ipAddressMasked;
- addr1 = new Ip6Address("::");
- addr2 = new Ip6Address("::");
- assertThat(addr1, is(addr2));
-
- addr1 = new Ip6Address("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff");
- addr2 = new Ip6Address("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff");
- assertThat(addr1, is(addr2));
+ ipAddressMasked = Ip6Address.makeMaskedAddress(ipAddress, 129);
}
/**
- * Tests non-equality of {@link Ip6Address}.
+ * Tests comparison of {@link Ip6Address} for IPv6.
*/
@Test
- public void testNonEquality() {
- Ip6Address addr1 =
- new Ip6Address("1111:2222:3333:4444:5555:6666:7777:8888");
- Ip6Address addr2 =
- new Ip6Address("1111:2222:3333:4444:5555:6666:7777:888A");
- Ip6Address addr3 = new Ip6Address("::");
- Ip6Address addr4 =
- new Ip6Address("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff");
- assertThat(addr1, is(not(addr2)));
- assertThat(addr3, is(not(addr2)));
- assertThat(addr4, is(not(addr2)));
- }
+ public void testComparisonIPv6() {
+ Ip6Address addr1, addr2, addr3, addr4;
- /**
- * Tests comparison of {@link Ip6Address}.
- */
- @Test
- public void testComparison() {
- Ip6Address addr1 =
- new Ip6Address("1111:2222:3333:4444:5555:6666:7777:8888");
- Ip6Address addr2 =
- new Ip6Address("1111:2222:3333:4444:5555:6666:7777:8888");
- Ip6Address addr3 =
- new Ip6Address("1111:2222:3333:4444:5555:6666:7777:8887");
- Ip6Address addr4 =
- new Ip6Address("1111:2222:3333:4444:5555:6666:7777:8889");
+ addr1 = Ip6Address.valueOf("1111:2222:3333:4444:5555:6666:7777:8888");
+ addr2 = Ip6Address.valueOf("1111:2222:3333:4444:5555:6666:7777:8888");
+ addr3 = Ip6Address.valueOf("1111:2222:3333:4444:5555:6666:7777:8887");
+ addr4 = Ip6Address.valueOf("1111:2222:3333:4444:5555:6666:7777:8889");
assertTrue(addr1.compareTo(addr2) == 0);
assertTrue(addr1.compareTo(addr3) > 0);
assertTrue(addr1.compareTo(addr4) < 0);
- addr1 = new Ip6Address("ffff:2222:3333:4444:5555:6666:7777:8888");
- addr2 = new Ip6Address("ffff:2222:3333:4444:5555:6666:7777:8888");
- addr3 = new Ip6Address("ffff:2222:3333:4444:5555:6666:7777:8887");
- addr4 = new Ip6Address("ffff:2222:3333:4444:5555:6666:7777:8889");
+ addr1 = Ip6Address.valueOf("ffff:2222:3333:4444:5555:6666:7777:8888");
+ addr2 = Ip6Address.valueOf("ffff:2222:3333:4444:5555:6666:7777:8888");
+ addr3 = Ip6Address.valueOf("ffff:2222:3333:4444:5555:6666:7777:8887");
+ addr4 = Ip6Address.valueOf("ffff:2222:3333:4444:5555:6666:7777:8889");
assertTrue(addr1.compareTo(addr2) == 0);
assertTrue(addr1.compareTo(addr3) > 0);
assertTrue(addr1.compareTo(addr4) < 0);
- addr1 = new Ip6Address("ffff:2222:3333:4444:5555:6666:7777:8888");
- addr2 = new Ip6Address("ffff:2222:3333:4444:5555:6666:7777:8888");
- addr3 = new Ip6Address("ffff:2222:3333:4443:5555:6666:7777:8888");
- addr4 = new Ip6Address("ffff:2222:3333:4445:5555:6666:7777:8888");
+ addr1 = Ip6Address.valueOf("ffff:2222:3333:4444:5555:6666:7777:8888");
+ addr2 = Ip6Address.valueOf("ffff:2222:3333:4444:5555:6666:7777:8888");
+ addr3 = Ip6Address.valueOf("ffff:2222:3333:4443:5555:6666:7777:8888");
+ addr4 = Ip6Address.valueOf("ffff:2222:3333:4445:5555:6666:7777:8888");
assertTrue(addr1.compareTo(addr2) == 0);
assertTrue(addr1.compareTo(addr3) > 0);
assertTrue(addr1.compareTo(addr4) < 0);
}
/**
- * Tests object string representation.
+ * Tests equality of {@link Ip6Address} for IPv6.
*/
@Test
- public void testToString() {
- Ip6Address ip6Address =
- new Ip6Address("1111:2222:3333:4444:5555:6666:7777:8888");
- assertThat(ip6Address.toString(),
+ public void testEqualityIPv6() {
+ new EqualsTester()
+ .addEqualityGroup(
+ Ip6Address.valueOf("1111:2222:3333:4444:5555:6666:7777:8888"),
+ Ip6Address.valueOf("1111:2222:3333:4444:5555:6666:7777:8888"))
+ .addEqualityGroup(
+ Ip6Address.valueOf("1111:2222:3333:4444:5555:6666:7777:888a"),
+ Ip6Address.valueOf("1111:2222:3333:4444:5555:6666:7777:888a"))
+ .addEqualityGroup(
+ Ip6Address.valueOf("::"),
+ Ip6Address.valueOf("::"))
+ .addEqualityGroup(
+ Ip6Address.valueOf("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"),
+ Ip6Address.valueOf("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"))
+ .testEquals();
+ }
+
+ /**
+ * Tests object string representation for IPv6.
+ */
+ @Test
+ public void testToStringIPv6() {
+ Ip6Address ipAddress;
+
+ ipAddress =
+ Ip6Address.valueOf("1111:2222:3333:4444:5555:6666:7777:8888");
+ assertThat(ipAddress.toString(),
is("1111:2222:3333:4444:5555:6666:7777:8888"));
- ip6Address = new Ip6Address("1111::8888");
- assertThat(ip6Address.toString(), is("1111::8888"));
+ ipAddress = Ip6Address.valueOf("1111::8888");
+ assertThat(ipAddress.toString(), is("1111::8888"));
- ip6Address = new Ip6Address("1111::");
- assertThat(ip6Address.toString(), is("1111::"));
+ ipAddress = Ip6Address.valueOf("1111::");
+ assertThat(ipAddress.toString(), is("1111::"));
- ip6Address = new Ip6Address("::8888");
- assertThat(ip6Address.toString(), is("::8888"));
+ ipAddress = Ip6Address.valueOf("::8888");
+ assertThat(ipAddress.toString(), is("::8888"));
- ip6Address = new Ip6Address("::");
- assertThat(ip6Address.toString(), is("::"));
+ ipAddress = Ip6Address.valueOf("::");
+ assertThat(ipAddress.toString(), is("::"));
- ip6Address = new Ip6Address("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff");
- assertThat(ip6Address.toString(),
+ ipAddress =
+ Ip6Address.valueOf("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff");
+ assertThat(ipAddress.toString(),
is("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"));
}
}
diff --git a/utils/misc/src/test/java/org/onlab/packet/Ip6PrefixTest.java b/utils/misc/src/test/java/org/onlab/packet/Ip6PrefixTest.java
index 59b19f6..dceeb84 100644
--- a/utils/misc/src/test/java/org/onlab/packet/Ip6PrefixTest.java
+++ b/utils/misc/src/test/java/org/onlab/packet/Ip6PrefixTest.java
@@ -15,12 +15,14 @@
*/
package org.onlab.packet;
+import com.google.common.testing.EqualsTester;
import org.junit.Test;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;
-import static org.hamcrest.Matchers.not;
import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
import static org.onlab.junit.ImmutableClassChecker.assertThatClassIsImmutable;
/**
@@ -36,235 +38,531 @@
}
/**
- * Tests default class constructor.
+ * Tests the IPv4 prefix address version constant.
*/
@Test
- public void testDefaultConstructor() {
- Ip6Prefix ip6prefix = new Ip6Prefix();
- assertThat(ip6prefix.toString(), is("::/0"));
+ public void testAddressVersion() {
+ assertThat(Ip6Prefix.VERSION, is(IpAddress.Version.INET6));
}
/**
- * Tests valid class copy constructor.
+ * Tests the maximum mask length.
*/
@Test
- public void testCopyConstructor() {
- Ip6Prefix fromAddr = new Ip6Prefix("1100::/8");
- Ip6Prefix ip6prefix = new Ip6Prefix(fromAddr);
- assertThat(ip6prefix.toString(), is("1100::/8"));
-
- fromAddr = new Ip6Prefix("::/0");
- ip6prefix = new Ip6Prefix(fromAddr);
- assertThat(ip6prefix.toString(), is("::/0"));
-
- fromAddr =
- new Ip6Prefix("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128");
- ip6prefix = new Ip6Prefix(fromAddr);
- assertThat(ip6prefix.toString(),
- is("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128"));
+ public void testMaxMaskLength() {
+ assertThat(Ip6Prefix.MAX_MASK_LENGTH, is(128));
}
/**
- * Tests invalid class copy constructor for a null object to copy from.
+ * Tests returning the IP version of the prefix.
*/
- @Test(expected = NullPointerException.class)
- public void testInvalidConstructorNullObject() {
- Ip6Prefix fromAddr = null;
- Ip6Prefix ip6prefix = new Ip6Prefix(fromAddr);
+ @Test
+ public void testVersion() {
+ Ip6Prefix ipPrefix;
+
+ // IPv6
+ ipPrefix = Ip6Prefix.valueOf("::/0");
+ assertThat(ipPrefix.version(), is(IpAddress.Version.INET6));
}
/**
- * Tests valid class constructor for an address and prefix length.
+ * Tests returning the IP address value and IP address prefix length of
+ * an IPv6 prefix.
*/
@Test
- public void testConstructorForAddressAndPrefixLength() {
- Ip6Prefix ip6prefix =
- new Ip6Prefix(new Ip6Address("1100::"), (short) 8);
- assertThat(ip6prefix.toString(), is("1100::/8"));
+ public void testAddressAndPrefixLengthIPv6() {
+ Ip6Prefix ipPrefix;
- ip6prefix =
- new Ip6Prefix(new Ip6Address("1111:2222:3333:4444:5555:6666:7777:8885"),
- (short) 8);
- assertThat(ip6prefix.toString(), is("1100::/8"));
+ ipPrefix = Ip6Prefix.valueOf("1100::/8");
+ assertThat(ipPrefix.address(), equalTo(Ip6Address.valueOf("1100::")));
+ assertThat(ipPrefix.prefixLength(), is(8));
- ip6prefix =
- new Ip6Prefix(new Ip6Address("1111:2222:3333:4444:5555:6666:7777:8800"),
- (short) 120);
- assertThat(ip6prefix.toString(),
+ ipPrefix =
+ Ip6Prefix.valueOf("1111:2222:3333:4444:5555:6666:7777:8885/8");
+ assertThat(ipPrefix.address(), equalTo(Ip6Address.valueOf("1100::")));
+ assertThat(ipPrefix.prefixLength(), is(8));
+
+ ipPrefix =
+ Ip6Prefix.valueOf("1111:2222:3333:4444:5555:6666:7777:8800/120");
+ assertThat(ipPrefix.address(),
+ equalTo(Ip6Address.valueOf("1111:2222:3333:4444:5555:6666:7777:8800")));
+ assertThat(ipPrefix.prefixLength(), is(120));
+
+ ipPrefix =
+ Ip6Prefix.valueOf("1111:2222:3333:4444:5555:6666:7777:8885/128");
+ assertThat(ipPrefix.address(),
+ equalTo(Ip6Address.valueOf("1111:2222:3333:4444:5555:6666:7777:8885")));
+ assertThat(ipPrefix.prefixLength(), is(128));
+
+ ipPrefix = Ip6Prefix.valueOf("::/0");
+ assertThat(ipPrefix.address(), equalTo(Ip6Address.valueOf("::")));
+ assertThat(ipPrefix.prefixLength(), is(0));
+
+ ipPrefix =
+ Ip6Prefix.valueOf("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128");
+ assertThat(ipPrefix.address(),
+ equalTo(Ip6Address.valueOf("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff")));
+ assertThat(ipPrefix.prefixLength(), is(128));
+
+ ipPrefix =
+ Ip6Prefix.valueOf("1111:2222:3333:4444:5555:6666:7777:8885/64");
+ assertThat(ipPrefix.address(),
+ equalTo(Ip6Address.valueOf("1111:2222:3333:4444::")));
+ assertThat(ipPrefix.prefixLength(), is(64));
+ }
+
+ /**
+ * Tests valueOf() converter for IPv6 byte array.
+ */
+ @Test
+ public void testValueOfByteArrayIPv6() {
+ Ip6Prefix ipPrefix;
+ byte[] value;
+
+ value = new byte[] {0x11, 0x11, 0x22, 0x22,
+ 0x33, 0x33, 0x44, 0x44,
+ 0x55, 0x55, 0x66, 0x66,
+ 0x77, 0x77, (byte) 0x88, (byte) 0x88};
+ ipPrefix = Ip6Prefix.valueOf(value, 120);
+ assertThat(ipPrefix.toString(),
is("1111:2222:3333:4444:5555:6666:7777:8800/120"));
- ip6prefix = new Ip6Prefix(new Ip6Address("::"), (short) 0);
- assertThat(ip6prefix.toString(), is("::/0"));
+ ipPrefix = Ip6Prefix.valueOf(value, 128);
+ assertThat(ipPrefix.toString(),
+ is("1111:2222:3333:4444:5555:6666:7777:8888/128"));
- ip6prefix =
- new Ip6Prefix(new Ip6Address("1111:2222:3333:4444:5555:6666:7777:8885"),
- (short) 128);
- assertThat(ip6prefix.toString(),
- is("1111:2222:3333:4444:5555:6666:7777:8885/128"));
+ value = new byte[] {0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00};
+ ipPrefix = Ip6Prefix.valueOf(value, 0);
+ assertThat(ipPrefix.toString(), is("::/0"));
- ip6prefix =
- new Ip6Prefix(new Ip6Address("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"),
- (short) 128);
- assertThat(ip6prefix.toString(),
+ ipPrefix = Ip6Prefix.valueOf(value, 128);
+ assertThat(ipPrefix.toString(), is("::/128"));
+
+ value = new byte[] {(byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff};
+ ipPrefix = Ip6Prefix.valueOf(value, 0);
+ assertThat(ipPrefix.toString(), is("::/0"));
+
+ ipPrefix = Ip6Prefix.valueOf(value, 64);
+ assertThat(ipPrefix.toString(), is("ffff:ffff:ffff:ffff::/64"));
+
+ ipPrefix = Ip6Prefix.valueOf(value, 128);
+ assertThat(ipPrefix.toString(),
is("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128"));
-
- ip6prefix =
- new Ip6Prefix(new Ip6Address("1111:2222:3333:4444:5555:6666:7777:8885"),
- (short) 64);
- assertThat(ip6prefix.toString(), is("1111:2222:3333:4444::/64"));
}
/**
- * Tests valid class constructor for a string.
- */
- @Test
- public void testConstructorForString() {
- Ip6Prefix ip6prefix = new Ip6Prefix("1100::/8");
- assertThat(ip6prefix.toString(), is("1100::/8"));
-
- ip6prefix = new Ip6Prefix("1111:2222:3333:4444:5555:6666:7777:8885/8");
- assertThat(ip6prefix.toString(), is("1100::/8"));
-
- ip6prefix =
- new Ip6Prefix("1111:2222:3333:4444:5555:6666:7777:8800/120");
- assertThat(ip6prefix.toString(),
- is("1111:2222:3333:4444:5555:6666:7777:8800/120"));
-
- ip6prefix = new Ip6Prefix("::/0");
- assertThat(ip6prefix.toString(), is("::/0"));
-
- ip6prefix =
- new Ip6Prefix("1111:2222:3333:4444:5555:6666:7777:8885/128");
- assertThat(ip6prefix.toString(),
- is("1111:2222:3333:4444:5555:6666:7777:8885/128"));
-
- ip6prefix = new Ip6Prefix("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128");
- assertThat(ip6prefix.toString(),
- is("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128"));
-
- ip6prefix =
- new Ip6Prefix("1111:2222:3333:4444:5555:6666:7777:8885/64");
- assertThat(ip6prefix.toString(), is("1111:2222:3333:4444::/64"));
- }
-
- /**
- * Tests invalid class constructor for a null string.
+ * Tests invalid valueOf() converter for a null array for IPv6.
*/
@Test(expected = NullPointerException.class)
- public void testInvalidConstructorNullString() {
- String fromString = null;
- Ip6Prefix ip6prefix = new Ip6Prefix(fromString);
+ public void testInvalidValueOfNullArrayIPv6() {
+ Ip6Prefix ipPrefix;
+ byte[] value;
+
+ value = null;
+ ipPrefix = Ip6Prefix.valueOf(value, 120);
}
/**
- * Tests invalid class constructor for an empty string.
+ * Tests invalid valueOf() converter for a short array for IPv6.
*/
@Test(expected = IllegalArgumentException.class)
- public void testInvalidConstructors() {
- // Check constructor for invalid ID: empty string
- Ip6Prefix ip6prefix = new Ip6Prefix("");
+ public void testInvalidValueOfShortArrayIPv6() {
+ Ip6Prefix ipPrefix;
+ byte[] value;
+
+ value = new byte[] {1, 2, 3, 4, 5, 6, 7, 8, 9};
+ ipPrefix = Ip6Prefix.valueOf(value, 120);
}
/**
- * Tests getting the value of an address.
+ * Tests invalid valueOf() converter for IPv6 byte array and
+ * negative prefix length.
*/
- @Test
- public void testGetValue() {
- Ip6Prefix ip6prefix = new Ip6Prefix("1100::/8");
- assertThat(ip6prefix.getAddress(), equalTo(new Ip6Address("1100::")));
- assertThat(ip6prefix.getPrefixLen(), is((short) 8));
+ @Test(expected = IllegalArgumentException.class)
+ public void testInvalidValueOfByteArrayNegativePrefixLengthIPv6() {
+ Ip6Prefix ipPrefix;
+ byte[] value;
- ip6prefix = new Ip6Prefix("1111:2222:3333:4444:5555:6666:7777:8885/8");
- assertThat(ip6prefix.getAddress(), equalTo(new Ip6Address("1100::")));
- assertThat(ip6prefix.getPrefixLen(), is((short) 8));
-
- ip6prefix =
- new Ip6Prefix("1111:2222:3333:4444:5555:6666:7777:8800/120");
- assertThat(ip6prefix.getAddress(),
- equalTo(new Ip6Address("1111:2222:3333:4444:5555:6666:7777:8800")));
- assertThat(ip6prefix.getPrefixLen(), is((short) 120));
-
- ip6prefix = new Ip6Prefix("::/0");
- assertThat(ip6prefix.getAddress(), equalTo(new Ip6Address("::")));
- assertThat(ip6prefix.getPrefixLen(), is((short) 0));
-
- ip6prefix =
- new Ip6Prefix("1111:2222:3333:4444:5555:6666:7777:8885/128");
- assertThat(ip6prefix.getAddress(),
- equalTo(new Ip6Address("1111:2222:3333:4444:5555:6666:7777:8885")));
- assertThat(ip6prefix.getPrefixLen(), is((short) 128));
-
- ip6prefix =
- new Ip6Prefix("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128");
- assertThat(ip6prefix.getAddress(),
- equalTo(new Ip6Address("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff")));
- assertThat(ip6prefix.getPrefixLen(), is((short) 128));
-
- ip6prefix =
- new Ip6Prefix("1111:2222:3333:4444:5555:6666:7777:8885/64");
- assertThat(ip6prefix.getAddress(),
- equalTo(new Ip6Address("1111:2222:3333:4444::")));
- assertThat(ip6prefix.getPrefixLen(), is((short) 64));
+ value = new byte[] {0x11, 0x11, 0x22, 0x22,
+ 0x33, 0x33, 0x44, 0x44,
+ 0x55, 0x55, 0x66, 0x66,
+ 0x77, 0x77, (byte) 0x88, (byte) 0x88};
+ ipPrefix = Ip6Prefix.valueOf(value, -1);
}
/**
- * Tests equality of {@link Ip6Address}.
+ * Tests invalid valueOf() converter for IPv6 byte array and
+ * too long prefix length.
*/
- @Test
- public void testEquality() {
- Ip6Prefix addr1net = new Ip6Prefix("1100::/8");
- Ip6Prefix addr2net = new Ip6Prefix("1100::/8");
- assertThat(addr1net, is(addr2net));
+ @Test(expected = IllegalArgumentException.class)
+ public void testInvalidValueOfByteArrayTooLongPrefixLengthIPv6() {
+ Ip6Prefix ipPrefix;
+ byte[] value;
- addr1net = new Ip6Prefix("1111:2222:3333:4444:5555:6666:7777:8885/8");
- addr2net = new Ip6Prefix("1100::/8");
- assertThat(addr1net, is(addr2net));
-
- addr1net = new Ip6Prefix("::/0");
- addr2net = new Ip6Prefix("::/0");
- assertThat(addr1net, is(addr2net));
-
- addr1net =
- new Ip6Prefix("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128");
- addr2net =
- new Ip6Prefix("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128");
- assertThat(addr1net, is(addr2net));
+ value = new byte[] {0x11, 0x11, 0x22, 0x22,
+ 0x33, 0x33, 0x44, 0x44,
+ 0x55, 0x55, 0x66, 0x66,
+ 0x77, 0x77, (byte) 0x88, (byte) 0x88};
+ ipPrefix = Ip6Prefix.valueOf(value, 129);
}
/**
- * Tests non-equality of {@link Ip6Address}.
+ * Tests valueOf() converter for IPv6 address.
*/
@Test
- public void testNonEquality() {
- Ip6Prefix addr1net = new Ip6Prefix("1100::/8");
- Ip6Prefix addr2net = new Ip6Prefix("1200::/8");
- Ip6Prefix addr3net = new Ip6Prefix("1200::/12");
- Ip6Prefix addr4net = new Ip6Prefix("::/0");
- Ip6Prefix addr5net =
- new Ip6Prefix("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128");
- assertThat(addr1net, is(not(addr2net)));
- assertThat(addr3net, is(not(addr2net)));
- assertThat(addr4net, is(not(addr2net)));
- assertThat(addr5net, is(not(addr2net)));
+ public void testValueOfAddressIPv6() {
+ Ip6Address ipAddress;
+ Ip6Prefix ipPrefix;
+
+ ipAddress =
+ Ip6Address.valueOf("1111:2222:3333:4444:5555:6666:7777:8888");
+ ipPrefix = Ip6Prefix.valueOf(ipAddress, 120);
+ assertThat(ipPrefix.toString(),
+ is("1111:2222:3333:4444:5555:6666:7777:8800/120"));
+
+ ipPrefix = Ip6Prefix.valueOf(ipAddress, 128);
+ assertThat(ipPrefix.toString(),
+ is("1111:2222:3333:4444:5555:6666:7777:8888/128"));
+
+ ipAddress = Ip6Address.valueOf("::");
+ ipPrefix = Ip6Prefix.valueOf(ipAddress, 0);
+ assertThat(ipPrefix.toString(), is("::/0"));
+
+ ipPrefix = Ip6Prefix.valueOf(ipAddress, 128);
+ assertThat(ipPrefix.toString(), is("::/128"));
+
+ ipAddress =
+ Ip6Address.valueOf("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff");
+ ipPrefix = Ip6Prefix.valueOf(ipAddress, 0);
+ assertThat(ipPrefix.toString(), is("::/0"));
+
+ ipPrefix = Ip6Prefix.valueOf(ipAddress, 64);
+ assertThat(ipPrefix.toString(), is("ffff:ffff:ffff:ffff::/64"));
+
+ ipPrefix = Ip6Prefix.valueOf(ipAddress, 128);
+ assertThat(ipPrefix.toString(),
+ is("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128"));
}
/**
- * Tests object string representation.
+ * Tests invalid valueOf() converter for a null IP address.
+ */
+ @Test(expected = NullPointerException.class)
+ public void testInvalidValueOfNullAddress() {
+ Ip6Address ipAddress;
+ Ip6Prefix ipPrefix;
+
+ ipAddress = null;
+ ipPrefix = Ip6Prefix.valueOf(ipAddress, 24);
+ }
+
+ /**
+ * Tests invalid valueOf() converter for IPv6 address and
+ * negative prefix length.
+ */
+ @Test(expected = IllegalArgumentException.class)
+ public void testInvalidValueOfAddressNegativePrefixLengthIPv6() {
+ Ip6Address ipAddress;
+ Ip6Prefix ipPrefix;
+
+ ipAddress =
+ Ip6Address.valueOf("1111:2222:3333:4444:5555:6666:7777:8888");
+ ipPrefix = Ip6Prefix.valueOf(ipAddress, -1);
+ }
+
+ /**
+ * Tests invalid valueOf() converter for IPv6 address and
+ * too long prefix length.
+ */
+ @Test(expected = IllegalArgumentException.class)
+ public void testInvalidValueOfAddressTooLongPrefixLengthIPv6() {
+ Ip6Address ipAddress;
+ Ip6Prefix ipPrefix;
+
+ ipAddress =
+ Ip6Address.valueOf("1111:2222:3333:4444:5555:6666:7777:8888");
+ ipPrefix = Ip6Prefix.valueOf(ipAddress, 129);
+ }
+
+ /**
+ * Tests valueOf() converter for IPv6 string.
*/
@Test
- public void testToString() {
- Ip6Prefix ip6prefix = new Ip6Prefix("1100::/8");
- assertThat(ip6prefix.toString(), is("1100::/8"));
+ public void testValueOfStringIPv6() {
+ Ip6Prefix ipPrefix;
- ip6prefix = new Ip6Prefix("1111:2222:3333:4444:5555:6666:7777:8885/8");
- assertThat(ip6prefix.toString(), is("1100::/8"));
+ ipPrefix =
+ Ip6Prefix.valueOf("1111:2222:3333:4444:5555:6666:7777:8888/120");
+ assertThat(ipPrefix.toString(),
+ is("1111:2222:3333:4444:5555:6666:7777:8800/120"));
- ip6prefix = new Ip6Prefix("::/0");
- assertThat(ip6prefix.toString(), is("::/0"));
+ ipPrefix =
+ Ip6Prefix.valueOf("1111:2222:3333:4444:5555:6666:7777:8888/128");
+ assertThat(ipPrefix.toString(),
+ is("1111:2222:3333:4444:5555:6666:7777:8888/128"));
- ip6prefix =
- new Ip6Prefix("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128");
- assertThat(ip6prefix.toString(),
+ ipPrefix = Ip6Prefix.valueOf("::/0");
+ assertThat(ipPrefix.toString(), is("::/0"));
+
+ ipPrefix = Ip6Prefix.valueOf("::/128");
+ assertThat(ipPrefix.toString(), is("::/128"));
+
+ ipPrefix =
+ Ip6Prefix.valueOf("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/0");
+ assertThat(ipPrefix.toString(), is("::/0"));
+
+ ipPrefix =
+ Ip6Prefix.valueOf("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/64");
+ assertThat(ipPrefix.toString(), is("ffff:ffff:ffff:ffff::/64"));
+
+ ipPrefix =
+ Ip6Prefix.valueOf("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128");
+ assertThat(ipPrefix.toString(),
+ is("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128"));
+ }
+
+ /**
+ * Tests invalid valueOf() converter for a null string.
+ */
+ @Test(expected = NullPointerException.class)
+ public void testInvalidValueOfNullString() {
+ Ip6Prefix ipPrefix;
+ String fromString;
+
+ fromString = null;
+ ipPrefix = Ip6Prefix.valueOf(fromString);
+ }
+
+ /**
+ * Tests invalid valueOf() converter for an empty string.
+ */
+ @Test(expected = IllegalArgumentException.class)
+ public void testInvalidValueOfEmptyString() {
+ Ip6Prefix ipPrefix;
+ String fromString;
+
+ fromString = "";
+ ipPrefix = Ip6Prefix.valueOf(fromString);
+ }
+
+ /**
+ * Tests invalid valueOf() converter for an incorrect string.
+ */
+ @Test(expected = IllegalArgumentException.class)
+ public void testInvalidValueOfIncorrectString() {
+ Ip6Prefix ipPrefix;
+ String fromString;
+
+ fromString = "NoSuchIpPrefix";
+ ipPrefix = Ip6Prefix.valueOf(fromString);
+ }
+
+ /**
+ * Tests invalid valueOf() converter for IPv6 string and
+ * negative prefix length.
+ */
+ @Test(expected = IllegalArgumentException.class)
+ public void testInvalidValueOfStringNegativePrefixLengthIPv6() {
+ Ip6Prefix ipPrefix;
+
+ ipPrefix =
+ Ip6Prefix.valueOf("1111:2222:3333:4444:5555:6666:7777:8888/-1");
+ }
+
+ /**
+ * Tests invalid valueOf() converter for IPv6 string and
+ * too long prefix length.
+ */
+ @Test(expected = IllegalArgumentException.class)
+ public void testInvalidValueOfStringTooLongPrefixLengthIPv6() {
+ Ip6Prefix ipPrefix;
+
+ ipPrefix =
+ Ip6Prefix.valueOf("1111:2222:3333:4444:5555:6666:7777:8888/129");
+ }
+
+ /**
+ * Tests IP prefix contains another IP prefix for IPv6.
+ */
+ @Test
+ public void testContainsIpPrefixIPv6() {
+ Ip6Prefix ipPrefix;
+
+ ipPrefix = Ip6Prefix.valueOf("1111:2222:3333:4444::/120");
+ assertTrue(ipPrefix.contains(
+ Ip6Prefix.valueOf("1111:2222:3333:4444::/120")));
+ assertTrue(ipPrefix.contains(
+ Ip6Prefix.valueOf("1111:2222:3333:4444::/128")));
+ assertTrue(ipPrefix.contains(
+ Ip6Prefix.valueOf("1111:2222:3333:4444::1/128")));
+ assertFalse(ipPrefix.contains(
+ Ip6Prefix.valueOf("1111:2222:3333:4444::/64")));
+ assertFalse(ipPrefix.contains(
+ Ip6Prefix.valueOf("1111:2222:3333:4445::/120")));
+ assertFalse(ipPrefix.contains(Ip6Prefix.valueOf("::/64")));
+ assertFalse(ipPrefix.contains(Ip6Prefix.valueOf("::/0")));
+ assertFalse(ipPrefix.contains(
+ Ip6Prefix.valueOf("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128")));
+
+ ipPrefix = Ip6Prefix.valueOf("1111:2222:3333:4444::/128");
+ assertFalse(ipPrefix.contains(
+ Ip6Prefix.valueOf("1111:2222:3333:4444::/120")));
+ assertTrue(ipPrefix.contains(
+ Ip6Prefix.valueOf("1111:2222:3333:4444::/128")));
+ assertFalse(ipPrefix.contains(
+ Ip6Prefix.valueOf("1111:2222:3333:4444::1/128")));
+ assertFalse(ipPrefix.contains(
+ Ip6Prefix.valueOf("1111:2222:3333:4444::/64")));
+ assertFalse(ipPrefix.contains(
+ Ip6Prefix.valueOf("1111:2222:3333:4445::/120")));
+ assertFalse(ipPrefix.contains(Ip6Prefix.valueOf("::/64")));
+ assertFalse(ipPrefix.contains(Ip6Prefix.valueOf("::/0")));
+ assertFalse(ipPrefix.contains(
+ Ip6Prefix.valueOf("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128")));
+
+ ipPrefix = Ip6Prefix.valueOf("::/0");
+ assertTrue(ipPrefix.contains(
+ Ip6Prefix.valueOf("1111:2222:3333:4444::/120")));
+ assertTrue(ipPrefix.contains(
+ Ip6Prefix.valueOf("1111:2222:3333:4444::/128")));
+ assertTrue(ipPrefix.contains(
+ Ip6Prefix.valueOf("1111:2222:3333:4444::1/128")));
+ assertTrue(ipPrefix.contains(
+ Ip6Prefix.valueOf("1111:2222:3333:4444::/64")));
+ assertTrue(ipPrefix.contains(
+ Ip6Prefix.valueOf("1111:2222:3333:4445::/120")));
+ assertTrue(ipPrefix.contains(Ip6Prefix.valueOf("::/64")));
+ assertTrue(ipPrefix.contains(Ip6Prefix.valueOf("::/0")));
+ assertTrue(ipPrefix.contains(
+ Ip6Prefix.valueOf("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128")));
+
+ ipPrefix =
+ Ip6Prefix.valueOf("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128");
+ assertFalse(ipPrefix.contains(
+ Ip6Prefix.valueOf("1111:2222:3333:4444::/120")));
+ assertFalse(ipPrefix.contains(
+ Ip6Prefix.valueOf("1111:2222:3333:4444::/128")));
+ assertFalse(ipPrefix.contains(
+ Ip6Prefix.valueOf("1111:2222:3333:4444::1/128")));
+ assertFalse(ipPrefix.contains(
+ Ip6Prefix.valueOf("1111:2222:3333:4444::/64")));
+ assertFalse(ipPrefix.contains(
+ Ip6Prefix.valueOf("1111:2222:3333:4445::/120")));
+ assertFalse(ipPrefix.contains(Ip6Prefix.valueOf("::/64")));
+ assertFalse(ipPrefix.contains(Ip6Prefix.valueOf("::/0")));
+ assertTrue(ipPrefix.contains(
+ Ip6Prefix.valueOf("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128")));
+ }
+
+ /**
+ * Tests IP prefix contains IP address for IPv6.
+ */
+ @Test
+ public void testContainsIpAddressIPv6() {
+ Ip6Prefix ipPrefix;
+
+ ipPrefix = Ip6Prefix.valueOf("1111:2222:3333:4444::/120");
+ assertTrue(ipPrefix.contains(
+ Ip6Address.valueOf("1111:2222:3333:4444::")));
+ assertTrue(ipPrefix.contains(
+ Ip6Address.valueOf("1111:2222:3333:4444::1")));
+ assertFalse(ipPrefix.contains(
+ Ip6Address.valueOf("1111:2222:3333:4445::")));
+ assertFalse(ipPrefix.contains(Ip6Address.valueOf("::")));
+ assertFalse(ipPrefix.contains(
+ Ip6Address.valueOf("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff")));
+
+ ipPrefix = Ip6Prefix.valueOf("1111:2222:3333:4444::/128");
+ assertTrue(ipPrefix.contains(
+ Ip6Address.valueOf("1111:2222:3333:4444::")));
+ assertFalse(ipPrefix.contains(
+ Ip6Address.valueOf("1111:2222:3333:4444::1")));
+ assertFalse(ipPrefix.contains(
+ Ip6Address.valueOf("1111:2222:3333:4445::")));
+ assertFalse(ipPrefix.contains(Ip6Address.valueOf("::")));
+ assertFalse(ipPrefix.contains(
+ Ip6Address.valueOf("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff")));
+
+ ipPrefix = Ip6Prefix.valueOf("::/0");
+ assertTrue(ipPrefix.contains(
+ Ip6Address.valueOf("1111:2222:3333:4444::")));
+ assertTrue(ipPrefix.contains(
+ Ip6Address.valueOf("1111:2222:3333:4444::1")));
+ assertTrue(ipPrefix.contains(
+ Ip6Address.valueOf("1111:2222:3333:4445::")));
+ assertTrue(ipPrefix.contains(Ip6Address.valueOf("::")));
+ assertTrue(ipPrefix.contains(
+ Ip6Address.valueOf("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff")));
+
+ ipPrefix =
+ Ip6Prefix.valueOf("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128");
+ assertFalse(ipPrefix.contains(
+ Ip6Address.valueOf("1111:2222:3333:4444::")));
+ assertFalse(ipPrefix.contains(
+ Ip6Address.valueOf("1111:2222:3333:4444::1")));
+ assertFalse(ipPrefix.contains(
+ Ip6Address.valueOf("1111:2222:3333:4445::")));
+ assertFalse(ipPrefix.contains(Ip6Address.valueOf("::")));
+ assertTrue(ipPrefix.contains(
+ Ip6Address.valueOf("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff")));
+ }
+
+ /**
+ * Tests equality of {@link Ip6Prefix} for IPv6.
+ */
+ @Test
+ public void testEqualityIPv6() {
+ new EqualsTester()
+ .addEqualityGroup(
+ Ip6Prefix.valueOf("1111:2222:3333:4444::/120"),
+ Ip6Prefix.valueOf("1111:2222:3333:4444::1/120"),
+ Ip6Prefix.valueOf("1111:2222:3333:4444::/120"))
+ .addEqualityGroup(
+ Ip6Prefix.valueOf("1111:2222:3333:4444::/64"),
+ Ip6Prefix.valueOf("1111:2222:3333:4444::/64"))
+ .addEqualityGroup(
+ Ip6Prefix.valueOf("1111:2222:3333:4444::/128"),
+ Ip6Prefix.valueOf("1111:2222:3333:4444::/128"))
+ .addEqualityGroup(
+ Ip6Prefix.valueOf("1111:2222:3333:4445::/64"),
+ Ip6Prefix.valueOf("1111:2222:3333:4445::/64"))
+ .addEqualityGroup(
+ Ip6Prefix.valueOf("::/0"),
+ Ip6Prefix.valueOf("::/0"))
+ .addEqualityGroup(
+ Ip6Prefix.valueOf("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128"),
+ Ip6Prefix.valueOf("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128"))
+ .testEquals();
+ }
+
+ /**
+ * Tests object string representation for IPv6.
+ */
+ @Test
+ public void testToStringIPv6() {
+ Ip6Prefix ipPrefix;
+
+ ipPrefix = Ip6Prefix.valueOf("1100::/8");
+ assertThat(ipPrefix.toString(), is("1100::/8"));
+
+ ipPrefix = Ip6Prefix.valueOf("1111:2222:3333:4444:5555:6666:7777:8885/8");
+ assertThat(ipPrefix.toString(), is("1100::/8"));
+
+ ipPrefix = Ip6Prefix.valueOf("::/0");
+ assertThat(ipPrefix.toString(), is("::/0"));
+
+ ipPrefix = Ip6Prefix.valueOf("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128");
+ assertThat(ipPrefix.toString(),
is("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128"));
}
}
diff --git a/utils/misc/src/test/java/org/onlab/packet/IpAddressTest.java b/utils/misc/src/test/java/org/onlab/packet/IpAddressTest.java
index 4237829..ad3ca4b 100644
--- a/utils/misc/src/test/java/org/onlab/packet/IpAddressTest.java
+++ b/utils/misc/src/test/java/org/onlab/packet/IpAddressTest.java
@@ -17,11 +17,13 @@
import com.google.common.net.InetAddresses;
import com.google.common.testing.EqualsTester;
+import org.junit.Ignore;
import org.junit.Test;
import java.net.InetAddress;
import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.onlab.junit.ImmutableClassChecker.assertThatClassIsImmutable;
@@ -33,6 +35,7 @@
/**
* Tests the immutability of {@link IpAddress}.
*/
+ @Ignore("The class is not pure immutable, because it is not 'final'")
@Test
public void testImmutable() {
assertThatClassIsImmutable(IpAddress.class);
@@ -75,24 +78,63 @@
}
/**
+ * Tests getting the Ip4Address and Ip6Address view of the IP address.
+ */
+ @Test
+ public void testGetIp4AndIp6AddressView() {
+ IpAddress ipAddress;
+ Ip4Address ip4Address;
+ Ip6Address ip6Address;
+
+ // Pure IPv4 IpAddress
+ ipAddress = IpAddress.valueOf("1.2.3.4");
+ ip4Address = ipAddress.getIp4Address();
+ ip6Address = ipAddress.getIp6Address();
+ assertThat(ip4Address.toString(), is("1.2.3.4"));
+ assertNull(ip6Address);
+
+ // IPv4 IpAddress that is Ip4Address
+ ipAddress = Ip4Address.valueOf("1.2.3.4");
+ ip4Address = ipAddress.getIp4Address();
+ ip6Address = ipAddress.getIp6Address();
+ assertThat(ip4Address.toString(), is("1.2.3.4"));
+ assertNull(ip6Address);
+
+ // Pure IPv6 IpAddress
+ ipAddress = IpAddress.valueOf("1111:2222::");
+ ip4Address = ipAddress.getIp4Address();
+ ip6Address = ipAddress.getIp6Address();
+ assertNull(ip4Address);
+ assertThat(ip6Address.toString(), is("1111:2222::"));
+
+ // IPv6 IpAddress that is Ip6Address
+ ipAddress = Ip6Address.valueOf("1111:2222::");
+ ip4Address = ipAddress.getIp4Address();
+ ip6Address = ipAddress.getIp6Address();
+ assertNull(ip4Address);
+ assertThat(ip6Address.toString(), is("1111:2222::"));
+ }
+
+ /**
* Tests returning an IPv4 address as a byte array.
*/
@Test
public void testAddressToOctetsIPv4() {
IpAddress ipAddress;
+ byte[] value;
- final byte[] value1 = new byte[] {1, 2, 3, 4};
+ value = new byte[] {1, 2, 3, 4};
ipAddress = IpAddress.valueOf("1.2.3.4");
- assertThat(ipAddress.toOctets(), is(value1));
+ assertThat(ipAddress.toOctets(), is(value));
- final byte[] value2 = new byte[] {0, 0, 0, 0};
+ value = new byte[] {0, 0, 0, 0};
ipAddress = IpAddress.valueOf("0.0.0.0");
- assertThat(ipAddress.toOctets(), is(value2));
+ assertThat(ipAddress.toOctets(), is(value));
- final byte[] value3 = new byte[] {(byte) 0xff, (byte) 0xff,
- (byte) 0xff, (byte) 0xff};
+ value = new byte[] {(byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff};
ipAddress = IpAddress.valueOf("255.255.255.255");
- assertThat(ipAddress.toOctets(), is(value3));
+ assertThat(ipAddress.toOctets(), is(value));
}
/**
@@ -101,41 +143,42 @@
@Test
public void testAddressToOctetsIPv6() {
IpAddress ipAddress;
+ byte[] value;
- final byte[] value1 = new byte[] {0x11, 0x11, 0x22, 0x22,
- 0x33, 0x33, 0x44, 0x44,
- 0x55, 0x55, 0x66, 0x66,
- 0x77, 0x77,
- (byte) 0x88, (byte) 0x88};
+ value = new byte[] {0x11, 0x11, 0x22, 0x22,
+ 0x33, 0x33, 0x44, 0x44,
+ 0x55, 0x55, 0x66, 0x66,
+ 0x77, 0x77,
+ (byte) 0x88, (byte) 0x88};
ipAddress =
IpAddress.valueOf("1111:2222:3333:4444:5555:6666:7777:8888");
- assertThat(ipAddress.toOctets(), is(value1));
+ assertThat(ipAddress.toOctets(), is(value));
- final byte[] value2 = new byte[] {0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00};
+ value = new byte[] {0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00};
ipAddress = IpAddress.valueOf("::");
- assertThat(ipAddress.toOctets(), is(value2));
+ assertThat(ipAddress.toOctets(), is(value));
- final byte[] value3 = new byte[] {(byte) 0xff, (byte) 0xff,
- (byte) 0xff, (byte) 0xff,
- (byte) 0xff, (byte) 0xff,
- (byte) 0xff, (byte) 0xff,
- (byte) 0xff, (byte) 0xff,
- (byte) 0xff, (byte) 0xff,
- (byte) 0xff, (byte) 0xff,
- (byte) 0xff, (byte) 0xff};
+ value = new byte[] {(byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff};
ipAddress =
IpAddress.valueOf("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff");
- assertThat(ipAddress.toOctets(), is(value3));
+ assertThat(ipAddress.toOctets(), is(value));
}
/**
- * Tests returning an IPv4 address asn an integer.
+ * Tests returning an IPv4 address as an integer.
*/
@Test
- public void testToint() {
+ public void testToInt() {
IpAddress ipAddress;
ipAddress = IpAddress.valueOf("1.2.3.4");
@@ -149,10 +192,10 @@
}
/**
- * Tests valueOf() converter for an integer value.
+ * Tests valueOf() converter for IPv4 integer value.
*/
@Test
- public void testValueOfForInteger() {
+ public void testValueOfForIntegerIPv4() {
IpAddress ipAddress;
ipAddress = IpAddress.valueOf(0x01020304);
@@ -171,18 +214,19 @@
@Test
public void testValueOfByteArrayIPv4() {
IpAddress ipAddress;
+ byte[] value;
- final byte[] value1 = new byte[] {1, 2, 3, 4};
- ipAddress = IpAddress.valueOf(IpAddress.Version.INET, value1);
+ value = new byte[] {1, 2, 3, 4};
+ ipAddress = IpAddress.valueOf(IpAddress.Version.INET, value);
assertThat(ipAddress.toString(), is("1.2.3.4"));
- final byte[] value2 = new byte[] {0, 0, 0, 0};
- ipAddress = IpAddress.valueOf(IpAddress.Version.INET, value2);
+ value = new byte[] {0, 0, 0, 0};
+ ipAddress = IpAddress.valueOf(IpAddress.Version.INET, value);
assertThat(ipAddress.toString(), is("0.0.0.0"));
- final byte[] value3 = new byte[] {(byte) 0xff, (byte) 0xff,
- (byte) 0xff, (byte) 0xff};
- ipAddress = IpAddress.valueOf(IpAddress.Version.INET, value3);
+ value = new byte[] {(byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff};
+ ipAddress = IpAddress.valueOf(IpAddress.Version.INET, value);
assertThat(ipAddress.toString(), is("255.255.255.255"));
}
@@ -192,32 +236,33 @@
@Test
public void testValueOfByteArrayIPv6() {
IpAddress ipAddress;
+ byte[] value;
- final byte[] value1 = new byte[] {0x11, 0x11, 0x22, 0x22,
- 0x33, 0x33, 0x44, 0x44,
- 0x55, 0x55, 0x66, 0x66,
- 0x77, 0x77,
- (byte) 0x88, (byte) 0x88};
- ipAddress = IpAddress.valueOf(IpAddress.Version.INET6, value1);
+ value = new byte[] {0x11, 0x11, 0x22, 0x22,
+ 0x33, 0x33, 0x44, 0x44,
+ 0x55, 0x55, 0x66, 0x66,
+ 0x77, 0x77,
+ (byte) 0x88, (byte) 0x88};
+ ipAddress = IpAddress.valueOf(IpAddress.Version.INET6, value);
assertThat(ipAddress.toString(),
is("1111:2222:3333:4444:5555:6666:7777:8888"));
- final byte[] value2 = new byte[] {0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00};
- ipAddress = IpAddress.valueOf(IpAddress.Version.INET6, value2);
+ value = new byte[] {0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00};
+ ipAddress = IpAddress.valueOf(IpAddress.Version.INET6, value);
assertThat(ipAddress.toString(), is("::"));
- final byte[] value3 = new byte[] {(byte) 0xff, (byte) 0xff,
- (byte) 0xff, (byte) 0xff,
- (byte) 0xff, (byte) 0xff,
- (byte) 0xff, (byte) 0xff,
- (byte) 0xff, (byte) 0xff,
- (byte) 0xff, (byte) 0xff,
- (byte) 0xff, (byte) 0xff,
- (byte) 0xff, (byte) 0xff};
- ipAddress = IpAddress.valueOf(IpAddress.Version.INET6, value3);
+ value = new byte[] {(byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff};
+ ipAddress = IpAddress.valueOf(IpAddress.Version.INET6, value);
assertThat(ipAddress.toString(),
is("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"));
}
@@ -228,9 +273,10 @@
@Test(expected = NullPointerException.class)
public void testInvalidValueOfNullArrayIPv4() {
IpAddress ipAddress;
+ byte[] value;
- final byte[] fromArray = null;
- ipAddress = IpAddress.valueOf(IpAddress.Version.INET, fromArray);
+ value = null;
+ ipAddress = IpAddress.valueOf(IpAddress.Version.INET, value);
}
/**
@@ -239,9 +285,10 @@
@Test(expected = NullPointerException.class)
public void testInvalidValueOfNullArrayIPv6() {
IpAddress ipAddress;
+ byte[] value;
- final byte[] fromArray = null;
- ipAddress = IpAddress.valueOf(IpAddress.Version.INET6, fromArray);
+ value = null;
+ ipAddress = IpAddress.valueOf(IpAddress.Version.INET6, value);
}
/**
@@ -251,9 +298,10 @@
@Test(expected = IllegalArgumentException.class)
public void testInvalidValueOfShortArrayIPv4() {
IpAddress ipAddress;
+ byte[] value;
- final byte[] fromArray = new byte[] {1, 2, 3};
- ipAddress = IpAddress.valueOf(IpAddress.Version.INET, fromArray);
+ value = new byte[] {1, 2, 3};
+ ipAddress = IpAddress.valueOf(IpAddress.Version.INET, value);
}
/**
@@ -263,9 +311,10 @@
@Test(expected = IllegalArgumentException.class)
public void testInvalidValueOfShortArrayIPv6() {
IpAddress ipAddress;
+ byte[] value;
- final byte[] fromArray = new byte[] {1, 2, 3, 4, 5, 6, 7, 8, 9};
- ipAddress = IpAddress.valueOf(IpAddress.Version.INET6, fromArray);
+ value = new byte[] {1, 2, 3, 4, 5, 6, 7, 8, 9};
+ ipAddress = IpAddress.valueOf(IpAddress.Version.INET6, value);
}
/**
@@ -274,24 +323,25 @@
@Test
public void testValueOfByteArrayOffsetIPv4() {
IpAddress ipAddress;
+ byte[] value;
- final byte[] value1 = new byte[] {11, 22, 33, // Preamble
- 1, 2, 3, 4,
- 44, 55}; // Extra bytes
- ipAddress = IpAddress.valueOf(IpAddress.Version.INET, value1, 3);
+ value = new byte[] {11, 22, 33, // Preamble
+ 1, 2, 3, 4,
+ 44, 55}; // Extra bytes
+ ipAddress = IpAddress.valueOf(IpAddress.Version.INET, value, 3);
assertThat(ipAddress.toString(), is("1.2.3.4"));
- final byte[] value2 = new byte[] {11, 22, // Preamble
- 0, 0, 0, 0,
- 33}; // Extra bytes
- ipAddress = IpAddress.valueOf(IpAddress.Version.INET, value2, 2);
+ value = new byte[] {11, 22, // Preamble
+ 0, 0, 0, 0,
+ 33}; // Extra bytes
+ ipAddress = IpAddress.valueOf(IpAddress.Version.INET, value, 2);
assertThat(ipAddress.toString(), is("0.0.0.0"));
- final byte[] value3 = new byte[] {11, 22, // Preamble
- (byte) 0xff, (byte) 0xff,
- (byte) 0xff, (byte) 0xff,
- 33}; // Extra bytes
- ipAddress = IpAddress.valueOf(IpAddress.Version.INET, value3, 2);
+ value = new byte[] {11, 22, // Preamble
+ (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff,
+ 33}; // Extra bytes
+ ipAddress = IpAddress.valueOf(IpAddress.Version.INET, value, 2);
assertThat(ipAddress.toString(), is("255.255.255.255"));
}
@@ -301,38 +351,39 @@
@Test
public void testValueOfByteArrayOffsetIPv6() {
IpAddress ipAddress;
+ byte[] value;
- final byte[] value1 = new byte[] {11, 22, 33, // Preamble
- 0x11, 0x11, 0x22, 0x22,
- 0x33, 0x33, 0x44, 0x44,
- 0x55, 0x55, 0x66, 0x66,
- 0x77, 0x77,
- (byte) 0x88, (byte) 0x88,
- 44, 55}; // Extra bytes
- ipAddress = IpAddress.valueOf(IpAddress.Version.INET6, value1, 3);
+ value = new byte[] {11, 22, 33, // Preamble
+ 0x11, 0x11, 0x22, 0x22,
+ 0x33, 0x33, 0x44, 0x44,
+ 0x55, 0x55, 0x66, 0x66,
+ 0x77, 0x77,
+ (byte) 0x88, (byte) 0x88,
+ 44, 55}; // Extra bytes
+ ipAddress = IpAddress.valueOf(IpAddress.Version.INET6, value, 3);
assertThat(ipAddress.toString(),
is("1111:2222:3333:4444:5555:6666:7777:8888"));
- final byte[] value2 = new byte[] {11, 22, // Preamble
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
- 33}; // Extra bytes
- ipAddress = IpAddress.valueOf(IpAddress.Version.INET6, value2, 2);
+ value = new byte[] {11, 22, // Preamble
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 33}; // Extra bytes
+ ipAddress = IpAddress.valueOf(IpAddress.Version.INET6, value, 2);
assertThat(ipAddress.toString(), is("::"));
- final byte[] value3 = new byte[] {11, 22, // Preamble
- (byte) 0xff, (byte) 0xff,
- (byte) 0xff, (byte) 0xff,
- (byte) 0xff, (byte) 0xff,
- (byte) 0xff, (byte) 0xff,
- (byte) 0xff, (byte) 0xff,
- (byte) 0xff, (byte) 0xff,
- (byte) 0xff, (byte) 0xff,
- (byte) 0xff, (byte) 0xff,
- 33}; // Extra bytes
- ipAddress = IpAddress.valueOf(IpAddress.Version.INET6, value3, 2);
+ value = new byte[] {11, 22, // Preamble
+ (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff,
+ 33}; // Extra bytes
+ ipAddress = IpAddress.valueOf(IpAddress.Version.INET6, value, 2);
assertThat(ipAddress.toString(),
is("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"));
}
@@ -344,11 +395,12 @@
@Test(expected = IllegalArgumentException.class)
public void testInvalidValueOfArrayInvalidOffsetIPv4() {
IpAddress ipAddress;
+ byte[] value;
- final byte[] value1 = new byte[] {11, 22, 33, // Preamble
- 1, 2, 3, 4,
- 44, 55}; // Extra bytes
- ipAddress = IpAddress.valueOf(IpAddress.Version.INET, value1, 6);
+ value = new byte[] {11, 22, 33, // Preamble
+ 1, 2, 3, 4,
+ 44, 55}; // Extra bytes
+ ipAddress = IpAddress.valueOf(IpAddress.Version.INET, value, 6);
}
/**
@@ -358,15 +410,16 @@
@Test(expected = IllegalArgumentException.class)
public void testInvalidValueOfArrayInvalidOffsetIPv6() {
IpAddress ipAddress;
+ byte[] value;
- final byte[] value1 = new byte[] {11, 22, 33, // Preamble
- 0x11, 0x11, 0x22, 0x22,
- 0x33, 0x33, 0x44, 0x44,
- 0x55, 0x55, 0x66, 0x66,
- 0x77, 0x77,
- (byte) 0x88, (byte) 0x88,
- 44, 55}; // Extra bytes
- ipAddress = IpAddress.valueOf(IpAddress.Version.INET6, value1, 6);
+ value = new byte[] {11, 22, 33, // Preamble
+ 0x11, 0x11, 0x22, 0x22,
+ 0x33, 0x33, 0x44, 0x44,
+ 0x55, 0x55, 0x66, 0x66,
+ 0x77, 0x77,
+ (byte) 0x88, (byte) 0x88,
+ 44, 55}; // Extra bytes
+ ipAddress = IpAddress.valueOf(IpAddress.Version.INET6, value, 6);
}
/**
diff --git a/utils/misc/src/test/java/org/onlab/packet/IpPrefixTest.java b/utils/misc/src/test/java/org/onlab/packet/IpPrefixTest.java
index f5ad88b..91a4d49 100644
--- a/utils/misc/src/test/java/org/onlab/packet/IpPrefixTest.java
+++ b/utils/misc/src/test/java/org/onlab/packet/IpPrefixTest.java
@@ -15,123 +15,1011 @@
*/
package org.onlab.packet;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
-import java.util.Arrays;
-
-import org.junit.Test;
-import org.onlab.packet.IpAddress.Version;
-
import com.google.common.testing.EqualsTester;
+import org.junit.Ignore;
+import org.junit.Test;
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+import static org.onlab.junit.ImmutableClassChecker.assertThatClassIsImmutable;
+
+/**
+ * Tests for class {@link IpPrefix}.
+ */
public class IpPrefixTest {
-
- private static final byte [] BYTES1 = new byte [] {0xa, 0x0, 0x0, 0xa};
- private static final byte [] BYTES2 = new byte [] {0xa, 0x0, 0x0, 0xb};
- private static final int INTVAL0 = 0x0a000000;
- private static final int INTVAL1 = 0x0a00000a;
- private static final int INTVAL2 = 0x0a00000b;
- private static final String STRVAL = "10.0.0.12/16";
- private static final int MASK_LENGTH = 16;
-
+ /**
+ * Tests the immutability of {@link IpPrefix}.
+ */
+ @Ignore("The class is not pure immutable, because it is not 'final'")
@Test
- public void testEquality() {
- IpPrefix ip1 = IpPrefix.valueOf(IpAddress.Version.INET,
- BYTES1, IpPrefix.MAX_INET_MASK_LENGTH);
- IpPrefix ip2 = IpPrefix.valueOf(INTVAL1, IpPrefix.MAX_INET_MASK_LENGTH);
- IpPrefix ip3 = IpPrefix.valueOf(IpAddress.Version.INET,
- BYTES2, IpPrefix.MAX_INET_MASK_LENGTH);
- IpPrefix ip4 = IpPrefix.valueOf(INTVAL2, IpPrefix.MAX_INET_MASK_LENGTH);
- IpPrefix ip5 = IpPrefix.valueOf(STRVAL);
-
- new EqualsTester().addEqualityGroup(ip1, ip2)
- .addEqualityGroup(ip3, ip4)
- .addEqualityGroup(ip5)
- .testEquals();
-
- // string conversions
- IpPrefix ip6 = IpPrefix.valueOf(IpAddress.Version.INET,
- BYTES1, MASK_LENGTH);
- IpPrefix ip7 = IpPrefix.valueOf("10.0.0.10/16");
- IpPrefix ip8 = IpPrefix.valueOf(IpAddress.Version.INET,
- new byte [] {0xa, 0x0, 0x0, 0xc}, 16);
- assertEquals("incorrect address conversion", ip6, ip7);
- assertEquals("incorrect address conversion", ip5, ip8);
+ public void testImmutable() {
+ assertThatClassIsImmutable(IpPrefix.class);
}
+ /**
+ * Tests the maximum mask length.
+ */
@Test
- public void basics() {
- IpPrefix ip1 = IpPrefix.valueOf(IpAddress.Version.INET,
- BYTES1, MASK_LENGTH);
- final byte [] bytes = new byte [] {0xa, 0x0, 0x0, 0x0};
-
- // check fields
- assertEquals("incorrect IP Version", Version.INET, ip1.version());
- assertEquals("incorrect netmask", 16, ip1.prefixLength());
- assertTrue("faulty toOctets()",
- Arrays.equals(bytes, ip1.address().toOctets()));
- assertEquals("faulty toInt()", INTVAL0, ip1.address().toInt());
- assertEquals("faulty toString()", "10.0.0.0/16", ip1.toString());
+ public void testMaxMaskLength() {
+ assertThat(IpPrefix.MAX_INET_MASK_LENGTH, is(32));
+ assertThat(IpPrefix.MAX_INET6_MASK_LENGTH, is(128));
}
+ /**
+ * Tests returning the IP version of the prefix.
+ */
@Test
- public void netmasks() {
- // masked
- IpPrefix ip1 = IpPrefix.valueOf(IpAddress.Version.INET,
- BYTES1, MASK_LENGTH);
- IpPrefix ip2 = IpPrefix.valueOf("10.0.0.10/16");
- IpPrefix ip3 = IpPrefix.valueOf("10.0.0.0/16");
- assertEquals("incorrect binary masked address",
- ip1.toString(), "10.0.0.0/16");
- assertEquals("incorrect string masked address",
- ip2.toString(), "10.0.0.0/16");
- assertEquals("incorrect network address",
- ip2.toString(), "10.0.0.0/16");
+ public void testVersion() {
+ IpPrefix ipPrefix;
+
+ // IPv4
+ ipPrefix = IpPrefix.valueOf("0.0.0.0/0");
+ assertThat(ipPrefix.version(), is(IpAddress.Version.INET));
+
+ // IPv6
+ ipPrefix = IpPrefix.valueOf("::/0");
+ assertThat(ipPrefix.version(), is(IpAddress.Version.INET6));
}
+ /**
+ * Tests returning the IP address value and IP address prefix length of
+ * an IPv4 prefix.
+ */
@Test
- public void testContainsIpPrefix() {
- IpPrefix slash31 = IpPrefix.valueOf(IpAddress.Version.INET,
- BYTES1, 31);
- IpPrefix slash32 = IpPrefix.valueOf(IpAddress.Version.INET,
- BYTES1, 32);
- IpPrefix differentSlash32 = IpPrefix.valueOf(IpAddress.Version.INET,
- BYTES2, 32);
+ public void testAddressAndPrefixLengthIPv4() {
+ IpPrefix ipPrefix;
- assertTrue(slash31.contains(differentSlash32));
- assertFalse(differentSlash32.contains(slash31));
+ ipPrefix = IpPrefix.valueOf("1.2.3.0/24");
+ assertThat(ipPrefix.address(), equalTo(IpAddress.valueOf("1.2.3.0")));
+ assertThat(ipPrefix.prefixLength(), is(24));
- assertTrue(slash31.contains(slash32));
- assertFalse(slash32.contains(differentSlash32));
- assertFalse(differentSlash32.contains(slash32));
+ ipPrefix = IpPrefix.valueOf("1.2.3.4/24");
+ assertThat(ipPrefix.address(), equalTo(IpAddress.valueOf("1.2.3.0")));
+ assertThat(ipPrefix.prefixLength(), is(24));
- IpPrefix zero = IpPrefix.valueOf("0.0.0.0/0");
- assertTrue(zero.contains(differentSlash32));
- assertFalse(differentSlash32.contains(zero));
+ ipPrefix = IpPrefix.valueOf("1.2.3.4/32");
+ assertThat(ipPrefix.address(), equalTo(IpAddress.valueOf("1.2.3.4")));
+ assertThat(ipPrefix.prefixLength(), is(32));
- IpPrefix slash8 = IpPrefix.valueOf("10.0.0.0/8");
- assertTrue(slash8.contains(slash31));
- assertFalse(slash31.contains(slash8));
+ ipPrefix = IpPrefix.valueOf("1.2.3.5/32");
+ assertThat(ipPrefix.address(), equalTo(IpAddress.valueOf("1.2.3.5")));
+ assertThat(ipPrefix.prefixLength(), is(32));
+
+ ipPrefix = IpPrefix.valueOf("0.0.0.0/0");
+ assertThat(ipPrefix.address(), equalTo(IpAddress.valueOf("0.0.0.0")));
+ assertThat(ipPrefix.prefixLength(), is(0));
+
+ ipPrefix = IpPrefix.valueOf("255.255.255.255/32");
+ assertThat(ipPrefix.address(),
+ equalTo(IpAddress.valueOf("255.255.255.255")));
+ assertThat(ipPrefix.prefixLength(), is(32));
}
+ /**
+ * Tests returning the IP address value and IP address prefix length of
+ * an IPv6 prefix.
+ */
@Test
- public void testContainsIpAddress() {
- IpPrefix slash31 = IpPrefix.valueOf(IpAddress.Version.INET,
- BYTES1, 31);
- IpAddress addr32 = IpAddress.valueOf(IpAddress.Version.INET, BYTES1);
+ public void testAddressAndPrefixLengthIPv6() {
+ IpPrefix ipPrefix;
- assertTrue(slash31.contains(addr32));
+ ipPrefix = IpPrefix.valueOf("1100::/8");
+ assertThat(ipPrefix.address(), equalTo(IpAddress.valueOf("1100::")));
+ assertThat(ipPrefix.prefixLength(), is(8));
- IpPrefix intf = IpPrefix.valueOf("192.168.10.101/24");
- IpAddress addr = IpAddress.valueOf("192.168.10.1");
+ ipPrefix =
+ IpPrefix.valueOf("1111:2222:3333:4444:5555:6666:7777:8885/8");
+ assertThat(ipPrefix.address(), equalTo(IpAddress.valueOf("1100::")));
+ assertThat(ipPrefix.prefixLength(), is(8));
- assertTrue(intf.contains(addr));
+ ipPrefix =
+ IpPrefix.valueOf("1111:2222:3333:4444:5555:6666:7777:8800/120");
+ assertThat(ipPrefix.address(),
+ equalTo(IpAddress.valueOf("1111:2222:3333:4444:5555:6666:7777:8800")));
+ assertThat(ipPrefix.prefixLength(), is(120));
- IpPrefix intf1 = IpPrefix.valueOf("10.0.0.101/24");
- IpAddress addr1 = IpAddress.valueOf("10.0.0.4");
+ ipPrefix =
+ IpPrefix.valueOf("1111:2222:3333:4444:5555:6666:7777:8885/128");
+ assertThat(ipPrefix.address(),
+ equalTo(IpAddress.valueOf("1111:2222:3333:4444:5555:6666:7777:8885")));
+ assertThat(ipPrefix.prefixLength(), is(128));
- assertTrue(intf1.contains(addr1));
+ ipPrefix = IpPrefix.valueOf("::/0");
+ assertThat(ipPrefix.address(), equalTo(IpAddress.valueOf("::")));
+ assertThat(ipPrefix.prefixLength(), is(0));
+
+ ipPrefix =
+ IpPrefix.valueOf("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128");
+ assertThat(ipPrefix.address(),
+ equalTo(IpAddress.valueOf("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff")));
+ assertThat(ipPrefix.prefixLength(), is(128));
+
+ ipPrefix =
+ IpPrefix.valueOf("1111:2222:3333:4444:5555:6666:7777:8885/64");
+ assertThat(ipPrefix.address(),
+ equalTo(IpAddress.valueOf("1111:2222:3333:4444::")));
+ assertThat(ipPrefix.prefixLength(), is(64));
+ }
+
+ /**
+ * Tests getting the Ip4Prefix and Ip6Prefix view of the IP prefix.
+ */
+ @Test
+ public void testGetIp4AndIp6PrefixView() {
+ IpPrefix ipPrefix;
+ Ip4Prefix ip4Prefix;
+ Ip6Prefix ip6Prefix;
+
+ // Pure IPv4 IpPrefix
+ ipPrefix = IpPrefix.valueOf("1.2.3.0/24");
+ ip4Prefix = ipPrefix.getIp4Prefix();
+ ip6Prefix = ipPrefix.getIp6Prefix();
+ assertThat(ip4Prefix.toString(), is("1.2.3.0/24"));
+ assertNull(ip6Prefix);
+
+ // IPv4 IpPrefix that is Ip4Prefix
+ ipPrefix = Ip4Prefix.valueOf("1.2.3.0/24");
+ ip4Prefix = ipPrefix.getIp4Prefix();
+ ip6Prefix = ipPrefix.getIp6Prefix();
+ assertThat(ip4Prefix.toString(), is("1.2.3.0/24"));
+ assertNull(ip6Prefix);
+
+ // Pure IPv6 IpPrefix
+ ipPrefix = IpPrefix.valueOf("1111:2222::/64");
+ ip4Prefix = ipPrefix.getIp4Prefix();
+ ip6Prefix = ipPrefix.getIp6Prefix();
+ assertNull(ip4Prefix);
+ assertThat(ip6Prefix.toString(), is("1111:2222::/64"));
+
+ // IPv6 IpPrefix that is Ip6Prefix
+ ipPrefix = Ip6Prefix.valueOf("1111:2222::/64");
+ ip4Prefix = ipPrefix.getIp4Prefix();
+ ip6Prefix = ipPrefix.getIp6Prefix();
+ assertNull(ip4Prefix);
+ assertThat(ip6Prefix.toString(), is("1111:2222::/64"));
+ }
+
+ /**
+ * Tests valueOf() converter for IPv4 integer value.
+ */
+ @Test
+ public void testValueOfForIntegerIPv4() {
+ IpPrefix ipPrefix;
+
+ ipPrefix = IpPrefix.valueOf(0x01020304, 24);
+ assertThat(ipPrefix.toString(), is("1.2.3.0/24"));
+
+ ipPrefix = IpPrefix.valueOf(0x01020304, 32);
+ assertThat(ipPrefix.toString(), is("1.2.3.4/32"));
+
+ ipPrefix = IpPrefix.valueOf(0x01020305, 32);
+ assertThat(ipPrefix.toString(), is("1.2.3.5/32"));
+
+ ipPrefix = IpPrefix.valueOf(0, 0);
+ assertThat(ipPrefix.toString(), is("0.0.0.0/0"));
+
+ ipPrefix = IpPrefix.valueOf(0, 32);
+ assertThat(ipPrefix.toString(), is("0.0.0.0/32"));
+
+ ipPrefix = IpPrefix.valueOf(0xffffffff, 0);
+ assertThat(ipPrefix.toString(), is("0.0.0.0/0"));
+
+ ipPrefix = IpPrefix.valueOf(0xffffffff, 16);
+ assertThat(ipPrefix.toString(), is("255.255.0.0/16"));
+
+ ipPrefix = IpPrefix.valueOf(0xffffffff, 32);
+ assertThat(ipPrefix.toString(), is("255.255.255.255/32"));
+ }
+
+ /**
+ * Tests invalid valueOf() converter for IPv4 integer value and
+ * negative prefix length.
+ */
+ @Test(expected = IllegalArgumentException.class)
+ public void testInvalidValueOfIntegerNegativePrefixLengthIPv4() {
+ IpPrefix ipPrefix;
+
+ ipPrefix = IpPrefix.valueOf(0x01020304, -1);
+ }
+
+ /**
+ * Tests invalid valueOf() converter for IPv4 integer value and
+ * too long prefix length.
+ */
+ @Test(expected = IllegalArgumentException.class)
+ public void testInvalidValueOfIntegerTooLongPrefixLengthIPv4() {
+ IpPrefix ipPrefix;
+
+ ipPrefix = IpPrefix.valueOf(0x01020304, 33);
+ }
+
+ /**
+ * Tests valueOf() converter for IPv4 byte array.
+ */
+ @Test
+ public void testValueOfByteArrayIPv4() {
+ IpPrefix ipPrefix;
+ byte[] value;
+
+ value = new byte[] {1, 2, 3, 4};
+ ipPrefix = IpPrefix.valueOf(IpAddress.Version.INET, value, 24);
+ assertThat(ipPrefix.toString(), is("1.2.3.0/24"));
+
+ ipPrefix = IpPrefix.valueOf(IpAddress.Version.INET, value, 32);
+ assertThat(ipPrefix.toString(), is("1.2.3.4/32"));
+
+ value = new byte[] {1, 2, 3, 5};
+ ipPrefix = IpPrefix.valueOf(IpAddress.Version.INET, value, 32);
+ assertThat(ipPrefix.toString(), is("1.2.3.5/32"));
+
+ value = new byte[] {0, 0, 0, 0};
+ ipPrefix = IpPrefix.valueOf(IpAddress.Version.INET, value, 0);
+ assertThat(ipPrefix.toString(), is("0.0.0.0/0"));
+
+ ipPrefix = IpPrefix.valueOf(IpAddress.Version.INET, value, 32);
+ assertThat(ipPrefix.toString(), is("0.0.0.0/32"));
+
+ value = new byte[] {(byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff};
+ ipPrefix = IpPrefix.valueOf(IpAddress.Version.INET, value, 0);
+ assertThat(ipPrefix.toString(), is("0.0.0.0/0"));
+
+ ipPrefix = IpPrefix.valueOf(IpAddress.Version.INET, value, 16);
+ assertThat(ipPrefix.toString(), is("255.255.0.0/16"));
+
+ ipPrefix = IpPrefix.valueOf(IpAddress.Version.INET, value, 32);
+ assertThat(ipPrefix.toString(), is("255.255.255.255/32"));
+ }
+
+ /**
+ * Tests valueOf() converter for IPv6 byte array.
+ */
+ @Test
+ public void testValueOfByteArrayIPv6() {
+ IpPrefix ipPrefix;
+ byte[] value;
+
+ value = new byte[] {0x11, 0x11, 0x22, 0x22,
+ 0x33, 0x33, 0x44, 0x44,
+ 0x55, 0x55, 0x66, 0x66,
+ 0x77, 0x77, (byte) 0x88, (byte) 0x88};
+ ipPrefix = IpPrefix.valueOf(IpAddress.Version.INET6, value, 120);
+ assertThat(ipPrefix.toString(),
+ is("1111:2222:3333:4444:5555:6666:7777:8800/120"));
+
+ ipPrefix = IpPrefix.valueOf(IpAddress.Version.INET6, value, 128);
+ assertThat(ipPrefix.toString(),
+ is("1111:2222:3333:4444:5555:6666:7777:8888/128"));
+
+ value = new byte[] {0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00};
+ ipPrefix = IpPrefix.valueOf(IpAddress.Version.INET6, value, 0);
+ assertThat(ipPrefix.toString(), is("::/0"));
+
+ ipPrefix = IpPrefix.valueOf(IpAddress.Version.INET6, value, 128);
+ assertThat(ipPrefix.toString(), is("::/128"));
+
+ value = new byte[] {(byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff};
+ ipPrefix = IpPrefix.valueOf(IpAddress.Version.INET6, value, 0);
+ assertThat(ipPrefix.toString(), is("::/0"));
+
+ ipPrefix = IpPrefix.valueOf(IpAddress.Version.INET6, value, 64);
+ assertThat(ipPrefix.toString(), is("ffff:ffff:ffff:ffff::/64"));
+
+ ipPrefix = IpPrefix.valueOf(IpAddress.Version.INET6, value, 128);
+ assertThat(ipPrefix.toString(),
+ is("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128"));
+ }
+
+ /**
+ * Tests invalid valueOf() converter for a null array for IPv4.
+ */
+ @Test(expected = NullPointerException.class)
+ public void testInvalidValueOfNullArrayIPv4() {
+ IpPrefix ipPrefix;
+ byte[] value;
+
+ value = null;
+ ipPrefix = IpPrefix.valueOf(IpAddress.Version.INET, value, 24);
+ }
+
+ /**
+ * Tests invalid valueOf() converter for a null array for IPv6.
+ */
+ @Test(expected = NullPointerException.class)
+ public void testInvalidValueOfNullArrayIPv6() {
+ IpPrefix ipPrefix;
+ byte[] value;
+
+ value = null;
+ ipPrefix = IpPrefix.valueOf(IpAddress.Version.INET6, value, 120);
+ }
+
+ /**
+ * Tests invalid valueOf() converter for a short array for IPv4.
+ */
+ @Test(expected = IllegalArgumentException.class)
+ public void testInvalidValueOfShortArrayIPv4() {
+ IpPrefix ipPrefix;
+ byte[] value;
+
+ value = new byte[] {1, 2, 3};
+ ipPrefix = IpPrefix.valueOf(IpAddress.Version.INET, value, 24);
+ }
+
+ /**
+ * Tests invalid valueOf() converter for a short array for IPv6.
+ */
+ @Test(expected = IllegalArgumentException.class)
+ public void testInvalidValueOfShortArrayIPv6() {
+ IpPrefix ipPrefix;
+ byte[] value;
+
+ value = new byte[] {1, 2, 3, 4, 5, 6, 7, 8, 9};
+ ipPrefix = IpPrefix.valueOf(IpAddress.Version.INET6, value, 120);
+ }
+
+ /**
+ * Tests invalid valueOf() converter for IPv4 byte array and
+ * negative prefix length.
+ */
+ @Test(expected = IllegalArgumentException.class)
+ public void testInvalidValueOfByteArrayNegativePrefixLengthIPv4() {
+ IpPrefix ipPrefix;
+ byte[] value;
+
+ value = new byte[] {1, 2, 3, 4};
+ ipPrefix = IpPrefix.valueOf(IpAddress.Version.INET, value, -1);
+ }
+
+ /**
+ * Tests invalid valueOf() converter for IPv6 byte array and
+ * negative prefix length.
+ */
+ @Test(expected = IllegalArgumentException.class)
+ public void testInvalidValueOfByteArrayNegativePrefixLengthIPv6() {
+ IpPrefix ipPrefix;
+ byte[] value;
+
+ value = new byte[] {0x11, 0x11, 0x22, 0x22,
+ 0x33, 0x33, 0x44, 0x44,
+ 0x55, 0x55, 0x66, 0x66,
+ 0x77, 0x77, (byte) 0x88, (byte) 0x88};
+ ipPrefix = IpPrefix.valueOf(IpAddress.Version.INET6, value, -1);
+ }
+
+ /**
+ * Tests invalid valueOf() converter for IPv4 byte array and
+ * too long prefix length.
+ */
+ @Test(expected = IllegalArgumentException.class)
+ public void testInvalidValueOfByteArrayTooLongPrefixLengthIPv4() {
+ IpPrefix ipPrefix;
+ byte[] value;
+
+ value = new byte[] {1, 2, 3, 4};
+ ipPrefix = IpPrefix.valueOf(IpAddress.Version.INET, value, 33);
+ }
+
+ /**
+ * Tests invalid valueOf() converter for IPv6 byte array and
+ * too long prefix length.
+ */
+ @Test(expected = IllegalArgumentException.class)
+ public void testInvalidValueOfByteArrayTooLongPrefixLengthIPv6() {
+ IpPrefix ipPrefix;
+ byte[] value;
+
+ value = new byte[] {0x11, 0x11, 0x22, 0x22,
+ 0x33, 0x33, 0x44, 0x44,
+ 0x55, 0x55, 0x66, 0x66,
+ 0x77, 0x77, (byte) 0x88, (byte) 0x88};
+ ipPrefix = IpPrefix.valueOf(IpAddress.Version.INET6, value, 129);
+ }
+
+ /**
+ * Tests valueOf() converter for IPv4 address.
+ */
+ @Test
+ public void testValueOfAddressIPv4() {
+ IpAddress ipAddress;
+ IpPrefix ipPrefix;
+
+ ipAddress = IpAddress.valueOf("1.2.3.4");
+ ipPrefix = IpPrefix.valueOf(ipAddress, 24);
+ assertThat(ipPrefix.toString(), is("1.2.3.0/24"));
+
+ ipPrefix = IpPrefix.valueOf(ipAddress, 32);
+ assertThat(ipPrefix.toString(), is("1.2.3.4/32"));
+
+ ipAddress = IpAddress.valueOf("1.2.3.5");
+ ipPrefix = IpPrefix.valueOf(ipAddress, 32);
+ assertThat(ipPrefix.toString(), is("1.2.3.5/32"));
+
+ ipAddress = IpAddress.valueOf("0.0.0.0");
+ ipPrefix = IpPrefix.valueOf(ipAddress, 0);
+ assertThat(ipPrefix.toString(), is("0.0.0.0/0"));
+
+ ipPrefix = IpPrefix.valueOf(ipAddress, 32);
+ assertThat(ipPrefix.toString(), is("0.0.0.0/32"));
+
+ ipAddress = IpAddress.valueOf("255.255.255.255");
+ ipPrefix = IpPrefix.valueOf(ipAddress, 0);
+ assertThat(ipPrefix.toString(), is("0.0.0.0/0"));
+
+ ipPrefix = IpPrefix.valueOf(ipAddress, 16);
+ assertThat(ipPrefix.toString(), is("255.255.0.0/16"));
+
+ ipPrefix = IpPrefix.valueOf(ipAddress, 32);
+ assertThat(ipPrefix.toString(), is("255.255.255.255/32"));
+ }
+
+ /**
+ * Tests valueOf() converter for IPv6 address.
+ */
+ @Test
+ public void testValueOfAddressIPv6() {
+ IpAddress ipAddress;
+ IpPrefix ipPrefix;
+
+ ipAddress =
+ IpAddress.valueOf("1111:2222:3333:4444:5555:6666:7777:8888");
+ ipPrefix = IpPrefix.valueOf(ipAddress, 120);
+ assertThat(ipPrefix.toString(),
+ is("1111:2222:3333:4444:5555:6666:7777:8800/120"));
+
+ ipPrefix = IpPrefix.valueOf(ipAddress, 128);
+ assertThat(ipPrefix.toString(),
+ is("1111:2222:3333:4444:5555:6666:7777:8888/128"));
+
+ ipAddress = IpAddress.valueOf("::");
+ ipPrefix = IpPrefix.valueOf(ipAddress, 0);
+ assertThat(ipPrefix.toString(), is("::/0"));
+
+ ipPrefix = IpPrefix.valueOf(ipAddress, 128);
+ assertThat(ipPrefix.toString(), is("::/128"));
+
+ ipAddress =
+ IpAddress.valueOf("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff");
+ ipPrefix = IpPrefix.valueOf(ipAddress, 0);
+ assertThat(ipPrefix.toString(), is("::/0"));
+
+ ipPrefix = IpPrefix.valueOf(ipAddress, 64);
+ assertThat(ipPrefix.toString(), is("ffff:ffff:ffff:ffff::/64"));
+
+ ipPrefix = IpPrefix.valueOf(ipAddress, 128);
+ assertThat(ipPrefix.toString(),
+ is("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128"));
+ }
+
+ /**
+ * Tests invalid valueOf() converter for a null IP address.
+ */
+ @Test(expected = NullPointerException.class)
+ public void testInvalidValueOfNullAddress() {
+ IpAddress ipAddress;
+ IpPrefix ipPrefix;
+
+ ipAddress = null;
+ ipPrefix = IpPrefix.valueOf(ipAddress, 24);
+ }
+
+ /**
+ * Tests invalid valueOf() converter for IPv4 address and
+ * negative prefix length.
+ */
+ @Test(expected = IllegalArgumentException.class)
+ public void testInvalidValueOfAddressNegativePrefixLengthIPv4() {
+ IpAddress ipAddress;
+ IpPrefix ipPrefix;
+
+ ipAddress = IpAddress.valueOf("1.2.3.4");
+ ipPrefix = IpPrefix.valueOf(ipAddress, -1);
+ }
+
+ /**
+ * Tests invalid valueOf() converter for IPv6 address and
+ * negative prefix length.
+ */
+ @Test(expected = IllegalArgumentException.class)
+ public void testInvalidValueOfAddressNegativePrefixLengthIPv6() {
+ IpAddress ipAddress;
+ IpPrefix ipPrefix;
+
+ ipAddress =
+ IpAddress.valueOf("1111:2222:3333:4444:5555:6666:7777:8888");
+ ipPrefix = IpPrefix.valueOf(ipAddress, -1);
+ }
+
+ /**
+ * Tests invalid valueOf() converter for IPv4 address and
+ * too long prefix length.
+ */
+ @Test(expected = IllegalArgumentException.class)
+ public void testInvalidValueOfAddressTooLongPrefixLengthIPv4() {
+ IpAddress ipAddress;
+ IpPrefix ipPrefix;
+
+ ipAddress = IpAddress.valueOf("1.2.3.4");
+ ipPrefix = IpPrefix.valueOf(ipAddress, 33);
+ }
+
+ /**
+ * Tests invalid valueOf() converter for IPv6 address and
+ * too long prefix length.
+ */
+ @Test(expected = IllegalArgumentException.class)
+ public void testInvalidValueOfAddressTooLongPrefixLengthIPv6() {
+ IpAddress ipAddress;
+ IpPrefix ipPrefix;
+
+ ipAddress =
+ IpAddress.valueOf("1111:2222:3333:4444:5555:6666:7777:8888");
+ ipPrefix = IpPrefix.valueOf(ipAddress, 129);
+ }
+
+ /**
+ * Tests valueOf() converter for IPv4 string.
+ */
+ @Test
+ public void testValueOfStringIPv4() {
+ IpPrefix ipPrefix;
+
+ ipPrefix = IpPrefix.valueOf("1.2.3.4/24");
+ assertThat(ipPrefix.toString(), is("1.2.3.0/24"));
+
+ ipPrefix = IpPrefix.valueOf("1.2.3.4/32");
+ assertThat(ipPrefix.toString(), is("1.2.3.4/32"));
+
+ ipPrefix = IpPrefix.valueOf("1.2.3.5/32");
+ assertThat(ipPrefix.toString(), is("1.2.3.5/32"));
+
+ ipPrefix = IpPrefix.valueOf("0.0.0.0/0");
+ assertThat(ipPrefix.toString(), is("0.0.0.0/0"));
+
+ ipPrefix = IpPrefix.valueOf("0.0.0.0/32");
+ assertThat(ipPrefix.toString(), is("0.0.0.0/32"));
+
+ ipPrefix = IpPrefix.valueOf("255.255.255.255/0");
+ assertThat(ipPrefix.toString(), is("0.0.0.0/0"));
+
+ ipPrefix = IpPrefix.valueOf("255.255.255.255/16");
+ assertThat(ipPrefix.toString(), is("255.255.0.0/16"));
+
+ ipPrefix = IpPrefix.valueOf("255.255.255.255/32");
+ assertThat(ipPrefix.toString(), is("255.255.255.255/32"));
+ }
+
+ /**
+ * Tests valueOf() converter for IPv6 string.
+ */
+ @Test
+ public void testValueOfStringIPv6() {
+ IpPrefix ipPrefix;
+
+ ipPrefix =
+ IpPrefix.valueOf("1111:2222:3333:4444:5555:6666:7777:8888/120");
+ assertThat(ipPrefix.toString(),
+ is("1111:2222:3333:4444:5555:6666:7777:8800/120"));
+
+ ipPrefix =
+ IpPrefix.valueOf("1111:2222:3333:4444:5555:6666:7777:8888/128");
+ assertThat(ipPrefix.toString(),
+ is("1111:2222:3333:4444:5555:6666:7777:8888/128"));
+
+ ipPrefix = IpPrefix.valueOf("::/0");
+ assertThat(ipPrefix.toString(), is("::/0"));
+
+ ipPrefix = IpPrefix.valueOf("::/128");
+ assertThat(ipPrefix.toString(), is("::/128"));
+
+ ipPrefix =
+ IpPrefix.valueOf("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/0");
+ assertThat(ipPrefix.toString(), is("::/0"));
+
+ ipPrefix =
+ IpPrefix.valueOf("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/64");
+ assertThat(ipPrefix.toString(), is("ffff:ffff:ffff:ffff::/64"));
+
+ ipPrefix =
+ IpPrefix.valueOf("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128");
+ assertThat(ipPrefix.toString(),
+ is("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128"));
+ }
+
+ /**
+ * Tests invalid valueOf() converter for a null string.
+ */
+ @Test(expected = NullPointerException.class)
+ public void testInvalidValueOfNullString() {
+ IpPrefix ipPrefix;
+ String fromString;
+
+ fromString = null;
+ ipPrefix = IpPrefix.valueOf(fromString);
+ }
+
+ /**
+ * Tests invalid valueOf() converter for an empty string.
+ */
+ @Test(expected = IllegalArgumentException.class)
+ public void testInvalidValueOfEmptyString() {
+ IpPrefix ipPrefix;
+ String fromString;
+
+ fromString = "";
+ ipPrefix = IpPrefix.valueOf(fromString);
+ }
+
+ /**
+ * Tests invalid valueOf() converter for an incorrect string.
+ */
+ @Test(expected = IllegalArgumentException.class)
+ public void testInvalidValueOfIncorrectString() {
+ IpPrefix ipPrefix;
+ String fromString;
+
+ fromString = "NoSuchIpPrefix";
+ ipPrefix = IpPrefix.valueOf(fromString);
+ }
+
+ /**
+ * Tests invalid valueOf() converter for IPv4 string and
+ * negative prefix length.
+ */
+ @Test(expected = IllegalArgumentException.class)
+ public void testInvalidValueOfStringNegativePrefixLengthIPv4() {
+ IpPrefix ipPrefix;
+
+ ipPrefix = IpPrefix.valueOf("1.2.3.4/-1");
+ }
+
+ /**
+ * Tests invalid valueOf() converter for IPv6 string and
+ * negative prefix length.
+ */
+ @Test(expected = IllegalArgumentException.class)
+ public void testInvalidValueOfStringNegativePrefixLengthIPv6() {
+ IpPrefix ipPrefix;
+
+ ipPrefix =
+ IpPrefix.valueOf("1111:2222:3333:4444:5555:6666:7777:8888/-1");
+ }
+
+ /**
+ * Tests invalid valueOf() converter for IPv4 string and
+ * too long prefix length.
+ */
+ @Test(expected = IllegalArgumentException.class)
+ public void testInvalidValueOfStringTooLongPrefixLengthIPv4() {
+ IpPrefix ipPrefix;
+
+ ipPrefix = IpPrefix.valueOf("1.2.3.4/33");
+ }
+
+ /**
+ * Tests invalid valueOf() converter for IPv6 string and
+ * too long prefix length.
+ */
+ @Test(expected = IllegalArgumentException.class)
+ public void testInvalidValueOfStringTooLongPrefixLengthIPv6() {
+ IpPrefix ipPrefix;
+
+ ipPrefix =
+ IpPrefix.valueOf("1111:2222:3333:4444:5555:6666:7777:8888/129");
+ }
+
+ /**
+ * Tests IP prefix contains another IP prefix for IPv4.
+ */
+ @Test
+ public void testContainsIpPrefixIPv4() {
+ IpPrefix ipPrefix;
+
+ ipPrefix = IpPrefix.valueOf("1.2.0.0/24");
+ assertTrue(ipPrefix.contains(IpPrefix.valueOf("1.2.0.0/24")));
+ assertTrue(ipPrefix.contains(IpPrefix.valueOf("1.2.0.0/32")));
+ assertTrue(ipPrefix.contains(IpPrefix.valueOf("1.2.0.4/32")));
+ assertFalse(ipPrefix.contains(IpPrefix.valueOf("1.2.0.0/16")));
+ assertFalse(ipPrefix.contains(IpPrefix.valueOf("1.3.0.0/24")));
+ assertFalse(ipPrefix.contains(IpPrefix.valueOf("0.0.0.0/16")));
+ assertFalse(ipPrefix.contains(IpPrefix.valueOf("0.0.0.0/0")));
+ assertFalse(ipPrefix.contains(IpPrefix.valueOf("255.255.255.255/32")));
+
+ ipPrefix = IpPrefix.valueOf("1.2.0.0/32");
+ assertFalse(ipPrefix.contains(IpPrefix.valueOf("1.2.0.0/24")));
+ assertTrue(ipPrefix.contains(IpPrefix.valueOf("1.2.0.0/32")));
+ assertFalse(ipPrefix.contains(IpPrefix.valueOf("1.2.0.4/32")));
+ assertFalse(ipPrefix.contains(IpPrefix.valueOf("1.2.0.0/16")));
+ assertFalse(ipPrefix.contains(IpPrefix.valueOf("1.3.0.0/24")));
+ assertFalse(ipPrefix.contains(IpPrefix.valueOf("0.0.0.0/16")));
+ assertFalse(ipPrefix.contains(IpPrefix.valueOf("0.0.0.0/0")));
+ assertFalse(ipPrefix.contains(IpPrefix.valueOf("255.255.255.255/32")));
+
+ ipPrefix = IpPrefix.valueOf("0.0.0.0/0");
+ assertTrue(ipPrefix.contains(IpPrefix.valueOf("1.2.0.0/24")));
+ assertTrue(ipPrefix.contains(IpPrefix.valueOf("1.2.0.0/32")));
+ assertTrue(ipPrefix.contains(IpPrefix.valueOf("1.2.0.4/32")));
+ assertTrue(ipPrefix.contains(IpPrefix.valueOf("1.2.0.0/16")));
+ assertTrue(ipPrefix.contains(IpPrefix.valueOf("1.3.0.0/24")));
+ assertTrue(ipPrefix.contains(IpPrefix.valueOf("0.0.0.0/16")));
+ assertTrue(ipPrefix.contains(IpPrefix.valueOf("0.0.0.0/0")));
+ assertTrue(ipPrefix.contains(IpPrefix.valueOf("255.255.255.255/32")));
+
+ ipPrefix = IpPrefix.valueOf("255.255.255.255/32");
+ assertFalse(ipPrefix.contains(IpPrefix.valueOf("1.2.0.0/24")));
+ assertFalse(ipPrefix.contains(IpPrefix.valueOf("1.2.0.0/32")));
+ assertFalse(ipPrefix.contains(IpPrefix.valueOf("1.2.0.4/32")));
+ assertFalse(ipPrefix.contains(IpPrefix.valueOf("1.2.0.0/16")));
+ assertFalse(ipPrefix.contains(IpPrefix.valueOf("1.3.0.0/24")));
+ assertFalse(ipPrefix.contains(IpPrefix.valueOf("0.0.0.0/16")));
+ assertFalse(ipPrefix.contains(IpPrefix.valueOf("0.0.0.0/0")));
+ assertTrue(ipPrefix.contains(IpPrefix.valueOf("255.255.255.255/32")));
+ }
+
+ /**
+ * Tests IP prefix contains another IP prefix for IPv6.
+ */
+ @Test
+ public void testContainsIpPrefixIPv6() {
+ IpPrefix ipPrefix;
+
+ ipPrefix = IpPrefix.valueOf("1111:2222:3333:4444::/120");
+ assertTrue(ipPrefix.contains(
+ IpPrefix.valueOf("1111:2222:3333:4444::/120")));
+ assertTrue(ipPrefix.contains(
+ IpPrefix.valueOf("1111:2222:3333:4444::/128")));
+ assertTrue(ipPrefix.contains(
+ IpPrefix.valueOf("1111:2222:3333:4444::1/128")));
+ assertFalse(ipPrefix.contains(
+ IpPrefix.valueOf("1111:2222:3333:4444::/64")));
+ assertFalse(ipPrefix.contains(
+ IpPrefix.valueOf("1111:2222:3333:4445::/120")));
+ assertFalse(ipPrefix.contains(IpPrefix.valueOf("::/64")));
+ assertFalse(ipPrefix.contains(IpPrefix.valueOf("::/0")));
+ assertFalse(ipPrefix.contains(
+ IpPrefix.valueOf("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128")));
+
+ ipPrefix = IpPrefix.valueOf("1111:2222:3333:4444::/128");
+ assertFalse(ipPrefix.contains(
+ IpPrefix.valueOf("1111:2222:3333:4444::/120")));
+ assertTrue(ipPrefix.contains(
+ IpPrefix.valueOf("1111:2222:3333:4444::/128")));
+ assertFalse(ipPrefix.contains(
+ IpPrefix.valueOf("1111:2222:3333:4444::1/128")));
+ assertFalse(ipPrefix.contains(
+ IpPrefix.valueOf("1111:2222:3333:4444::/64")));
+ assertFalse(ipPrefix.contains(
+ IpPrefix.valueOf("1111:2222:3333:4445::/120")));
+ assertFalse(ipPrefix.contains(IpPrefix.valueOf("::/64")));
+ assertFalse(ipPrefix.contains(IpPrefix.valueOf("::/0")));
+ assertFalse(ipPrefix.contains(
+ IpPrefix.valueOf("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128")));
+
+ ipPrefix = IpPrefix.valueOf("::/0");
+ assertTrue(ipPrefix.contains(
+ IpPrefix.valueOf("1111:2222:3333:4444::/120")));
+ assertTrue(ipPrefix.contains(
+ IpPrefix.valueOf("1111:2222:3333:4444::/128")));
+ assertTrue(ipPrefix.contains(
+ IpPrefix.valueOf("1111:2222:3333:4444::1/128")));
+ assertTrue(ipPrefix.contains(
+ IpPrefix.valueOf("1111:2222:3333:4444::/64")));
+ assertTrue(ipPrefix.contains(
+ IpPrefix.valueOf("1111:2222:3333:4445::/120")));
+ assertTrue(ipPrefix.contains(IpPrefix.valueOf("::/64")));
+ assertTrue(ipPrefix.contains(IpPrefix.valueOf("::/0")));
+ assertTrue(ipPrefix.contains(
+ IpPrefix.valueOf("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128")));
+
+ ipPrefix =
+ IpPrefix.valueOf("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128");
+ assertFalse(ipPrefix.contains(
+ IpPrefix.valueOf("1111:2222:3333:4444::/120")));
+ assertFalse(ipPrefix.contains(
+ IpPrefix.valueOf("1111:2222:3333:4444::/128")));
+ assertFalse(ipPrefix.contains(
+ IpPrefix.valueOf("1111:2222:3333:4444::1/128")));
+ assertFalse(ipPrefix.contains(
+ IpPrefix.valueOf("1111:2222:3333:4444::/64")));
+ assertFalse(ipPrefix.contains(
+ IpPrefix.valueOf("1111:2222:3333:4445::/120")));
+ assertFalse(ipPrefix.contains(IpPrefix.valueOf("::/64")));
+ assertFalse(ipPrefix.contains(IpPrefix.valueOf("::/0")));
+ assertTrue(ipPrefix.contains(
+ IpPrefix.valueOf("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128")));
+ }
+
+ /**
+ * Tests IP prefix contains IP address for IPv4.
+ */
+ @Test
+ public void testContainsIpAddressIPv4() {
+ IpPrefix ipPrefix;
+
+ ipPrefix = IpPrefix.valueOf("1.2.0.0/24");
+ assertTrue(ipPrefix.contains(IpAddress.valueOf("1.2.0.0")));
+ assertTrue(ipPrefix.contains(IpAddress.valueOf("1.2.0.4")));
+ assertFalse(ipPrefix.contains(IpAddress.valueOf("1.3.0.0")));
+ assertFalse(ipPrefix.contains(IpAddress.valueOf("0.0.0.0")));
+ assertFalse(ipPrefix.contains(IpAddress.valueOf("255.255.255.255")));
+
+ ipPrefix = IpPrefix.valueOf("1.2.0.0/32");
+ assertTrue(ipPrefix.contains(IpAddress.valueOf("1.2.0.0")));
+ assertFalse(ipPrefix.contains(IpAddress.valueOf("1.2.0.4")));
+ assertFalse(ipPrefix.contains(IpAddress.valueOf("1.3.0.0")));
+ assertFalse(ipPrefix.contains(IpAddress.valueOf("0.0.0.0")));
+ assertFalse(ipPrefix.contains(IpAddress.valueOf("255.255.255.255")));
+
+ ipPrefix = IpPrefix.valueOf("0.0.0.0/0");
+ assertTrue(ipPrefix.contains(IpAddress.valueOf("1.2.0.0")));
+ assertTrue(ipPrefix.contains(IpAddress.valueOf("1.2.0.4")));
+ assertTrue(ipPrefix.contains(IpAddress.valueOf("1.3.0.0")));
+ assertTrue(ipPrefix.contains(IpAddress.valueOf("0.0.0.0")));
+ assertTrue(ipPrefix.contains(IpAddress.valueOf("255.255.255.255")));
+
+ ipPrefix = IpPrefix.valueOf("255.255.255.255/32");
+ assertFalse(ipPrefix.contains(IpAddress.valueOf("1.2.0.0")));
+ assertFalse(ipPrefix.contains(IpAddress.valueOf("1.2.0.4")));
+ assertFalse(ipPrefix.contains(IpAddress.valueOf("1.3.0.0")));
+ assertFalse(ipPrefix.contains(IpAddress.valueOf("0.0.0.0")));
+ assertTrue(ipPrefix.contains(IpAddress.valueOf("255.255.255.255")));
+ }
+
+ /**
+ * Tests IP prefix contains IP address for IPv6.
+ */
+ @Test
+ public void testContainsIpAddressIPv6() {
+ IpPrefix ipPrefix;
+
+ ipPrefix = IpPrefix.valueOf("1111:2222:3333:4444::/120");
+ assertTrue(ipPrefix.contains(
+ IpAddress.valueOf("1111:2222:3333:4444::")));
+ assertTrue(ipPrefix.contains(
+ IpAddress.valueOf("1111:2222:3333:4444::1")));
+ assertFalse(ipPrefix.contains(
+ IpAddress.valueOf("1111:2222:3333:4445::")));
+ assertFalse(ipPrefix.contains(IpAddress.valueOf("::")));
+ assertFalse(ipPrefix.contains(
+ IpAddress.valueOf("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff")));
+
+ ipPrefix = IpPrefix.valueOf("1111:2222:3333:4444::/128");
+ assertTrue(ipPrefix.contains(
+ IpAddress.valueOf("1111:2222:3333:4444::")));
+ assertFalse(ipPrefix.contains(
+ IpAddress.valueOf("1111:2222:3333:4444::1")));
+ assertFalse(ipPrefix.contains(
+ IpAddress.valueOf("1111:2222:3333:4445::")));
+ assertFalse(ipPrefix.contains(IpAddress.valueOf("::")));
+ assertFalse(ipPrefix.contains(
+ IpAddress.valueOf("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff")));
+
+ ipPrefix = IpPrefix.valueOf("::/0");
+ assertTrue(ipPrefix.contains(
+ IpAddress.valueOf("1111:2222:3333:4444::")));
+ assertTrue(ipPrefix.contains(
+ IpAddress.valueOf("1111:2222:3333:4444::1")));
+ assertTrue(ipPrefix.contains(
+ IpAddress.valueOf("1111:2222:3333:4445::")));
+ assertTrue(ipPrefix.contains(IpAddress.valueOf("::")));
+ assertTrue(ipPrefix.contains(
+ IpAddress.valueOf("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff")));
+
+ ipPrefix =
+ IpPrefix.valueOf("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128");
+ assertFalse(ipPrefix.contains(
+ IpAddress.valueOf("1111:2222:3333:4444::")));
+ assertFalse(ipPrefix.contains(
+ IpAddress.valueOf("1111:2222:3333:4444::1")));
+ assertFalse(ipPrefix.contains(
+ IpAddress.valueOf("1111:2222:3333:4445::")));
+ assertFalse(ipPrefix.contains(IpAddress.valueOf("::")));
+ assertTrue(ipPrefix.contains(
+ IpAddress.valueOf("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff")));
+ }
+
+ /**
+ * Tests equality of {@link IpPrefix} for IPv4.
+ */
+ @Test
+ public void testEqualityIPv4() {
+ new EqualsTester()
+ .addEqualityGroup(IpPrefix.valueOf("1.2.0.0/24"),
+ IpPrefix.valueOf("1.2.0.0/24"),
+ IpPrefix.valueOf("1.2.0.4/24"))
+ .addEqualityGroup(IpPrefix.valueOf("1.2.0.0/16"),
+ IpPrefix.valueOf("1.2.0.0/16"))
+ .addEqualityGroup(IpPrefix.valueOf("1.2.0.0/32"),
+ IpPrefix.valueOf("1.2.0.0/32"))
+ .addEqualityGroup(IpPrefix.valueOf("1.3.0.0/24"),
+ IpPrefix.valueOf("1.3.0.0/24"))
+ .addEqualityGroup(IpPrefix.valueOf("0.0.0.0/0"),
+ IpPrefix.valueOf("0.0.0.0/0"))
+ .addEqualityGroup(IpPrefix.valueOf("255.255.255.255/32"),
+ IpPrefix.valueOf("255.255.255.255/32"))
+ .testEquals();
+ }
+
+ /**
+ * Tests equality of {@link IpPrefix} for IPv6.
+ */
+ @Test
+ public void testEqualityIPv6() {
+ new EqualsTester()
+ .addEqualityGroup(
+ IpPrefix.valueOf("1111:2222:3333:4444::/120"),
+ IpPrefix.valueOf("1111:2222:3333:4444::1/120"),
+ IpPrefix.valueOf("1111:2222:3333:4444::/120"))
+ .addEqualityGroup(
+ IpPrefix.valueOf("1111:2222:3333:4444::/64"),
+ IpPrefix.valueOf("1111:2222:3333:4444::/64"))
+ .addEqualityGroup(
+ IpPrefix.valueOf("1111:2222:3333:4444::/128"),
+ IpPrefix.valueOf("1111:2222:3333:4444::/128"))
+ .addEqualityGroup(
+ IpPrefix.valueOf("1111:2222:3333:4445::/64"),
+ IpPrefix.valueOf("1111:2222:3333:4445::/64"))
+ .addEqualityGroup(
+ IpPrefix.valueOf("::/0"),
+ IpPrefix.valueOf("::/0"))
+ .addEqualityGroup(
+ IpPrefix.valueOf("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128"),
+ IpPrefix.valueOf("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128"))
+ .testEquals();
+ }
+
+ /**
+ * Tests object string representation for IPv4.
+ */
+ @Test
+ public void testToStringIPv4() {
+ IpPrefix ipPrefix;
+
+ ipPrefix = IpPrefix.valueOf("1.2.3.0/24");
+ assertThat(ipPrefix.toString(), is("1.2.3.0/24"));
+
+ ipPrefix = IpPrefix.valueOf("1.2.3.4/24");
+ assertThat(ipPrefix.toString(), is("1.2.3.0/24"));
+
+ ipPrefix = IpPrefix.valueOf("0.0.0.0/0");
+ assertThat(ipPrefix.toString(), is("0.0.0.0/0"));
+
+ ipPrefix = IpPrefix.valueOf("255.255.255.255/32");
+ assertThat(ipPrefix.toString(), is("255.255.255.255/32"));
+ }
+
+ /**
+ * Tests object string representation for IPv6.
+ */
+ @Test
+ public void testToStringIPv6() {
+ IpPrefix ipPrefix;
+
+ ipPrefix = IpPrefix.valueOf("1100::/8");
+ assertThat(ipPrefix.toString(), is("1100::/8"));
+
+ ipPrefix = IpPrefix.valueOf("1111:2222:3333:4444:5555:6666:7777:8885/8");
+ assertThat(ipPrefix.toString(), is("1100::/8"));
+
+ ipPrefix = IpPrefix.valueOf("::/0");
+ assertThat(ipPrefix.toString(), is("::/0"));
+
+ ipPrefix = IpPrefix.valueOf("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128");
+ assertThat(ipPrefix.toString(),
+ is("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128"));
}
}
diff --git a/utils/netty/pom.xml b/utils/netty/pom.xml
index fd0e8e0..436214f 100644
--- a/utils/netty/pom.xml
+++ b/utils/netty/pom.xml
@@ -73,6 +73,7 @@
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-transport-native-epoll</artifactId>
+ <version>${netty4.version}</version>
</dependency>
</dependencies>
diff --git a/utils/netty/src/main/java/org/onlab/netty/MessageDecoder.java b/utils/netty/src/main/java/org/onlab/netty/MessageDecoder.java
index c957153..51f8dd3 100644
--- a/utils/netty/src/main/java/org/onlab/netty/MessageDecoder.java
+++ b/utils/netty/src/main/java/org/onlab/netty/MessageDecoder.java
@@ -50,7 +50,7 @@
ByteBuf buffer,
List<Object> out) throws Exception {
- switch(state()) {
+ switch (state()) {
case READ_HEADER_VERSION:
int headerVersion = buffer.readInt();
checkState(headerVersion == MessageEncoder.HEADER_VERSION, "Unexpected header version");
diff --git a/utils/netty/src/main/java/org/onlab/netty/MessageEncoder.java b/utils/netty/src/main/java/org/onlab/netty/MessageEncoder.java
index d4b82cf..fe89039 100644
--- a/utils/netty/src/main/java/org/onlab/netty/MessageEncoder.java
+++ b/utils/netty/src/main/java/org/onlab/netty/MessageEncoder.java
@@ -15,6 +15,8 @@
*/
package org.onlab.netty;
+import java.nio.charset.StandardCharsets;
+
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -32,7 +34,7 @@
private final Logger log = LoggerFactory.getLogger(getClass());
// onosiscool in ascii
- public static final byte[] PREAMBLE = "onosiscool".getBytes();
+ static final byte[] PREAMBLE = "onosiscool".getBytes(StandardCharsets.US_ASCII);
public static final int HEADER_VERSION = 1;
public static final int SERIALIZER_VERSION = 1;
diff --git a/utils/netty/src/main/java/org/onlab/netty/MessagingService.java b/utils/netty/src/main/java/org/onlab/netty/MessagingService.java
index 8c4f4ee..9277448 100644
--- a/utils/netty/src/main/java/org/onlab/netty/MessagingService.java
+++ b/utils/netty/src/main/java/org/onlab/netty/MessagingService.java
@@ -29,7 +29,7 @@
* @param ep end point to send the message to.
* @param type type of message.
* @param payload message payload bytes.
- * @throws IOException
+ * @throws IOException when I/O exception of some sort has occurred
*/
public void sendAsync(Endpoint ep, String type, byte[] payload) throws IOException;
@@ -39,7 +39,7 @@
* @param type type of message.
* @param payload message payload.
* @return a response future
- * @throws IOException
+ * @throws IOException when I/O exception of some sort has occurred
*/
public ListenableFuture<byte[]> sendAndReceive(Endpoint ep, String type, byte[] payload) throws IOException;
diff --git a/utils/nio/src/main/java/org/onlab/nio/IOLoop.java b/utils/nio/src/main/java/org/onlab/nio/IOLoop.java
index 9d417ac..106df7b 100644
--- a/utils/nio/src/main/java/org/onlab/nio/IOLoop.java
+++ b/utils/nio/src/main/java/org/onlab/nio/IOLoop.java
@@ -107,6 +107,7 @@
* Completes connection request pending on the given selection key.
*
* @param key selection key holding the pending connect operation.
+ * @throws IOException when I/O exception of some sort has occurred
*/
protected void connect(SelectionKey key) throws IOException {
SocketChannel ch = (SocketChannel) key.channel();
diff --git a/utils/nio/src/main/java/org/onlab/nio/SelectorLoop.java b/utils/nio/src/main/java/org/onlab/nio/SelectorLoop.java
index 4f3220d..605e0a9 100644
--- a/utils/nio/src/main/java/org/onlab/nio/SelectorLoop.java
+++ b/utils/nio/src/main/java/org/onlab/nio/SelectorLoop.java
@@ -74,6 +74,7 @@
/**
* Indicates that the loop is marked to run.
+ * @return true if the loop is marked to run
*/
protected boolean isRunning() {
return state == State.STARTED || state == State.STARTING;
diff --git a/utils/thirdparty/src/main/java/org/onlab/thirdparty/OnlabThirdparty.java b/utils/thirdparty/src/main/java/org/onlab/thirdparty/OnlabThirdparty.java
index 9c9b03f..7405558 100644
--- a/utils/thirdparty/src/main/java/org/onlab/thirdparty/OnlabThirdparty.java
+++ b/utils/thirdparty/src/main/java/org/onlab/thirdparty/OnlabThirdparty.java
@@ -18,7 +18,7 @@
/**
* Empty class required to get the onlab-thirdparty module to build properly.
- * <p/>
+ *
* TODO Figure out how to remove this.
*/
public class OnlabThirdparty {
diff --git a/web/gui/src/main/java/org/onlab/onos/gui/TopologyResource.java b/web/gui/src/main/java/org/onlab/onos/gui/TopologyResource.java
index 14795b2..b63e3e0 100644
--- a/web/gui/src/main/java/org/onlab/onos/gui/TopologyResource.java
+++ b/web/gui/src/main/java/org/onlab/onos/gui/TopologyResource.java
@@ -101,7 +101,7 @@
new Prop("Vendor", device.manufacturer()),
new Prop("H/W Version", device.hwVersion()),
new Prop("S/W Version", device.swVersion()),
- new Prop("S/W Version", device.serialNumber()),
+ new Prop("Serial Number", device.serialNumber()),
new Separator(),
new Prop("Latitude", annot.value("latitude")),
new Prop("Longitude", annot.value("longitude")),
diff --git a/web/gui/src/main/webapp/geometry2.js b/web/gui/src/main/webapp/geometry2.js
new file mode 100644
index 0000000..5ede643
--- /dev/null
+++ b/web/gui/src/main/webapp/geometry2.js
@@ -0,0 +1,123 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ Geometry library - based on work by Mike Bostock.
+ */
+
+(function() {
+
+ if (typeof geo == 'undefined') {
+ geo = {};
+ }
+
+ var tolerance = 1e-10;
+
+ function eq(a, b) {
+ return (Math.abs(a - b) < tolerance);
+ }
+
+ function gt(a, b) {
+ return (a - b > -tolerance);
+ }
+
+ function lt(a, b) {
+ return gt(b, a);
+ }
+
+ geo.eq = eq;
+ geo.gt = gt;
+ geo.lt = lt;
+
+ geo.LineSegment = function(x1, y1, x2, y2) {
+ this.x1 = x1;
+ this.y1 = y1;
+ this.x2 = x2;
+ this.y2 = y2;
+
+ // Ax + By = C
+ this.a = y2 - y1;
+ this.b = x1 - x2;
+ this.c = x1 * this.a + y1 * this.b;
+
+ if (eq(this.a, 0) && eq(this.b, 0)) {
+ throw new Error(
+ 'Cannot construct a LineSegment with two equal endpoints.');
+ }
+ };
+
+ geo.LineSegment.prototype.intersect = function(that) {
+ var d = (this.x1 - this.x2) * (that.y1 - that.y2) -
+ (this.y1 - this.y2) * (that.x1 - that.x2);
+
+ if (eq(d, 0)) {
+ // The two lines are parallel or very close.
+ return {
+ x : NaN,
+ y : NaN
+ };
+ }
+
+ var t1 = this.x1 * this.y2 - this.y1 * this.x2,
+ t2 = that.x1 * that.y2 - that.y1 * that.x2,
+ x = (t1 * (that.x1 - that.x2) - t2 * (this.x1 - this.x2)) / d,
+ y = (t1 * (that.y1 - that.y2) - t2 * (this.y1 - this.y2)) / d,
+ in1 = (gt(x, Math.min(this.x1, this.x2)) && lt(x, Math.max(this.x1, this.x2)) &&
+ gt(y, Math.min(this.y1, this.y2)) && lt(y, Math.max(this.y1, this.y2))),
+ in2 = (gt(x, Math.min(that.x1, that.x2)) && lt(x, Math.max(that.x1, that.x2)) &&
+ gt(y, Math.min(that.y1, that.y2)) && lt(y, Math.max(that.y1, that.y2)));
+
+ return {
+ x : x,
+ y : y,
+ in1 : in1,
+ in2 : in2
+ };
+ };
+
+ geo.LineSegment.prototype.x = function(y) {
+ // x = (C - By) / a;
+ if (this.a) {
+ return (this.c - this.b * y) / this.a;
+ } else {
+ // a == 0 -> horizontal line
+ return NaN;
+ }
+ };
+
+ geo.LineSegment.prototype.y = function(x) {
+ // y = (C - Ax) / b;
+ if (this.b) {
+ return (this.c - this.a * x) / this.b;
+ } else {
+ // b == 0 -> vertical line
+ return NaN;
+ }
+ };
+
+ geo.LineSegment.prototype.length = function() {
+ return Math.sqrt(
+ (this.y2 - this.y1) * (this.y2 - this.y1) +
+ (this.x2 - this.x1) * (this.x2 - this.x1));
+ };
+
+ geo.LineSegment.prototype.offset = function(x, y) {
+ return new geo.LineSegment(
+ this.x1 + x, this.y1 + y,
+ this.x2 + x, this.y2 + y);
+ };
+
+})();
diff --git a/web/gui/src/main/webapp/index2.html b/web/gui/src/main/webapp/index2.html
new file mode 100644
index 0000000..1ddc318
--- /dev/null
+++ b/web/gui/src/main/webapp/index2.html
@@ -0,0 +1,98 @@
+<!DOCTYPE html>
+<!--
+ ~ Copyright 2014 Open Networking Laboratory
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<!--
+ ONOS UI - single page web app
+ Version 1.1
+
+ @author Simon Hunt
+ -->
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>ONOS GUI (v1.1)</title>
+
+ <link rel="shortcut icon" href="img/onos-logo.png">
+
+ <!-- first script to be run -->
+ <script src="preamble.js"></script>
+
+ <!-- Third party library code included here -->
+ <!--TODO: use the minified version of d3, once debugging is complete -->
+ <script src="libs/d3.js"></script>
+ <script src="libs/jquery-2.1.1.min.js"></script>
+
+ <!-- Base library and framework stylesheets included here -->
+ <link rel="stylesheet" href="base.css">
+ <link rel="stylesheet" href="onos2.css">
+ <link rel="stylesheet" href="mast2.css">
+
+ <!-- This is where contributed stylesheets get INJECTED -->
+ <!-- TODO: replace with template marker and inject refs server-side -->
+ <link rel="stylesheet" href="topo2.css">
+
+
+ <!-- General library modules included here-->
+ <script src="geometry2.js"></script>
+
+ <!-- ONOS UI Framework included here-->
+ <script src="onos2.js"></script>
+
+</head>
+<body>
+ <div id="frame">
+ <div id="mast">
+ <!-- NOTE: masthead injected here by mast.js -->
+ </div>
+ <div id="view">
+ <!-- NOTE: views injected here by onos.js -->
+ </div>
+ <div id="overlays">
+ <!-- NOTE: overlays injected here, as needed -->
+ </div>
+ </div>
+
+ <!-- Initialize the UI...-->
+ <script type="text/javascript">
+ var ONOS = $.onos({
+ comment: "configuration options",
+ startVid: 'topo',
+// startVid: 'sampleKeys',
+ trace: false
+ });
+ </script>
+
+ <!-- Framework module files included here -->
+ <script src="mast2.js"></script>
+
+ <!-- Sample views; can be dispensed with eventually -->
+ <script src="sample2.js"></script>
+ <script src="sampleAlt2.js"></script>
+ <script src="sampleRadio.js"></script>
+ <script src="sampleKeys.js"></script>
+
+ <!-- Contributed (application) views injected here -->
+ <!-- TODO: replace with template marker and inject refs server-side -->
+ <script src="topo2.js"></script>
+
+ <!-- finally, build the UI-->
+ <script type="text/javascript">
+ $(ONOS.buildUi);
+ </script>
+
+</body>
+</html>
diff --git a/web/gui/src/main/webapp/mast2.css b/web/gui/src/main/webapp/mast2.css
new file mode 100644
index 0000000..7f094b3
--- /dev/null
+++ b/web/gui/src/main/webapp/mast2.css
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ ONOS GUI -- Masthead -- CSS file
+
+ @author Simon Hunt
+ */
+
+#mast {
+ height: 36px;
+ padding: 4px;
+ background-color: #bbb;
+ vertical-align: baseline;
+ box-shadow: 0px 2px 8px #777;
+}
+
+#mast img#logo {
+ height: 38px;
+ padding-left: 8px;
+ padding-right: 8px;
+}
+
+#mast span.title {
+ color: #369;
+ font-size: 14pt;
+ font-style: italic;
+ vertical-align: 12px;
+}
+
+#mast span.right {
+ padding-top: 8px;
+ padding-right: 16px;
+ float: right;
+}
+
+#mast span.radio {
+ font-size: 10pt;
+ margin: 4px 2px;
+ border: 1px dotted #222;
+ padding: 1px 6px;
+ color: #eee;
+ cursor: pointer;
+}
+
+#mast span.radio.active {
+ background-color: #bbb;
+ border: 1px solid #eee;
+ padding: 1px 6px;
+ color: #666;
+ font-weight: bold;
+}
diff --git a/web/gui/src/main/webapp/mast2.js b/web/gui/src/main/webapp/mast2.js
new file mode 100644
index 0000000..169dd35
--- /dev/null
+++ b/web/gui/src/main/webapp/mast2.js
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ ONOS GUI -- Masthead
+
+ Defines the masthead for the UI. Injects logo and title, as well as providing
+ the placeholder for a set of radio buttons.
+
+ @author Simon Hunt
+ */
+
+(function (onos){
+ 'use strict';
+
+ // API's
+ var api = onos.api;
+
+ // Config variables
+ var guiTitle = 'Open Networking Operating System';
+
+ // DOM elements and the like
+ var mast = d3.select('#mast');
+
+ mast.append('img')
+ .attr({
+ id: 'logo',
+ src: 'img/onos-logo.png'
+ });
+
+ mast.append('span')
+ .attr({
+ class: 'title'
+ })
+ .text(guiTitle);
+
+ mast.append('span')
+ .attr({
+ id: 'mastRadio',
+ class: 'right'
+ });
+
+}(ONOS));
\ No newline at end of file
diff --git a/web/gui/src/main/webapp/module-div-template.js b/web/gui/src/main/webapp/module-div-template.js
new file mode 100644
index 0000000..09ea569
--- /dev/null
+++ b/web/gui/src/main/webapp/module-div-template.js
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ Module template file for DIV based view.
+
+ @author Simon Hunt
+ */
+
+(function (onos) {
+ 'use strict';
+
+ var list,
+ data = [ 'foo', 'bar', 'baz' ];
+
+ // invoked only the first time the view is loaded
+ function preload(view, ctx) {
+ // NOTE: view.$div is a D3 selection of the view's div
+ list = view.$div.append('ul');
+ }
+
+ // invoked just prior to loading the view
+ function reset(view) {
+
+ }
+
+ // invoked when the view is loaded
+ function load(view, ctx) {
+ list.selectAll('li')
+ .data(data)
+ .enter()
+ .append('li')
+ .text(function (d) { return d; })
+ }
+
+ // invoked when the view is resized
+ function resize(view, ctx) {
+
+ }
+
+ // == register the view here, with links to lifecycle callbacks
+
+ onos.ui.addView('myViewId', {
+ preload: preload,
+ reset: reset,
+ load: load,
+ // unload: unload,
+ // error: error
+ resize: resize
+ });
+
+}(ONOS));
diff --git a/web/gui/src/main/webapp/module-svg-template.js b/web/gui/src/main/webapp/module-svg-template.js
new file mode 100644
index 0000000..66da760
--- /dev/null
+++ b/web/gui/src/main/webapp/module-svg-template.js
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ Module template file for SVG based view.
+
+ @author Simon Hunt
+ */
+
+(function (onos) {
+ 'use strict';
+
+ var svg;
+
+ // set the size of the SVG layer to match that of the view
+ function sizeSvg(view) {
+ svg.attr({
+ width: view.width(),
+ height: view.height()
+ });
+ }
+
+ // invoked only the first time the view is loaded
+ function preload(view, ctx) {
+ // NOTE: view.$div is a D3 selection of the view's div
+ svg = view.$div.append('svg');
+ sizeSvg(view);
+ // ... further code to initialize the SVG view ...
+
+ }
+
+ // invoked just prior to loading the view
+ function reset(view) {
+
+ }
+
+ // invoked when the view is loaded
+ function load(view, ctx) {
+
+ }
+
+ // invoked when the view is resized
+ function resize(view, ctx) {
+ sizeSvg(view);
+ // ... further code to handle resize of view ...
+
+ }
+
+ // == register the view here, with links to lifecycle callbacks
+
+ onos.ui.addView('myViewId', {
+ preload: preload,
+ reset: reset,
+ load: load,
+ // unload: unload,
+ // error: error
+ resize: resize
+ });
+
+}(ONOS));
diff --git a/web/gui/src/main/webapp/network.js b/web/gui/src/main/webapp/network.js
index b249c09..01e4acd 100644
--- a/web/gui/src/main/webapp/network.js
+++ b/web/gui/src/main/webapp/network.js
@@ -28,7 +28,7 @@
// configuration data
var config = {
- useLiveData: true,
+ useLiveData: false,
debugOn: false,
debug: {
showNodeXY: false,
diff --git a/web/gui/src/main/webapp/onos2.css b/web/gui/src/main/webapp/onos2.css
new file mode 100644
index 0000000..f693ecc
--- /dev/null
+++ b/web/gui/src/main/webapp/onos2.css
@@ -0,0 +1,217 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ ONOS GUI -- Base Framework -- CSS file
+
+ @author Simon Hunt
+ */
+
+html, body {
+ height: 100%;
+}
+
+div.onosView {
+ display: none;
+}
+
+div.onosView.currentView {
+ display: block;
+}
+
+/*
+ * ==============================================================
+ * END OF NEW ONOS.JS file
+ * ==============================================================
+ */
+
+/*
+ * === DEBUGGING ======
+ */
+svg {
+ /*border: 1px dashed red;*/
+}
+
+svg #bg {
+ opacity: 0.5;
+}
+
+
+/*
+ * Network Graph elements ======================================
+ */
+
+svg .link {
+ fill: none;
+ stroke: #666;
+ stroke-width: 2.0px;
+ opacity: .7;
+
+ transition: opacity 250ms;
+ -webkit-transition: opacity 250ms;
+ -moz-transition: opacity 250ms;
+}
+
+svg .link.host {
+ stroke: #666;
+ stroke-width: 1px;
+}
+
+svg g.portLayer rect.port {
+ fill: #ccc;
+}
+
+svg g.portLayer text {
+ font: 8pt sans-serif;
+ pointer-events: none;
+}
+
+svg .node.device rect {
+ stroke-width: 1.5px;
+
+ transition: opacity 250ms;
+ -webkit-transition: opacity 250ms;
+ -moz-transition: opacity 250ms;
+}
+
+svg .node.device.fixed rect {
+ stroke-width: 1.5;
+ stroke: #ccc;
+}
+
+svg .node.device.roadm rect {
+ fill: #03c;
+}
+
+svg .node.device.switch rect {
+ fill: #06f;
+}
+
+svg .node.host circle {
+ fill: #c96;
+ stroke: #000;
+}
+
+svg .node text {
+ fill: white;
+ font: 10pt sans-serif;
+ pointer-events: none;
+}
+
+/* for debugging */
+svg .node circle.debug {
+ fill: white;
+ stroke: red;
+}
+svg .node rect.debug {
+ fill: yellow;
+ stroke: red;
+ opacity: 0.35;
+}
+
+
+svg .node.selected rect,
+svg .node.selected circle {
+ filter: url(#blue-glow);
+}
+
+svg .link.inactive,
+svg .port.inactive,
+svg .portText.inactive,
+svg .node.inactive rect,
+svg .node.inactive circle,
+svg .node.inactive text,
+svg .node.inactive image {
+ opacity: .1;
+}
+
+svg .node.inactive.selected rect,
+svg .node.inactive.selected text,
+svg .node.inactive.selected image {
+ opacity: .6;
+}
+
+/*
+ * =============================================================
+ */
+
+/*
+ * Specific structural elements
+ */
+
+/* This is to ensure that the body does not expand to account for the
+ flyout details pane, that is positioned "off screen".
+ */
+body {
+ overflow: hidden;
+}
+
+
+#frame {
+ width: 100%;
+ height: 100%;
+ background-color: #fff;
+}
+
+#flyout {
+ position: absolute;
+ z-index: 100;
+ display: block;
+ top: 10%;
+ width: 280px;
+ right: -300px;
+ opacity: 0;
+ background-color: rgba(255,255,255,0.8);
+
+ padding: 10px;
+ color: black;
+ font-size: 10pt;
+ box-shadow: 2px 2px 16px #777;
+}
+
+#flyout h2 {
+ margin: 8px 4px;
+ color: black;
+ vertical-align: middle;
+}
+
+#flyout h2 img {
+ height: 32px;
+ padding-right: 8px;
+ vertical-align: middle;
+}
+
+#flyout p, table {
+ margin: 4px 4px;
+}
+
+#flyout td.label {
+ font-style: italic;
+ color: #777;
+ padding-right: 12px;
+}
+
+#flyout td.value {
+
+}
+
+#flyout hr {
+ height: 1px;
+ color: #ccc;
+ background-color: #ccc;
+ border: 0;
+}
+
diff --git a/web/gui/src/main/webapp/onos2.js b/web/gui/src/main/webapp/onos2.js
new file mode 100644
index 0000000..f509757
--- /dev/null
+++ b/web/gui/src/main/webapp/onos2.js
@@ -0,0 +1,612 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ ONOS GUI -- Base Framework
+
+ @author Simon Hunt
+ */
+
+(function ($) {
+ 'use strict';
+ var tsI = new Date().getTime(), // initialize time stamp
+ tsB, // build time stamp
+ mastHeight = 36, // see mast2.css
+ defaultVid = 'sample';
+
+
+ // attach our main function to the jQuery object
+ $.onos = function (options) {
+ var uiApi,
+ viewApi,
+ navApi;
+
+ var defaultOptions = {
+ trace: false,
+ startVid: defaultVid
+ };
+
+ // compute runtime settings
+ var settings = $.extend({}, defaultOptions, options);
+
+ // internal state
+ var views = {},
+ current = {
+ view: null,
+ ctx: ''
+ },
+ built = false,
+ errorCount = 0,
+ keyHandler = {};
+
+ // DOM elements etc.
+ var $view,
+ $mastRadio;
+
+
+ function whatKey(code) {
+ switch (code) {
+ case 13: return 'enter';
+ case 16: return 'shift';
+ case 17: return 'ctrl';
+ case 18: return 'alt';
+ case 27: return 'esc';
+ case 32: return 'space';
+ case 37: return 'leftArrow';
+ case 38: return 'upArrow';
+ case 39: return 'rightArrow';
+ case 40: return 'downArrow';
+ case 91: return 'cmdLeft';
+ case 93: return 'cmdRight';
+ default:
+ if ((code >= 48 && code <= 57) ||
+ (code >= 65 && code <= 90)) {
+ return String.fromCharCode(code);
+ } else if (code >= 112 && code <= 123) {
+ return 'F' + (code - 111);
+ }
+ return '.';
+ }
+ }
+
+
+ // ..........................................................
+ // Internal functions
+
+ // throw an error
+ function throwError(msg) {
+ // separate function, as we might add tracing here too, later
+ throw new Error(msg);
+ }
+
+ function doError(msg) {
+ errorCount++;
+ console.error(msg);
+ }
+
+ function trace(msg) {
+ if (settings.trace) {
+ console.log(msg);
+ }
+ }
+
+ function traceFn(fn, params) {
+ if (settings.trace) {
+ console.log('*FN* ' + fn + '(...): ' + params);
+ }
+ }
+
+ // hash navigation
+ function hash() {
+ var hash = window.location.hash,
+ redo = false,
+ view,
+ t;
+
+ traceFn('hash', hash);
+
+ if (!hash) {
+ hash = settings.startVid;
+ redo = true;
+ }
+
+ t = parseHash(hash);
+ if (!t || !t.vid) {
+ doError('Unable to parse target hash: ' + hash);
+ }
+
+ view = views[t.vid];
+ if (!view) {
+ doError('No view defined with id: ' + t.vid);
+ }
+
+ if (redo) {
+ window.location.hash = makeHash(t);
+ // the above will result in a hashchange event, invoking
+ // this function again
+ } else {
+ // hash was not modified... navigate to where we need to be
+ navigate(hash, view, t);
+ }
+ }
+
+ function parseHash(s) {
+ // extract navigation coordinates from the supplied string
+ // "vid,ctx" --> { vid:vid, ctx:ctx }
+ traceFn('parseHash', s);
+
+ var m = /^[#]{0,1}(\S+),(\S*)$/.exec(s);
+ if (m) {
+ return { vid: m[1], ctx: m[2] };
+ }
+
+ m = /^[#]{0,1}(\S+)$/.exec(s);
+ return m ? { vid: m[1] } : null;
+ }
+
+ function makeHash(t, ctx) {
+ traceFn('makeHash');
+ // make a hash string from the given navigation coordinates.
+ // if t is not an object, then it is a vid
+ var h = t,
+ c = ctx || '';
+
+ if ($.isPlainObject(t)) {
+ h = t.vid;
+ c = t.ctx || '';
+ }
+
+ if (c) {
+ h += ',' + c;
+ }
+ trace('hash = "' + h + '"');
+ return h;
+ }
+
+ function navigate(hash, view, t) {
+ traceFn('navigate', view.vid);
+ // closePanes() // flyouts etc.
+ // updateNav() // accordion / selected nav item etc.
+ createView(view);
+ setView(view, hash, t);
+ }
+
+ function reportBuildErrors() {
+ traceFn('reportBuildErrors');
+ // TODO: validate registered views / nav-item linkage etc.
+ console.log('(no build errors)');
+ }
+
+ // returns the reference if it is a function, null otherwise
+ function isF(f) {
+ return $.isFunction(f) ? f : null;
+ }
+
+ // ..........................................................
+ // View life-cycle functions
+
+ function setViewDimensions(sel) {
+ var w = window.innerWidth,
+ h = window.innerHeight - mastHeight;
+ sel.each(function () {
+ $(this)
+ .css('width', w + 'px')
+ .css('height', h + 'px')
+ });
+ }
+
+ function createView(view) {
+ var $d;
+
+ // lazy initialization of the view
+ if (view && !view.$div) {
+ trace('creating view for ' + view.vid);
+ $d = $view.append('div')
+ .attr({
+ id: view.vid,
+ class: 'onosView'
+ });
+ setViewDimensions($d);
+ view.$div = $d; // cache a reference to the D3 selection
+ }
+ }
+
+ function setView(view, hash, t) {
+ traceFn('setView', view.vid);
+ // set the specified view as current, while invoking the
+ // appropriate life-cycle callbacks
+
+ // if there is a current view, and it is not the same as
+ // the incoming view, then unload it...
+ if (current.view && (current.view.vid !== view.vid)) {
+ current.view.unload();
+
+ // detach radio buttons, key handlers, etc.
+ $('#mastRadio').children().detach();
+ keyHandler.fn = null;
+ keyHandler.map = {};
+ }
+
+ // cache new view and context
+ current.view = view;
+ current.ctx = t.ctx || '';
+
+ // preload is called only once, after the view is in the DOM
+ if (!view.preloaded) {
+ view.preload(current.ctx);
+ view.preloaded = true;
+ }
+
+ // clear the view of stale data
+ view.reset();
+
+ // load the view
+ view.load(current.ctx);
+ }
+
+ // generate 'unique' id by prefixing view id
+ function uid(view, id) {
+ return view.vid + '-' + id;
+ }
+
+ // restore id by removing view id prefix
+ function unUid(view, uid) {
+ var re = new RegExp('^' + view.vid + '-');
+ return uid.replace(re, '');
+ }
+
+ function setRadioButtons(vid, btnSet, callback) {
+ var view = views[vid],
+ btnG;
+
+ // lazily create the buttons...
+ if (!(btnG = view.radioButtons)) {
+ btnG = d3.select(document.createElement('div'));
+
+ btnSet.forEach(function (btn, i) {
+ var bid = btn.id || 'b' + i,
+ txt = btn.text || 'Button #' + i,
+ b = btnG.append('span')
+ .attr({
+ id: uid(view, bid),
+ class: 'radio'
+ })
+ .text(txt);
+ if (i === 0) {
+ b.classed('active', true);
+ }
+ });
+
+ btnG.selectAll('span')
+ .on('click', function (d) {
+ var btn = d3.select(this),
+ bid = btn.attr('id'),
+ act = btn.classed('active');
+
+ if (!act) {
+ $mastRadio.selectAll('span')
+ .classed('active', false);
+ btn.classed('active', true);
+
+ callback(view.token(), unUid(view, bid));
+ }
+ });
+
+ view.radioButtons = btnG;
+ }
+
+ // attach the buttons to the masthead
+ $mastRadio.node().appendChild(btnG.node());
+ }
+
+ function setKeyBindings(keyArg) {
+ if ($.isFunction(keyArg)) {
+ // set general key handler callback
+ keyHandler.fn = keyArg;
+ } else {
+ // set specific key filter map
+ keyHandler.map = keyArg;
+ }
+ }
+
+ function keyIn() {
+ var event = d3.event,
+ keyCode = event.keyCode,
+ key = whatKey(keyCode),
+ cb = isF(keyHandler.map[key]) || isF(keyHandler.fn);
+
+ if (cb) {
+ cb(current.view.token(), key, keyCode, event);
+ }
+ }
+
+ function resize(e) {
+ d3.selectAll('.onosView').call(setViewDimensions);
+ // allow current view to react to resize event...
+ if (current.view) {
+ current.view.resize(current.ctx);
+ }
+ }
+
+ // ..........................................................
+ // View class
+ // Captures state information about a view.
+
+ // Constructor
+ // vid : view id
+ // nid : id of associated nav-item (optional)
+ // cb : callbacks (preload, reset, load, unload, resize, error)
+ function View(vid) {
+ var av = 'addView(): ',
+ args = Array.prototype.slice.call(arguments),
+ nid,
+ cb;
+
+ args.shift(); // first arg is always vid
+ if (typeof args[0] === 'string') { // nid specified
+ nid = args.shift();
+ }
+ cb = args.shift();
+
+ this.vid = vid;
+
+ if (validateViewArgs(vid)) {
+ this.nid = nid; // explicit navitem id (can be null)
+ this.cb = $.isPlainObject(cb) ? cb : {}; // callbacks
+ this.$div = null; // view not yet added to DOM
+ this.radioButtons = null; // no radio buttons yet
+ this.ok = true; // valid view
+ }
+ }
+
+ function validateViewArgs(vid) {
+ var av = "ui.addView(...): ",
+ ok = false;
+ if (typeof vid !== 'string' || !vid) {
+ doError(av + 'vid required');
+ } else if (views[vid]) {
+ doError(av + 'View ID "' + vid + '" already exists');
+ } else {
+ ok = true;
+ }
+ return ok;
+ }
+
+ var viewInstanceMethods = {
+ token: function () {
+ return {
+ // attributes
+ vid: this.vid,
+ nid: this.nid,
+ $div: this.$div,
+
+ // functions
+ width: this.width,
+ height: this.height,
+ uid: this.uid,
+ setRadio: this.setRadio,
+ setKeys: this.setKeys
+ }
+ },
+
+ preload: function (ctx) {
+ var c = ctx || '',
+ fn = isF(this.cb.preload);
+ traceFn('View.preload', this.vid + ', ' + c);
+ if (fn) {
+ trace('PRELOAD cb for ' + this.vid);
+ fn(this.token(), c);
+ }
+ },
+
+ reset: function () {
+ var fn = isF(this.cb.reset);
+ traceFn('View.reset', this.vid);
+ if (fn) {
+ trace('RESET cb for ' + this.vid);
+ fn(this.token());
+ } else if (this.cb.reset === true) {
+ // boolean true signifies "clear view"
+ trace(' [true] cleaing view...');
+ viewApi.empty();
+ }
+ },
+
+ load: function (ctx) {
+ var c = ctx || '',
+ fn = isF(this.cb.load);
+ traceFn('View.load', this.vid + ', ' + c);
+ this.$div.classed('currentView', true);
+ // TODO: add radio button set, if needed
+ if (fn) {
+ trace('LOAD cb for ' + this.vid);
+ fn(this.token(), c);
+ }
+ },
+
+ unload: function () {
+ var fn = isF(this.cb.unload);
+ traceFn('View.unload', this.vid);
+ this.$div.classed('currentView', false);
+ // TODO: remove radio button set, if needed
+ if (fn) {
+ trace('UNLOAD cb for ' + this.vid);
+ fn(this.token());
+ }
+ },
+
+ resize: function (ctx) {
+ var c = ctx || '',
+ fn = isF(this.cb.resize),
+ w = this.width(),
+ h = this.height();
+ traceFn('View.resize', this.vid + '/' + c +
+ ' [' + w + 'x' + h + ']');
+ if (fn) {
+ trace('RESIZE cb for ' + this.vid);
+ fn(this.token(), c);
+ }
+ },
+
+ error: function (ctx) {
+ var c = ctx || '',
+ fn = isF(this.cb.error);
+ traceFn('View.error', this.vid + ', ' + c);
+ if (fn) {
+ trace('ERROR cb for ' + this.vid);
+ fn(this.token(), c);
+ }
+ },
+
+ width: function () {
+ return $(this.$div.node()).width();
+ },
+
+ height: function () {
+ return $(this.$div.node()).height();
+ },
+
+ setRadio: function (btnSet, cb) {
+ setRadioButtons(this.vid, btnSet, cb);
+ },
+
+ setKeys: function (keyArg) {
+ setKeyBindings(keyArg);
+ },
+
+ uid: function (id) {
+ return uid(this, id);
+ }
+
+ // TODO: consider schedule, clearTimer, etc.
+ };
+
+ // attach instance methods to the view prototype
+ $.extend(View.prototype, viewInstanceMethods);
+
+ // ..........................................................
+ // UI API
+
+ uiApi = {
+ /** @api ui addView( vid, nid, cb )
+ * Adds a view to the UI.
+ * <p>
+ * Views are loaded/unloaded into the view content pane at
+ * appropriate times, by the navigation framework. This method
+ * adds a view to the UI and returns a token object representing
+ * the view. A view's token is always passed as the first
+ * argument to each of the view's life-cycle callback functions.
+ * <p>
+ * Note that if the view is directly referenced by a nav-item,
+ * or in a group of views with one of those views referenced by
+ * a nav-item, then the <i>nid</i> argument can be omitted as
+ * the framework can infer it.
+ * <p>
+ * <i>cb</i> is a plain object containing callback functions:
+ * "preload", "reset", "load", "unload", "resize", "error".
+ * <pre>
+ * function myLoad(view, ctx) { ... }
+ * ...
+ * // short form...
+ * onos.ui.addView('viewId', {
+ * load: myLoad
+ * });
+ * </pre>
+ *
+ * @param vid (string) [*] view ID (a unique DOM element id)
+ * @param nid (string) nav-item ID (a unique DOM element id)
+ * @param cb (object) [*] callbacks object
+ * @return the view token
+ */
+ addView: function (vid, nid, cb) {
+ traceFn('addView', vid);
+ var view = new View(vid, nid, cb),
+ token;
+ if (view.ok) {
+ views[vid] = view;
+ token = view.token();
+ } else {
+ token = { vid: view.vid, bad: true };
+ }
+ return token;
+ }
+ };
+
+ // ..........................................................
+ // View API
+
+ viewApi = {
+ /** @api view empty( )
+ * Empties the current view.
+ * <p>
+ * More specifically, removes all DOM elements from the
+ * current view's display div.
+ */
+ empty: function () {
+ if (!current.view) {
+ return;
+ }
+ current.view.$div.html('');
+ }
+ };
+
+ // ..........................................................
+ // Nav API
+ navApi = {
+
+ };
+
+ // ..........................................................
+ // Exported API
+
+ // function to be called from index.html to build the ONOS UI
+ function buildOnosUi() {
+ tsB = new Date().getTime();
+ tsI = tsB - tsI; // initialization duration
+
+ console.log('ONOS UI initialized in ' + tsI + 'ms');
+
+ if (built) {
+ throwError("ONOS UI already built!");
+ }
+ built = true;
+
+ $view = d3.select('#view');
+ $mastRadio = d3.select('#mastRadio');
+
+ $(window).on('hashchange', hash);
+ $(window).on('resize', resize);
+
+ d3.select('body').on('keydown', keyIn);
+
+ // Invoke hashchange callback to navigate to content
+ // indicated by the window location hash.
+ hash();
+
+ // If there were any build errors, report them
+ reportBuildErrors();
+ }
+
+ // export the api and build-UI function
+ return {
+ ui: uiApi,
+ view: viewApi,
+ nav: navApi,
+ buildUi: buildOnosUi
+ };
+ };
+
+}(jQuery));
diff --git a/web/gui/src/main/webapp/module-template.js b/web/gui/src/main/webapp/preamble.js
similarity index 65%
copy from web/gui/src/main/webapp/module-template.js
copy to web/gui/src/main/webapp/preamble.js
index 3de7d79..8ee8e45 100644
--- a/web/gui/src/main/webapp/module-template.js
+++ b/web/gui/src/main/webapp/preamble.js
@@ -15,22 +15,18 @@
*/
/*
- Module template file.
+ ONOS GUI -- Preamble -- the first thing we do
@author Simon Hunt
*/
-(function (onos) {
- 'use strict';
+(function () {
+ // Check if the URL in the address bar contains a parameter section
+ // (delineated by '?'). If this is the case, rewrite using '#' instead.
- var api = onos.api;
+ var m = /([^?]*)\?(.*)/.exec(window.location.href);
+ if (m) {
+ window.location.href = m[1] + '#' + m[2];
+ }
- // == define your functions here.....
-
-
- // == register views here, with links to lifecycle callbacks
-
-// api.addView('view-id', {/* callbacks */});
-
-
-}(ONOS));
+}());
diff --git a/web/gui/src/main/webapp/sample2.js b/web/gui/src/main/webapp/sample2.js
new file mode 100644
index 0000000..6b4329f
--- /dev/null
+++ b/web/gui/src/main/webapp/sample2.js
@@ -0,0 +1,222 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ Sample module file to illustrate framework integration.
+
+ @author Simon Hunt
+ */
+
+(function (onos) {
+ 'use strict';
+
+ var pi = Math.PI,
+ svg,
+ dotG,
+ nCircles = 12,
+ circleData = [],
+ dotId = 0,
+ angle = 360 / nCircles,
+ baseAngle = -90 - angle,
+ groupRadius = 120,
+ dotRadius = 24,
+ dotMoveMs = 800,
+ dotAppearMs = 300,
+ dotEase = 'elastic',
+ colorScale = d3.scale.linear()
+ .domain([-pi/2, 2*pi/4, 3*pi/2])
+ .range(['green', 'goldenrod', 'blue']);
+
+ // set the size of the SVG layer to match that of the view
+ function sizeSvg(view) {
+ svg.attr({
+ width: view.width(),
+ height: view.height()
+ });
+ }
+
+ // gets invoked only the first time the view is loaded
+ function preload(view, ctx) {
+ // prepare our SVG layer...
+ svg = view.$div.append('svg');
+ sizeSvg(view);
+ dotG = svg.append('g').attr('id', 'dots');
+ }
+
+ // gets invoked just before our view is loaded
+ function reset(view) {
+ // clear dot group and reset circle data
+ dotG.html('');
+ circleData = [];
+ // also clear text, if any
+ svg.selectAll('text').remove();
+ }
+
+ function updateCirclePositions(view, addNew) {
+ var w = view.width(),
+ h = view.height(),
+ ox = w / 2,
+ oy = h / 2;
+
+ // reposition existing dots
+ circleData.forEach(function (c, i) {
+ var inc = addNew ? 1 : 0,
+ theta = ((i + inc) * angle + baseAngle) * pi/180,
+ dx = Math.cos(theta) * groupRadius,
+ dy = Math.sin(theta) * groupRadius,
+ x = ox + dx,
+ y = oy + dy;
+ if (!addNew && i === 0) {
+ x = ox;
+ y = oy;
+ }
+ c.cx = x;
+ c.cy = y;
+ c.rgb = colorScale(theta);
+ });
+
+ if (addNew) {
+ // introduce a new dot
+ circleData.unshift({
+ cx: ox,
+ cy: oy,
+ id: dotId++
+ });
+ }
+
+ // +1 to account for the circle in the center..
+ if (circleData.length > nCircles + 1) {
+ circleData.splice(nCircles + 1, 1);
+ }
+ }
+
+ function doCircles(view) {
+ var ox = view.width() / 2,
+ oy = view.height() / 2,
+ stroke = 'black',
+ fill = 'red',
+ hoverFill = 'magenta';
+
+ // move existing circles, and add a new one
+ updateCirclePositions(view, true);
+
+ var circ = dotG.selectAll('circle')
+ .data(circleData, function (d) { return d.id; });
+
+ // operate on existing elements
+ circ.on('mouseover', null)
+ .on('mouseout', null)
+ .on('click', null)
+ .transition()
+ .duration(dotMoveMs)
+ .ease(dotEase)
+ .attr({
+ cx: function (d) { return d.cx; },
+ cy: function (d) { return d.cy; }
+ })
+ .style({
+ cursor: 'default',
+ fill: function (d) { return d.rgb; }
+ });
+
+ // operate on entering elements
+ circ.enter()
+ .append('circle')
+ .attr({
+ cx: function (d) { return d.cx; },
+ cy: function (d) { return d.cy; },
+ r: 0
+ })
+ .style({
+ fill: fill,
+ stroke: stroke,
+ 'stroke-width': 3.5,
+ cursor: 'pointer',
+ opacity: 0
+ })
+ .on('mouseover', function (d) {
+ d3.select(this).style('fill', hoverFill);
+ })
+ .on('mouseout', function (d) {
+ d3.select(this).style('fill', fill);
+ })
+ .on('click', function (d) {
+ setTimeout(function() {
+ doCircles(view, true);
+ }, 10);
+ })
+ .transition()
+ .delay(dotMoveMs)
+ .duration(dotAppearMs)
+ .attr('r', dotRadius)
+ .style('opacity', 1);
+
+ // operate on exiting elements
+ circ.exit()
+ .transition()
+ .duration(750)
+ .style('opacity', 0)
+ .attr({
+ cx: ox,
+ cy: oy,
+ r: groupRadius - dotRadius
+ })
+ .remove();
+ }
+
+ function load(view, ctx) {
+ var ctxText = ctx ? 'Context is "' + ctx + '"' : '';
+
+ // display our view context
+ if (ctxText) {
+ svg.append('text')
+ .text(ctxText)
+ .attr({
+ x: 20,
+ y: '1.5em'
+ })
+ .style({
+ fill: 'darkgreen',
+ 'font-size': '20pt'
+ });
+ }
+
+ doCircles(view);
+ }
+
+ function resize(view, ctx) {
+ sizeSvg(view);
+ updateCirclePositions(view);
+
+ // move exiting dots into new positions, relative to view size
+ var circ = dotG.selectAll('circle')
+ .data(circleData, function (d) { return d.id; });
+ circ.attr({
+ cx: function (d) { return d.cx; },
+ cy: function (d) { return d.cy; }
+ });
+ }
+
+ // == register our view here, with links to lifecycle callbacks
+
+ onos.ui.addView('sample', {
+ preload: preload,
+ reset: reset,
+ load: load,
+ resize: resize
+ });
+
+}(ONOS));
diff --git a/web/gui/src/main/webapp/sampleAlt2.js b/web/gui/src/main/webapp/sampleAlt2.js
new file mode 100644
index 0000000..60cbe9d
--- /dev/null
+++ b/web/gui/src/main/webapp/sampleAlt2.js
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ Alternate sample module file to illustrate framework integration.
+
+ @author Simon Hunt
+ */
+
+(function (onos) {
+ 'use strict';
+
+ var svg;
+
+
+ function sizeSvg(view) {
+ svg.attr({
+ width: view.width(),
+ height: view.height()
+ });
+ }
+
+ // gets invoked only the first time the view is loaded
+ function preload(view, ctx) {
+ svg = view.$div.append('svg');
+ sizeSvg(view);
+ }
+
+ function reset(view) {
+ // clear our svg of all objects
+ svg.html('');
+ }
+
+ function load(view, ctx) {
+ var fill = 'teal',
+ stroke = 'black';
+
+ svg.append('circle')
+ .attr({
+ cx: view.width() / 2,
+ cy: view.height() / 2,
+ r: 30
+ })
+ .style({
+ fill: fill,
+ stroke: stroke,
+ 'stroke-width': 1.5,
+ opacity: 0.5
+ });
+ }
+
+ function resize(view, ctx) {
+ sizeSvg(view);
+ svg.selectAll('circle')
+ .attr({
+ cx: view.width() / 2,
+ cy: view.height() / 2
+ });
+ }
+
+ // == register views here, with links to lifecycle callbacks
+
+ onos.ui.addView('sampleAlt', {
+ preload: preload,
+ reset: reset,
+ load: load,
+ resize: resize
+ });
+
+
+}(ONOS));
diff --git a/web/gui/src/main/webapp/sampleKeys.js b/web/gui/src/main/webapp/sampleKeys.js
new file mode 100644
index 0000000..a99d6a7
--- /dev/null
+++ b/web/gui/src/main/webapp/sampleKeys.js
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ Sample view to illustrate key bindings.
+
+ @author Simon Hunt
+ */
+
+(function (onos) {
+ 'use strict';
+
+ var keyDispatch = {
+ Z: keyUndo,
+ X: keyCut,
+ C: keyCopy,
+ V: keyPaste,
+ space: keySpace
+ };
+
+ function keyUndo(view) {
+ note(view, 'Z = UNDO');
+ }
+
+ function keyCut(view) {
+ note(view, 'X = CUT');
+ }
+
+ function keyCopy(view) {
+ note(view, 'C = COPY');
+ }
+
+ function keyPaste(view) {
+ note(view, 'V = PASTE');
+ }
+
+ function keySpace(view) {
+ note(view, 'The SpaceBar');
+ }
+
+ function note(view, msg) {
+ view.$div.append('p')
+ .text(msg)
+ .style({
+ 'font-size': '10pt',
+ color: 'darkorange',
+ padding: '0 20px',
+ margin: 0
+ });
+ }
+
+ function keyCallback(view, key, keyCode, event) {
+ note(view, 'Key = ' + key + ' KeyCode = ' + keyCode);
+ }
+
+ function load(view, ctx) {
+ // this maps specific keys to specific functions (1)
+ view.setKeys(keyDispatch);
+ // whereas, this installs a general key handler function (2)
+ view.setKeys(keyCallback);
+
+ // Note that (1) takes precedence over (2)
+
+ view.$div.append('p')
+ .text('Press a key or two (try Z,X,C,V and others) ...')
+ .style('padding', '2px 8px');
+ }
+
+ // == register the view here, with links to lifecycle callbacks
+
+ onos.ui.addView('sampleKeys', {
+ reset: true, // empty the div on reset
+ load: load
+ });
+
+}(ONOS));
diff --git a/web/gui/src/main/webapp/sampleRadio.js b/web/gui/src/main/webapp/sampleRadio.js
new file mode 100644
index 0000000..4d362a9
--- /dev/null
+++ b/web/gui/src/main/webapp/sampleRadio.js
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ Sample view to illustrate radio buttons.
+
+ @author Simon Hunt
+ */
+
+(function (onos) {
+ 'use strict';
+
+ var data = [ 'Yo, radio button set...', 'Time to shine' ],
+ btnSet = [
+ { id: 'b1', text: 'First Button' },
+ { id: 'b2', text: 'Second Button' },
+ { id: 'b3', text: 'Third Button' }
+ ],
+ btnLookup = {};
+
+ btnSet.forEach(function (b) {
+ btnLookup[b.id] = b;
+ });
+
+ // invoked when the view is loaded
+ function load(view, ctx) {
+ view.setRadio(btnSet, doRadio);
+
+ view.$div.selectAll('p')
+ .data(data)
+ .enter()
+ .append('p')
+ .text(function (d) { return d; })
+ .style('padding', '2px 8px');
+ }
+
+ function doRadio(view, id) {
+ view.$div.append('p')
+ .text('You pressed the ' + btnLookup[id].text)
+ .style({
+ 'font-size': '10pt',
+ color: 'green',
+ padding: '0 20px',
+ margin: '2px'
+ });
+ }
+
+ // == register the view here, with links to lifecycle callbacks
+
+ onos.ui.addView('sampleRadio', {
+ reset: true, // empty the div on reset
+ load: load
+ });
+
+}(ONOS));
diff --git a/web/gui/src/main/webapp/topo2-OLD.js b/web/gui/src/main/webapp/topo2-OLD.js
new file mode 100644
index 0000000..04ce7ab
--- /dev/null
+++ b/web/gui/src/main/webapp/topo2-OLD.js
@@ -0,0 +1,1219 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ ONOS network topology viewer - PoC version 1.0
+
+ @author Simon Hunt
+ */
+
+(function (onos) {
+ 'use strict';
+
+ // configuration data
+ var config = {
+ useLiveData: true,
+ debugOn: false,
+ debug: {
+ showNodeXY: false,
+ showKeyHandler: true
+ },
+ options: {
+ layering: true,
+ collisionPrevention: true,
+ loadBackground: true
+ },
+ backgroundUrl: 'img/us-map.png',
+ data: {
+ live: {
+ jsonUrl: 'rs/topology/graph',
+ detailPrefix: 'rs/topology/graph/',
+ detailSuffix: ''
+ },
+ fake: {
+ jsonUrl: 'json/network2.json',
+ detailPrefix: 'json/',
+ detailSuffix: '.json'
+ }
+ },
+ iconUrl: {
+ device: 'img/device.png',
+ host: 'img/host.png',
+ pkt: 'img/pkt.png',
+ opt: 'img/opt.png'
+ },
+ mastHeight: 36,
+ force: {
+ note: 'node.class or link.class is used to differentiate',
+ linkDistance: {
+ infra: 200,
+ host: 40
+ },
+ linkStrength: {
+ infra: 1.0,
+ host: 1.0
+ },
+ charge: {
+ device: -800,
+ host: -1000
+ },
+ ticksWithoutCollisions: 50,
+ marginLR: 20,
+ marginTB: 20,
+ translate: function() {
+ return 'translate(' +
+ config.force.marginLR + ',' +
+ config.force.marginTB + ')';
+ }
+ },
+ labels: {
+ imgPad: 16,
+ padLR: 8,
+ padTB: 6,
+ marginLR: 3,
+ marginTB: 2,
+ port: {
+ gap: 3,
+ width: 18,
+ height: 14
+ }
+ },
+ icons: {
+ w: 32,
+ h: 32,
+ xoff: -12,
+ yoff: -8
+ },
+ constraints: {
+ ypos: {
+ host: 0.05,
+ switch: 0.3,
+ roadm: 0.7
+ }
+ },
+ hostLinkWidth: 1.0,
+ hostRadius: 7,
+ mouseOutTimerDelayMs: 120
+ };
+
+ // state variables
+ var netView = {},
+ network = {},
+ selected = {},
+ highlighted = null,
+ hovered = null,
+ viewMode = 'showAll',
+ portLabelsOn = false;
+
+
+ function debug(what) {
+ return config.debugOn && config.debug[what];
+ }
+
+ function urlData() {
+ return config.data[config.useLiveData ? 'live' : 'fake'];
+ }
+
+ function networkJsonUrl() {
+ return urlData().jsonUrl;
+ }
+
+ function safeId(id) {
+ return id.replace(/[^a-z0-9]/gi, '_');
+ }
+
+ function detailJsonUrl(id) {
+ var u = urlData(),
+ encId = config.useLiveData ? encodeURIComponent(id) : safeId(id);
+ return u.detailPrefix + encId + u.detailSuffix;
+ }
+
+
+ // load the topology view of the network
+ function loadNetworkView() {
+ // Hey, here I am, calling something on the ONOS api:
+ api.printTime();
+
+ resize();
+
+ // go get our network data from the server...
+ var url = networkJsonUrl();
+ d3.json(url , function (err, data) {
+ if (err) {
+ alert('Oops! Error reading JSON...\n\n' +
+ 'URL: ' + url + '\n\n' +
+ 'Error: ' + err.message);
+ return;
+ }
+// console.log("here is the JSON data...");
+// console.log(data);
+
+ network.data = data;
+ drawNetwork();
+ });
+
+ // while we wait for the data, set up the handlers...
+ setUpClickHandler();
+ setUpRadioButtonHandler();
+ setUpKeyHandler();
+ $(window).on('resize', resize);
+ }
+
+ function setUpClickHandler() {
+ // click handler for "selectable" objects
+ $(document).on('click', '.select-object', function () {
+ // when any object of class "select-object" is clicked...
+ var obj = network.lookup[$(this).data('id')];
+ if (obj) {
+ selectObject(obj);
+ }
+ // stop propagation of event (I think) ...
+ return false;
+ });
+ }
+
+ function setUpRadioButtonHandler() {
+ d3.selectAll('#displayModes .radio').on('click', function () {
+ var id = d3.select(this).attr('id');
+ if (id !== viewMode) {
+ radioButton('displayModes', id);
+ viewMode = id;
+ doRadioAction(id);
+ }
+ });
+ }
+
+ function doRadioAction(id) {
+ showAllLayers();
+ if (id === 'showPkt') {
+ showPacketLayer();
+ } else if (id === 'showOpt') {
+ showOpticalLayer();
+ }
+ }
+
+ function showAllLayers() {
+ network.node.classed('inactive', false);
+ network.link.classed('inactive', false);
+ d3.selectAll('svg .port').classed('inactive', false);
+ d3.selectAll('svg .portText').classed('inactive', false);
+ }
+
+ function showPacketLayer() {
+ network.node.each(function(d) {
+ // deactivate nodes that are not hosts or switches
+ if (d.class === 'device' && d.type !== 'switch') {
+ d3.select(this).classed('inactive', true);
+ }
+ });
+
+ network.link.each(function(lnk) {
+ // deactivate infrastructure links that have opt's as endpoints
+ if (lnk.source.type === 'roadm' || lnk.target.type === 'roadm') {
+ d3.select(this).classed('inactive', true);
+ }
+ });
+
+ // deactivate non-packet ports
+ d3.selectAll('svg .optPort').classed('inactive', true)
+ }
+
+ function showOpticalLayer() {
+ network.node.each(function(d) {
+ // deactivate nodes that are not optical devices
+ if (d.type !== 'roadm') {
+ d3.select(this).classed('inactive', true);
+ }
+ });
+
+ network.link.each(function(lnk) {
+ // deactivate infrastructure links that have opt's as endpoints
+ if (lnk.source.type !== 'roadm' || lnk.target.type !== 'roadm') {
+ d3.select(this).classed('inactive', true);
+ }
+ });
+
+ // deactivate non-packet ports
+ d3.selectAll('svg .pktPort').classed('inactive', true)
+ }
+
+ function setUpKeyHandler() {
+ d3.select('body')
+ .on('keydown', function () {
+ processKeyEvent();
+ if (debug('showKeyHandler')) {
+ network.svg.append('text')
+ .attr('x', 5)
+ .attr('y', 15)
+ .style('font-size', '20pt')
+ .text('keyCode: ' + d3.event.keyCode +
+ ' applied to : ' + contextLabel())
+ .transition().duration(2000)
+ .style('font-size', '2pt')
+ .style('fill-opacity', 0.01)
+ .remove();
+ }
+ });
+ }
+
+ function contextLabel() {
+ return hovered === null ? "(nothing)" : hovered.id;
+ }
+
+ function radioButton(group, id) {
+ d3.selectAll("#" + group + " .radio").classed("active", false);
+ d3.select("#" + group + " #" + id).classed("active", true);
+ }
+
+ function processKeyEvent() {
+ var code = d3.event.keyCode;
+ switch (code) {
+ case 66: // B
+ toggleBackground();
+ break;
+ case 71: // G
+ cycleLayout();
+ break;
+ case 76: // L
+ cycleLabels();
+ break;
+ case 80: // P
+ togglePorts();
+ break;
+ case 85: // U
+ unpin();
+ break;
+ }
+
+ }
+
+ function toggleBackground() {
+ var bg = d3.select('#bg'),
+ vis = bg.style('visibility'),
+ newvis = (vis === 'hidden') ? 'visible' : 'hidden';
+ bg.style('visibility', newvis);
+ }
+
+ function cycleLayout() {
+ config.options.layering = !config.options.layering;
+ network.force.resume();
+ }
+
+ function cycleLabels() {
+ console.log('Cycle Labels - context = ' + contextLabel());
+ }
+
+ function togglePorts() {
+ portLabelsOn = !portLabelsOn;
+ var portVis = portLabelsOn ? 'visible' : 'hidden';
+ d3.selectAll('.port').style('visibility', portVis);
+ d3.selectAll('.portText').style('visibility', portVis);
+ }
+
+ function unpin() {
+ if (hovered) {
+ hovered.fixed = false;
+ findNodeFromData(hovered).classed('fixed', false);
+ network.force.resume();
+ }
+ console.log('Unpin - context = ' + contextLabel());
+ }
+
+
+ // ========================================================
+
+ function drawNetwork() {
+ $('#view').empty();
+
+ prepareNodesAndLinks();
+ createLayout();
+ console.log("\n\nHere is the augmented network object...");
+ console.log(network);
+ }
+
+ function prepareNodesAndLinks() {
+ network.lookup = {};
+ network.nodes = [];
+ network.links = [];
+
+ var nw = network.forceWidth,
+ nh = network.forceHeight;
+
+ function yPosConstraintForNode(n) {
+ return config.constraints.ypos[n.type || 'host'];
+ }
+
+ // Note that both 'devices' and 'hosts' get mapped into the nodes array
+
+ // first, the devices...
+ network.data.devices.forEach(function(n) {
+ var ypc = yPosConstraintForNode(n),
+ ix = Math.random() * 0.6 * nw + 0.2 * nw,
+ iy = ypc * nh,
+ node = {
+ id: n.id,
+ labels: n.labels,
+ class: 'device',
+ icon: 'device',
+ type: n.type,
+ x: ix,
+ y: iy,
+ constraint: {
+ weight: 0.7,
+ y: iy
+ }
+ };
+ network.lookup[n.id] = node;
+ network.nodes.push(node);
+ });
+
+ // then, the hosts...
+ network.data.hosts.forEach(function(n) {
+ var ypc = yPosConstraintForNode(n),
+ ix = Math.random() * 0.6 * nw + 0.2 * nw,
+ iy = ypc * nh,
+ node = {
+ id: n.id,
+ labels: n.labels,
+ class: 'host',
+ icon: 'host',
+ type: n.type,
+ x: ix,
+ y: iy,
+ constraint: {
+ weight: 0.7,
+ y: iy
+ }
+ };
+ network.lookup[n.id] = node;
+ network.nodes.push(node);
+ });
+
+
+ // now, process the explicit links...
+ network.data.links.forEach(function(lnk) {
+ var src = network.lookup[lnk.src],
+ dst = network.lookup[lnk.dst],
+ id = src.id + "-" + dst.id;
+
+ var link = {
+ class: 'infra',
+ id: id,
+ type: lnk.type,
+ width: lnk.linkWidth,
+ source: src,
+ srcPort: lnk.srcPort,
+ target: dst,
+ tgtPort: lnk.dstPort,
+ strength: config.force.linkStrength.infra
+ };
+ network.links.push(link);
+ });
+
+ // finally, infer host links...
+ network.data.hosts.forEach(function(n) {
+ var src = network.lookup[n.id],
+ dst = network.lookup[n.cp.device],
+ id = src.id + "-" + dst.id;
+
+ var link = {
+ class: 'host',
+ id: id,
+ type: 'hostLink',
+ width: config.hostLinkWidth,
+ source: src,
+ target: dst,
+ strength: config.force.linkStrength.host
+ };
+ network.links.push(link);
+ });
+ }
+
+ function createLayout() {
+
+ var cfg = config.force;
+
+ network.force = d3.layout.force()
+ .size([network.forceWidth, network.forceHeight])
+ .nodes(network.nodes)
+ .links(network.links)
+ .linkStrength(function(d) { return cfg.linkStrength[d.class]; })
+ .linkDistance(function(d) { return cfg.linkDistance[d.class]; })
+ .charge(function(d) { return cfg.charge[d.class]; })
+ .on('tick', tick);
+
+ network.svg = d3.select('#view').append('svg')
+ .attr('width', netView.width)
+ .attr('height', netView.height)
+ .append('g')
+ .attr('transform', config.force.translate());
+// .attr('id', 'zoomable')
+// .call(d3.behavior.zoom().on("zoom", zoomRedraw));
+
+ network.svg.append('svg:image')
+ .attr({
+ id: 'bg',
+ width: netView.width,
+ height: netView.height,
+ 'xlink:href': config.backgroundUrl
+ })
+ .style('visibility',
+ config.options.loadBackground ? 'visible' : 'hidden');
+
+// function zoomRedraw() {
+// d3.select("#zoomable").attr("transform",
+// "translate(" + d3.event.translate + ")"
+// + " scale(" + d3.event.scale + ")");
+// }
+
+ // TODO: move glow/blur stuff to util script
+ var glow = network.svg.append('filter')
+ .attr('x', '-50%')
+ .attr('y', '-50%')
+ .attr('width', '200%')
+ .attr('height', '200%')
+ .attr('id', 'blue-glow');
+
+ glow.append('feColorMatrix')
+ .attr('type', 'matrix')
+ .attr('values', '0 0 0 0 0 ' +
+ '0 0 0 0 0 ' +
+ '0 0 0 0 .7 ' +
+ '0 0 0 1 0 ');
+
+ glow.append('feGaussianBlur')
+ .attr('stdDeviation', 3)
+ .attr('result', 'coloredBlur');
+
+ glow.append('feMerge').selectAll('feMergeNode')
+ .data(['coloredBlur', 'SourceGraphic'])
+ .enter().append('feMergeNode')
+ .attr('in', String);
+
+ // TODO: legend (and auto adjust on scroll)
+// $('#view').on('scroll', function() {
+//
+// });
+
+
+ // TODO: move drag behavior into separate method.
+ // == define node drag behavior...
+ network.draggedThreshold = d3.scale.linear()
+ .domain([0, 0.1])
+ .range([5, 20])
+ .clamp(true);
+
+ function dragged(d) {
+ var threshold = network.draggedThreshold(network.force.alpha()),
+ dx = d.oldX - d.px,
+ dy = d.oldY - d.py;
+ if (Math.abs(dx) >= threshold || Math.abs(dy) >= threshold) {
+ d.dragged = true;
+ }
+ return d.dragged;
+ }
+
+ network.drag = d3.behavior.drag()
+ .origin(function(d) { return d; })
+ .on('dragstart', function(d) {
+ d.oldX = d.x;
+ d.oldY = d.y;
+ d.dragged = false;
+ d.fixed |= 2;
+ })
+ .on('drag', function(d) {
+ d.px = d3.event.x;
+ d.py = d3.event.y;
+ if (dragged(d)) {
+ if (!network.force.alpha()) {
+ network.force.alpha(.025);
+ }
+ }
+ })
+ .on('dragend', function(d) {
+ if (!dragged(d)) {
+ selectObject(d, this);
+ }
+ d.fixed &= ~6;
+
+ // once we've finished moving, pin the node in position,
+ // if it is a device (not a host)
+ if (d.class === 'device') {
+ d.fixed = true;
+ d3.select(this).classed('fixed', true)
+ }
+ });
+
+ $('#view').on('click', function(e) {
+ if (!$(e.target).closest('.node').length) {
+ deselectObject();
+ }
+ });
+
+ // ...............................................................
+
+ // add links to the display
+ network.link = network.svg.append('g').attr('id', 'links')
+ .selectAll('.link')
+ .data(network.force.links(), function(d) {return d.id})
+ .enter().append('line')
+ .attr('class', function(d) {return 'link ' + d.class});
+
+ network.linkSrcPort = network.svg.append('g')
+ .attr({
+ id: 'srcPorts',
+ class: 'portLayer'
+ });
+ network.linkTgtPort = network.svg.append('g')
+ .attr({
+ id: 'tgtPorts',
+ class: 'portLayer'
+ });
+
+ var portVis = portLabelsOn ? 'visible' : 'hidden',
+ pw = config.labels.port.width,
+ ph = config.labels.port.height;
+
+ network.link.filter('.infra').each(function(d) {
+ var srcType = d.source.type === 'roadm' ? 'optPort' : 'pktPort',
+ tgtType = d.target.type === 'roadm' ? 'optPort' : 'pktPort';
+
+ if (d.source.type)
+
+ network.linkSrcPort.append('rect').attr({
+ id: 'srcPort-' + safeId(d.id),
+ class: 'port ' + srcType,
+ width: pw,
+ height: ph,
+ rx: 4,
+ ry: 4
+ }).style('visibility', portVis);
+
+ network.linkTgtPort.append('rect').attr({
+ id: 'tgtPort-' + safeId(d.id),
+ class: 'port ' + tgtType,
+ width: pw,
+ height: ph,
+ rx: 4,
+ ry: 4
+ }).style('visibility', portVis);
+
+ network.linkSrcPort.append('text').attr({
+ id: 'srcText-' + safeId(d.id),
+ class: 'portText ' + srcType
+ }).text(d.srcPort)
+ .style('visibility', portVis);
+
+ network.linkTgtPort.append('text').attr({
+ id: 'tgtText-' + safeId(d.id),
+ class: 'portText ' + tgtType
+ }).text(d.tgtPort)
+ .style('visibility', portVis);
+ });
+
+ // ...............................................................
+
+ // add nodes to the display
+ network.node = network.svg.selectAll('.node')
+ .data(network.force.nodes(), function(d) {return d.id})
+ .enter().append('g')
+ .attr('class', function(d) {
+ var cls = 'node ' + d.class;
+ if (d.type) {
+ cls += ' ' + d.type;
+ }
+ return cls;
+ })
+ .attr('transform', function(d) {
+ return translate(d.x, d.y);
+ })
+ .call(network.drag)
+ .on('mouseover', function(d) {
+ // TODO: show tooltip
+ if (network.mouseoutTimeout) {
+ clearTimeout(network.mouseoutTimeout);
+ network.mouseoutTimeout = null;
+ }
+ hoverObject(d);
+ })
+ .on('mouseout', function(d) {
+ // TODO: hide tooltip
+ if (network.mouseoutTimeout) {
+ clearTimeout(network.mouseoutTimeout);
+ network.mouseoutTimeout = null;
+ }
+ network.mouseoutTimeout = setTimeout(function() {
+ hoverObject(null);
+ }, config.mouseOutTimerDelayMs);
+ });
+
+
+ // deal with device nodes first
+ network.nodeRect = network.node.filter('.device')
+ .append('rect')
+ .attr({
+ rx: 5,
+ ry: 5,
+ width: 100,
+ height: 12
+ });
+ // note that width/height are adjusted to fit the label text
+ // then padded, and space made for the icon.
+
+ network.node.filter('.device').each(function(d) {
+ var node = d3.select(this),
+ icon = iconUrl(d);
+
+ node.append('text')
+ // TODO: add label cycle behavior
+ .text(d.id)
+ .attr('dy', '1.1em');
+
+ if (icon) {
+ var cfg = config.icons;
+ node.append('svg:image')
+ .attr({
+ width: cfg.w,
+ height: cfg.h,
+ 'xlink:href': icon
+ });
+ // note, icon relative positioning (x,y) is done after we have
+ // adjusted the bounds of the rectangle...
+ }
+
+ // debug function to show the modelled x,y coordinates of nodes...
+ if (debug('showNodeXY')) {
+ node.select('rect').attr('fill-opacity', 0.5);
+ node.append('circle')
+ .attr({
+ class: 'debug',
+ cx: 0,
+ cy: 0,
+ r: '3px'
+ });
+ }
+ });
+
+ // now process host nodes
+ network.nodeCircle = network.node.filter('.host')
+ .append('circle')
+ .attr({
+ r: config.hostRadius
+ });
+
+ network.node.filter('.host').each(function(d) {
+ var node = d3.select(this),
+ icon = iconUrl(d);
+
+ // debug function to show the modelled x,y coordinates of nodes...
+ if (debug('showNodeXY')) {
+ node.select('circle').attr('fill-opacity', 0.5);
+ node.append('circle')
+ .attr({
+ class: 'debug',
+ cx: 0,
+ cy: 0,
+ r: '3px'
+ });
+ }
+ });
+
+ // this function is scheduled to happen soon after the given thread ends
+ setTimeout(function() {
+ var lab = config.labels,
+ portGap = lab.port.gap,
+ midW = portGap + lab.port.width/ 2,
+ midH = portGap + lab.port.height / 2;
+
+ // post process the device nodes, to pad their size to fit the
+ // label text and attach the icon to the right location.
+ network.node.filter('.device').each(function(d) {
+ // for every node, recompute size, padding, etc. so text fits
+ var node = d3.select(this),
+ text = node.select('text'),
+ box = adjustRectToFitText(node);
+
+ // now make the computed adjustment
+ node.select('rect')
+ .attr(box);
+
+ node.select('image')
+ .attr('x', box.x + config.icons.xoff)
+ .attr('y', box.y + config.icons.yoff);
+
+ var bounds = boundsFromBox(box),
+ portBounds = {
+ x1: bounds.x1 - midW,
+ x2: bounds.x2 + midW,
+ y1: bounds.y1 - midH,
+ y2: bounds.y2 + midH
+ };
+
+ // todo: clean up extent and edge work..
+ d.extent = {
+ left: bounds.x1 - lab.marginLR,
+ right: bounds.x2 + lab.marginLR,
+ top: bounds.y1 - lab.marginTB,
+ bottom: bounds.y2 + lab.marginTB
+ };
+
+ d.edge = {
+ left : new geo.LineSegment(bounds.x1, bounds.y1, bounds.x1, bounds.y2),
+ right : new geo.LineSegment(bounds.x2, bounds.y1, bounds.x2, bounds.y2),
+ top : new geo.LineSegment(bounds.x1, bounds.y1, bounds.x2, bounds.y1),
+ bottom : new geo.LineSegment(bounds.x1, bounds.y2, bounds.x2, bounds.y2)
+ };
+
+ d.portEdge = {
+ left : new geo.LineSegment(
+ portBounds.x1, portBounds.y1, portBounds.x1, portBounds.y2
+ ),
+ right : new geo.LineSegment(
+ portBounds.x2, portBounds.y1, portBounds.x2, portBounds.y2
+ ),
+ top : new geo.LineSegment(
+ portBounds.x1, portBounds.y1, portBounds.x2, portBounds.y1
+ ),
+ bottom : new geo.LineSegment(
+ portBounds.x1, portBounds.y2, portBounds.x2, portBounds.y2
+ )
+ };
+
+ });
+
+ network.numTicks = 0;
+ network.preventCollisions = false;
+ network.force.start();
+ for (var i = 0; i < config.force.ticksWithoutCollisions; i++) {
+ network.force.tick();
+ }
+ network.preventCollisions = true;
+ $('#view').css('visibility', 'visible');
+ });
+
+
+ // returns the newly computed bounding box of the rectangle
+ function adjustRectToFitText(n) {
+ var text = n.select('text'),
+ box = text.node().getBBox(),
+ lab = config.labels;
+
+ // not sure why n.data() returns an array of 1 element...
+ var data = n.data()[0];
+
+ text.attr('text-anchor', 'middle')
+ .attr('y', '-0.8em')
+ .attr('x', lab.imgPad/2)
+ ;
+
+ // translate the bbox so that it is centered on [x,y]
+ box.x = -box.width / 2;
+ box.y = -box.height / 2;
+
+ // add padding
+ box.x -= (lab.padLR + lab.imgPad/2);
+ box.width += lab.padLR * 2 + lab.imgPad;
+ box.y -= lab.padTB;
+ box.height += lab.padTB * 2;
+
+ return box;
+ }
+
+ function boundsFromBox(box) {
+ return {
+ x1: box.x,
+ y1: box.y,
+ x2: box.x + box.width,
+ y2: box.y + box.height
+ };
+ }
+
+ }
+
+ function iconUrl(d) {
+ return 'img/' + d.type + '.png';
+// return config.iconUrl[d.icon];
+ }
+
+ function translate(x, y) {
+ return 'translate(' + x + ',' + y + ')';
+ }
+
+ // prevents collisions amongst device nodes
+ function preventCollisions() {
+ var quadtree = d3.geom.quadtree(network.nodes),
+ hrad = config.hostRadius;
+
+ network.nodes.forEach(function(n) {
+ var nx1, nx2, ny1, ny2;
+
+ if (n.class === 'device') {
+ nx1 = n.x + n.extent.left;
+ nx2 = n.x + n.extent.right;
+ ny1 = n.y + n.extent.top;
+ ny2 = n.y + n.extent.bottom;
+
+ } else {
+ nx1 = n.x - hrad;
+ nx2 = n.x + hrad;
+ ny1 = n.y - hrad;
+ ny2 = n.y + hrad;
+ }
+
+ quadtree.visit(function(quad, x1, y1, x2, y2) {
+ if (quad.point && quad.point !== n) {
+ // check if the rectangles/circles intersect
+ var p = quad.point,
+ px1, px2, py1, py2, ix;
+
+ if (p.class === 'device') {
+ px1 = p.x + p.extent.left;
+ px2 = p.x + p.extent.right;
+ py1 = p.y + p.extent.top;
+ py2 = p.y + p.extent.bottom;
+
+ } else {
+ px1 = p.x - hrad;
+ px2 = p.x + hrad;
+ py1 = p.y - hrad;
+ py2 = p.y + hrad;
+ }
+
+ ix = (px1 <= nx2 && nx1 <= px2 && py1 <= ny2 && ny1 <= py2);
+
+ if (ix) {
+ var xa1 = nx2 - px1, // shift n left , p right
+ xa2 = px2 - nx1, // shift n right, p left
+ ya1 = ny2 - py1, // shift n up , p down
+ ya2 = py2 - ny1, // shift n down , p up
+ adj = Math.min(xa1, xa2, ya1, ya2);
+
+ if (adj == xa1) {
+ n.x -= adj / 2;
+ p.x += adj / 2;
+ } else if (adj == xa2) {
+ n.x += adj / 2;
+ p.x -= adj / 2;
+ } else if (adj == ya1) {
+ n.y -= adj / 2;
+ p.y += adj / 2;
+ } else if (adj == ya2) {
+ n.y += adj / 2;
+ p.y -= adj / 2;
+ }
+ }
+ return ix;
+ }
+ });
+
+ });
+ }
+
+ function tick(e) {
+ network.numTicks++;
+
+ if (config.options.layering) {
+ // adjust the y-coord of each node, based on y-pos constraints
+ network.nodes.forEach(function (n) {
+ var z = e.alpha * n.constraint.weight;
+ if (!isNaN(n.constraint.y)) {
+ n.y = (n.constraint.y * z + n.y * (1 - z));
+ }
+ });
+ }
+
+ if (config.options.collisionPrevention && network.preventCollisions) {
+ preventCollisions();
+ }
+
+ var portHalfW = config.labels.port.width / 2,
+ portHalfH = config.labels.port.height / 2;
+
+ // clip visualization of links at bounds of nodes...
+ network.link.each(function(d) {
+ var xs = d.source.x,
+ ys = d.source.y,
+ xt = d.target.x,
+ yt = d.target.y,
+ line = new geo.LineSegment(xs, ys, xt, yt),
+ e, ix,
+ exs, eys, ext, eyt,
+ pxs, pys, pxt, pyt;
+
+ if (d.class === 'host') {
+ // no adjustment for source end of link, since hosts are dots
+ exs = xs;
+ eys = ys;
+
+ } else {
+ for (e in d.source.edge) {
+ ix = line.intersect(d.source.edge[e].offset(xs, ys));
+ if (ix.in1 && ix.in2) {
+ exs = ix.x;
+ eys = ix.y;
+
+ // also pick off the port label intersection
+ ix = line.intersect(d.source.portEdge[e].offset(xs, ys));
+ pxs = ix.x;
+ pys = ix.y;
+ break;
+ }
+ }
+ }
+
+ for (e in d.target.edge) {
+ ix = line.intersect(d.target.edge[e].offset(xt, yt));
+ if (ix.in1 && ix.in2) {
+ ext = ix.x;
+ eyt = ix.y;
+
+ // also pick off the port label intersection
+ ix = line.intersect(d.target.portEdge[e].offset(xt, yt));
+ pxt = ix.x;
+ pyt = ix.y;
+ break;
+ }
+ }
+
+ // adjust the endpoints of the link's line to match rectangles
+ var sid = safeId(d.id);
+ d3.select(this)
+ .attr('x1', exs)
+ .attr('y1', eys)
+ .attr('x2', ext)
+ .attr('y2', eyt);
+
+ d3.select('#srcPort-' + sid)
+ .attr('x', pxs - portHalfW)
+ .attr('y', pys - portHalfH);
+
+ d3.select('#tgtPort-' + sid)
+ .attr('x', pxt - portHalfW)
+ .attr('y', pyt - portHalfH);
+
+ // TODO: fit label rect to size of port number.
+ d3.select('#srcText-' + sid)
+ .attr('x', pxs - 5)
+ .attr('y', pys + 3);
+
+ d3.select('#tgtText-' + sid)
+ .attr('x', pxt - 5)
+ .attr('y', pyt + 3);
+
+ });
+
+ // position each node by translating the node (group) by x,y
+ network.node
+ .attr('transform', function(d) {
+ return translate(d.x, d.y);
+ });
+
+ }
+
+ // $('#docs-close').on('click', function() {
+ // deselectObject();
+ // return false;
+ // });
+
+ // $(document).on('click', '.select-object', function() {
+ // var obj = graph.data[$(this).data('name')];
+ // if (obj) {
+ // selectObject(obj);
+ // }
+ // return false;
+ // });
+
+ function findNodeFromData(d) {
+ var el = null;
+ network.node.filter('.' + d.class).each(function(n) {
+ if (n.id === d.id) {
+ el = d3.select(this);
+ }
+ });
+ return el;
+ }
+
+ function selectObject(obj, el) {
+ var node;
+ if (el) {
+ node = d3.select(el);
+ } else {
+ network.node.each(function(d) {
+ if (d == obj) {
+ node = d3.select(el = this);
+ }
+ });
+ }
+ if (!node) return;
+
+ if (node.classed('selected')) {
+ deselectObject();
+ flyinPane(null);
+ return;
+ }
+ deselectObject(false);
+
+ selected = {
+ obj : obj,
+ el : el
+ };
+
+ node.classed('selected', true);
+ flyinPane(obj);
+ }
+
+ function deselectObject(doResize) {
+ // Review: logic of 'resize(...)' function.
+ if (doResize || typeof doResize == 'undefined') {
+ resize(false);
+ }
+
+ // deselect all nodes in the network...
+ network.node.classed('selected', false);
+ selected = {};
+ flyinPane(null);
+ }
+
+ function flyinPane(obj) {
+ var pane = d3.select('#flyout'),
+ url;
+
+ if (obj) {
+ // go get details of the selected object from the server...
+ url = detailJsonUrl(obj.id);
+ d3.json(url, function (err, data) {
+ if (err) {
+ alert('Oops! Error reading JSON...\n\n' +
+ 'URL: ' + url + '\n\n' +
+ 'Error: ' + err.message);
+ return;
+ }
+// console.log("JSON data... " + url);
+// console.log(data);
+
+ displayDetails(data, pane);
+ });
+
+ } else {
+ // hide pane
+ pane.transition().duration(750)
+ .style('right', '-320px')
+ .style('opacity', 0.0);
+ }
+ }
+
+ function displayDetails(data, pane) {
+ $('#flyout').empty();
+
+ var title = pane.append("h2"),
+ table = pane.append("table"),
+ tbody = table.append("tbody");
+
+ $('<img src="img/' + data.type + '.png">').appendTo(title);
+ $('<span>').attr('class', 'icon').text(data.id).appendTo(title);
+
+
+ // TODO: consider using d3 data bind to TR/TD
+
+ data.propOrder.forEach(function(p) {
+ if (p === '-') {
+ addSep(tbody);
+ } else {
+ addProp(tbody, p, data.props[p]);
+ }
+ });
+
+ function addSep(tbody) {
+ var tr = tbody.append('tr');
+ $('<hr>').appendTo(tr.append('td').attr('colspan', 2));
+ }
+
+ function addProp(tbody, label, value) {
+ var tr = tbody.append('tr');
+
+ tr.append('td')
+ .attr('class', 'label')
+ .text(label + ' :');
+
+ tr.append('td')
+ .attr('class', 'value')
+ .text(value);
+ }
+
+ // show pane
+ pane.transition().duration(750)
+ .style('right', '20px')
+ .style('opacity', 1.0);
+ }
+
+ function highlightObject(obj) {
+ if (obj) {
+ if (obj != highlighted) {
+ // TODO set or clear "inactive" class on nodes, based on criteria
+ network.node.classed('inactive', function(d) {
+ // return (obj !== d &&
+ // d.relation(obj.id));
+ return (obj !== d);
+ });
+ // TODO: same with links
+ network.link.classed('inactive', function(d) {
+ return (obj !== d.source && obj !== d.target);
+ });
+ }
+ highlighted = obj;
+ } else {
+ if (highlighted) {
+ // clear the inactive flag (no longer suppressed visually)
+ network.node.classed('inactive', false);
+ network.link.classed('inactive', false);
+ }
+ highlighted = null;
+
+ }
+ }
+
+ function hoverObject(obj) {
+ if (obj) {
+ hovered = obj;
+ } else {
+ if (hovered) {
+ hovered = null;
+ }
+ }
+ }
+
+
+ function resize() {
+ netView.height = window.innerHeight - config.mastHeight;
+ netView.width = window.innerWidth;
+ $('#view')
+ .css('height', netView.height + 'px')
+ .css('width', netView.width + 'px');
+
+ network.forceWidth = netView.width - config.force.marginLR;
+ network.forceHeight = netView.height - config.force.marginTB;
+ }
+
+ // ======================================================================
+ // register with the UI framework
+
+ onos.ui.addView('topo', {
+ load: loadNetworkView
+ });
+
+
+}(ONOS));
+
diff --git a/web/gui/src/main/webapp/module-template.js b/web/gui/src/main/webapp/topo2.css
similarity index 71%
rename from web/gui/src/main/webapp/module-template.js
rename to web/gui/src/main/webapp/topo2.css
index 3de7d79..88fcd94 100644
--- a/web/gui/src/main/webapp/module-template.js
+++ b/web/gui/src/main/webapp/topo2.css
@@ -15,22 +15,12 @@
*/
/*
- Module template file.
+ ONOS GUI -- Topology view -- CSS file
@author Simon Hunt
*/
-(function (onos) {
- 'use strict';
+svg #topo-bg {
+ opacity: 0.5;
+}
- var api = onos.api;
-
- // == define your functions here.....
-
-
- // == register views here, with links to lifecycle callbacks
-
-// api.addView('view-id', {/* callbacks */});
-
-
-}(ONOS));
diff --git a/web/gui/src/main/webapp/topo2.js b/web/gui/src/main/webapp/topo2.js
new file mode 100644
index 0000000..fcc4bb4
--- /dev/null
+++ b/web/gui/src/main/webapp/topo2.js
@@ -0,0 +1,173 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ ONOS network topology viewer - version 1.1
+
+ @author Simon Hunt
+ */
+
+(function (onos) {
+ 'use strict';
+
+ // configuration data
+ var config = {
+ useLiveData: false,
+ debugOn: false,
+ debug: {
+ showNodeXY: false,
+ showKeyHandler: true
+ },
+ options: {
+ layering: true,
+ collisionPrevention: true,
+ showBackground: true
+ },
+ backgroundUrl: 'img/us-map.png',
+ data: {
+ live: {
+ jsonUrl: 'rs/topology/graph',
+ detailPrefix: 'rs/topology/graph/',
+ detailSuffix: ''
+ },
+ fake: {
+ jsonUrl: 'json/network2.json',
+ detailPrefix: 'json/',
+ detailSuffix: '.json'
+ }
+ },
+ iconUrl: {
+ device: 'img/device.png',
+ host: 'img/host.png',
+ pkt: 'img/pkt.png',
+ opt: 'img/opt.png'
+ },
+ force: {
+ marginLR: 20,
+ marginTB: 20,
+ translate: function() {
+ return 'translate(' +
+ config.force.marginLR + ',' +
+ config.force.marginTB + ')';
+ }
+ }
+ };
+
+ // radio buttons
+ var btnSet = [
+ { id: 'showAll', text: 'All Layers' },
+ { id: 'showPkt', text: 'Packet Only' },
+ { id: 'showOpt', text: 'Optical Only' }
+ ];
+
+ // state variables
+ var svg,
+ bgImg,
+ network = {},
+ selected = {},
+ highlighted = null,
+ hovered = null,
+ viewMode = 'showAll',
+ portLabelsOn = false;
+
+
+ // ==============================
+ // Private functions
+
+ // set the size of the SVG layer (or other element) to that of the view
+ function setSize(view, el) {
+ var thing = el || svg;
+ thing.attr({
+ width: view.width(),
+ height: view.height()
+ });
+ }
+
+ function doRadio(view, id) {
+ showAllLayers();
+ if (id === 'showPkt') {
+ showPacketLayer();
+ } else if (id === 'showOpt') {
+ showOpticalLayer();
+ }
+ }
+
+ function showAllLayers() {
+// network.node.classed('inactive', false);
+// network.link.classed('inactive', false);
+// d3.selectAll('svg .port').classed('inactive', false);
+// d3.selectAll('svg .portText').classed('inactive', false);
+ alert('show all layers');
+ }
+
+ function showPacketLayer() {
+ alert('show packet layer');
+ }
+
+ function showOpticalLayer() {
+ alert('show optical layer');
+ }
+
+ // ==============================
+ // View life-cycle callbacks
+
+ function preload(view, ctx) {
+ var w = view.width(),
+ h = view.height(),
+ idBg = view.uid('bg'),
+ showBg = config.options.showBackground ? 'visible' : 'hidden';
+
+ // NOTE: view.$div is a D3 selection of the view's div
+ svg = view.$div.append('svg');
+ setSize(view);
+ svg.append('g')
+ .attr('transform', config.force.translate());
+
+ // load the background image
+ bgImg = svg.append('svg:image')
+ .attr({
+ id: idBg,
+ width: w,
+ height: h,
+ 'xlink:href': config.backgroundUrl
+ })
+ .style({
+ visibility: showBg
+ });
+ }
+
+
+ function load(view, ctx) {
+ view.setRadio(btnSet, doRadio);
+
+ }
+
+ function resize(view, ctx) {
+ setSize(view);
+ setSize(view, bgImg);
+ }
+
+
+ // ==============================
+ // View registration
+
+ onos.ui.addView('topo', {
+ preload: preload,
+ load: load,
+ resize: resize
+ });
+
+}(ONOS));