Merge branch 'master' of ssh://gerrit.onlab.us:29418/onos-next
diff --git a/.gitignore b/.gitignore
index 0b28617..8f725d6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -8,5 +8,6 @@
.checkstyle
target
*.iml
+*.pyc
dependency-reduced-pom.xml
.idea
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 cfa1a63..f6a0f04 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,14 +16,24 @@
package org.onlab.onos.calendar;
import java.net.URI;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
import org.onlab.onos.net.ConnectPoint;
import org.onlab.onos.net.DeviceId;
+import org.onlab.onos.net.intent.Intent;
+import org.onlab.onos.net.intent.IntentEvent;
+import org.onlab.onos.net.intent.IntentId;
+import org.onlab.onos.net.intent.IntentListener;
import org.onlab.onos.net.intent.IntentService;
+import org.onlab.onos.net.intent.IntentState;
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;
@@ -31,10 +41,15 @@
import org.onlab.onos.net.flow.TrafficTreatment;
import org.onlab.onos.net.intent.PointToPointIntent;
import org.onlab.packet.Ethernet;
+
import static org.onlab.onos.net.PortNumber.portNumber;
import static org.onlab.onos.net.flow.DefaultTrafficTreatment.builder;
+import static org.onlab.onos.net.intent.IntentState.FAILED;
+import static org.onlab.onos.net.intent.IntentState.INSTALLED;
+import static org.onlab.onos.net.intent.IntentState.WITHDRAWN;
import static org.slf4j.LoggerFactory.getLogger;
+
import org.slf4j.Logger;
/**
@@ -44,6 +59,7 @@
public class BandwidthCalendarResource extends BaseResource {
private static final Logger log = getLogger(BandwidthCalendarResource.class);
+ private static final long TIMEOUT = 5; // seconds
@javax.ws.rs.Path("/{src}/{dst}/{srcPort}/{dstPort}/{bandwidth}")
@POST
@@ -55,7 +71,7 @@
log.info("Receiving Create Intent request...");
log.info("Path Constraints: Src = {} SrcPort = {} Dest = {} DestPort = {} BW = {}",
- src, srcPort, dst, dstPort, bandwidth);
+ src, srcPort, dst, dstPort, bandwidth);
IntentService service = get(IntentService.class);
@@ -66,36 +82,50 @@
TrafficTreatment treatment = builder().build();
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";
+ new PointToPointIntent(appId(), selector, treatment,
+ srcPoint, dstPoint);
- return Response.ok(reply).build();
+ CountDownLatch latch = new CountDownLatch(1);
+ InternalIntentListener listener = new InternalIntentListener(intentP2P, service, latch);
+ service.addListener(listener);
+ service.submit(intentP2P);
+ try {
+ if (latch.await(TIMEOUT, TimeUnit.SECONDS)) {
+ log.info("Submitted Calendar App intent: src = {}; dst = {}; " +
+ "srcPort = {}; dstPort = {}; intentID = {}",
+ src, dst, srcPort, dstPort, intentP2P.id());
+ String reply = intentP2P.id() + " " + listener.getState() + "\n";
+ return Response.ok(reply).build();
+ }
+ } catch (InterruptedException e) {
+ log.warn("Interrupted while waiting for intent {} status", intentP2P.id());
+ }
+ return Response.status(Response.Status.INTERNAL_SERVER_ERROR).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();
+ log.info("Receiving Teardown request for {}", intentId);
+ IntentService service = get(IntentService.class);
+ Intent intent = service.getIntent(IntentId.valueOf(Long.parseLong(intentId)));
+ if (intent != null) {
+ service.withdraw(intent);
+ String reply = "ok\n";
+ return Response.ok(reply).build();
+ }
+ return Response.status(Response.Status.NOT_FOUND).build();
}
@javax.ws.rs.Path("/modification/{intentId}/{bandwidth}")
@POST
public Response modifyBandwidth(@PathParam("intentId") String intentId,
- @PathParam("bandwidth") String bandwidth) {
+ @PathParam("bandwidth") String bandwidth) {
log.info("Receiving Modify request...");
log.info("Modify bw for intentId = {} with new bandwidth = {}", intentId, bandwidth);
- String reply = "ok\n";
+ String reply = "ok\n";
return Response.ok(reply).build();
}
@@ -115,4 +145,34 @@
protected ApplicationId appId() {
return get(CoreService.class).registerApplication("org.onlab.onos.calendar");
}
+
+ // Auxiliary listener to wait until the given intent reaches the installed or failed states.
+ private final class InternalIntentListener implements IntentListener {
+ private final Intent intent;
+ private final IntentService service;
+ private final CountDownLatch latch;
+ private IntentState state;
+
+ private InternalIntentListener(Intent intent, IntentService service,
+ CountDownLatch latch) {
+ this.intent = intent;
+ this.service = service;
+ this.latch = latch;
+ }
+
+ @Override
+ public void event(IntentEvent event) {
+ if (event.subject().equals(intent)) {
+ state = service.getIntentState(intent.id());
+ if (state == INSTALLED || state == FAILED || state == WITHDRAWN) {
+ latch.countDown();
+ }
+ service.removeListener(this);
+ }
+ }
+
+ public IntentState getState() {
+ return state;
+ }
+ }
}
diff --git a/apps/config/src/main/java/org/onlab/onos/config/NetworkConfigReader.java b/apps/config/src/main/java/org/onlab/onos/config/NetworkConfigReader.java
index f015e36..846e83c 100644
--- a/apps/config/src/main/java/org/onlab/onos/config/NetworkConfigReader.java
+++ b/apps/config/src/main/java/org/onlab/onos/config/NetworkConfigReader.java
@@ -50,7 +50,10 @@
private final Logger log = getLogger(getClass());
- private static final String DEFAULT_CONFIG_FILE = "config/addresses.json";
+ // Current working dir seems to be /opt/onos/apache-karaf-3.0.2
+ // TODO: Set the path to /opt/onos/config
+ private static final String CONFIG_DIR = "../config";
+ private static final String DEFAULT_CONFIG_FILE = "addresses.json";
private String configFileName = DEFAULT_CONFIG_FILE;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
@@ -60,52 +63,9 @@
protected void activate() {
log.info("Started network config reader");
- log.info("Config file set to {}", configFileName);
-
AddressConfiguration config = readNetworkConfig();
-
if (config != null) {
- for (AddressEntry entry : config.getAddresses()) {
-
- ConnectPoint cp = new ConnectPoint(
- DeviceId.deviceId(dpidToUri(entry.getDpid())),
- PortNumber.portNumber(entry.getPortNumber()));
-
- Set<InterfaceIpAddress> interfaceIpAddresses = new HashSet<>();
-
- for (String strIp : entry.getIpAddresses()) {
- // Get the IP address and the subnet mask length
- try {
- String[] splits = strIp.split("/");
- if (splits.length != 2) {
- throw new IllegalArgumentException("Invalid IP address and prefix length format");
- }
- // NOTE: IpPrefix will mask-out the bits after the prefix length.
- IpPrefix subnet = IpPrefix.valueOf(strIp);
- IpAddress addr = IpAddress.valueOf(splits[0]);
- InterfaceIpAddress ia =
- new InterfaceIpAddress(addr, subnet);
- interfaceIpAddresses.add(ia);
- } catch (IllegalArgumentException e) {
- log.warn("Bad format for IP address in config: {}", strIp);
- }
- }
-
- MacAddress macAddress = null;
- if (entry.getMacAddress() != null) {
- try {
- macAddress = MacAddress.valueOf(entry.getMacAddress());
- } catch (IllegalArgumentException e) {
- log.warn("Bad format for MAC address in config: {}",
- entry.getMacAddress());
- }
- }
-
- PortAddresses addresses = new PortAddresses(cp,
- interfaceIpAddresses, macAddress);
-
- hostAdminService.bindAddressesToPort(addresses);
- }
+ applyNetworkConfig(config);
}
}
@@ -114,12 +74,17 @@
log.info("Stopped");
}
+ /**
+ * Reads the network configuration.
+ *
+ * @return the network configuration on success, otherwise null
+ */
private AddressConfiguration readNetworkConfig() {
- File configFile = new File(configFileName);
-
+ File configFile = new File(CONFIG_DIR, configFileName);
ObjectMapper mapper = new ObjectMapper();
try {
+ log.info("Loading config: {}", configFile.getAbsolutePath());
AddressConfiguration config =
mapper.readValue(configFile, AddressConfiguration.class);
@@ -127,12 +92,58 @@
} catch (FileNotFoundException e) {
log.warn("Configuration file not found: {}", configFileName);
} catch (IOException e) {
- log.error("Unable to read config from file:", e);
+ log.error("Error loading configuration", e);
}
return null;
}
+ /**
+ * Applies the network configuration.
+ *
+ * @param config the network configuration to apply
+ */
+ private void applyNetworkConfig(AddressConfiguration config) {
+ for (AddressEntry entry : config.getAddresses()) {
+ ConnectPoint cp = new ConnectPoint(
+ DeviceId.deviceId(dpidToUri(entry.getDpid())),
+ PortNumber.portNumber(entry.getPortNumber()));
+
+ Set<InterfaceIpAddress> interfaceIpAddresses = new HashSet<>();
+ for (String strIp : entry.getIpAddresses()) {
+ // Get the IP address and the subnet mask length
+ try {
+ String[] splits = strIp.split("/");
+ if (splits.length != 2) {
+ throw new IllegalArgumentException("Invalid IP address and prefix length format");
+ }
+ // NOTE: IpPrefix will mask-out the bits after the prefix length.
+ IpPrefix subnet = IpPrefix.valueOf(strIp);
+ IpAddress addr = IpAddress.valueOf(splits[0]);
+ InterfaceIpAddress ia =
+ new InterfaceIpAddress(addr, subnet);
+ interfaceIpAddresses.add(ia);
+ } catch (IllegalArgumentException e) {
+ log.warn("Bad format for IP address in config: {}", strIp);
+ }
+ }
+
+ MacAddress macAddress = null;
+ if (entry.getMacAddress() != null) {
+ try {
+ macAddress = MacAddress.valueOf(entry.getMacAddress());
+ } catch (IllegalArgumentException e) {
+ log.warn("Bad format for MAC address in config: {}",
+ entry.getMacAddress());
+ }
+ }
+
+ PortAddresses addresses = new PortAddresses(cp,
+ interfaceIpAddresses, macAddress);
+ hostAdminService.bindAddressesToPort(addresses);
+ }
+ }
+
private static String dpidToUri(String dpid) {
return "of:" + dpid.replace(":", "");
}
diff --git a/apps/sdnip/src/main/resources/config-examples/addresses.json b/apps/config/src/main/resources/addresses.json
similarity index 100%
rename from apps/sdnip/src/main/resources/config-examples/addresses.json
rename to apps/config/src/main/resources/addresses.json
diff --git a/apps/config/src/main/resources/config.json b/apps/config/src/main/resources/config.json
deleted file mode 100644
index ca4be83..0000000
--- a/apps/config/src/main/resources/config.json
+++ /dev/null
@@ -1,21 +0,0 @@
-{
- "interfaces" : [
- {
- "dpid" : "00:00:00:00:00:00:01",
- "port" : "1",
- "ips" : ["192.168.10.101/24"],
- "mac" : "00:00:00:11:22:33"
- },
- {
- "dpid" : "00:00:00:00:00:00:02",
- "port" : "1",
- "ips" : ["192.168.20.101/24", "192.168.30.101/24"]
- },
- {
- "dpid" : "00:00:00:00:00:00:03",
- "port" : "1",
- "ips" : ["10.1.0.1/16"],
- "mac" : "00:00:00:00:00:01"
- }
- ]
-}
diff --git a/apps/demo/pom.xml b/apps/demo/pom.xml
new file mode 100644
index 0000000..fbc7f91
--- /dev/null
+++ b/apps/demo/pom.xml
@@ -0,0 +1,121 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ 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.
+ -->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.onlab.onos</groupId>
+ <artifactId>onos-apps</artifactId>
+ <version>1.0.0-SNAPSHOT</version>
+ <relativePath>../pom.xml</relativePath>
+ </parent>
+
+ <artifactId>onos-app-demo</artifactId>
+ <packaging>bundle</packaging>
+
+ <description>ONOS demo app bundle</description>
+
+ <properties>
+ <web.context>/onos/demo</web.context>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.compendium</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.onlab.onos</groupId>
+ <artifactId>onlab-rest</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.onlab.onos</groupId>
+ <artifactId>onos-rest</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>com.sun.jersey</groupId>
+ <artifactId>jersey-servlet</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.sun.jersey.jersey-test-framework</groupId>
+ <artifactId>jersey-test-framework-core</artifactId>
+ <version>1.18.1</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.sun.jersey.jersey-test-framework</groupId>
+ <artifactId>jersey-test-framework-grizzly2</artifactId>
+ <version>1.18.1</version>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>com.fasterxml.jackson.core</groupId>
+ <artifactId>jackson-databind</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>com.fasterxml.jackson.core</groupId>
+ <artifactId>jackson-annotations</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.core</artifactId>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <extensions>true</extensions>
+ <configuration>
+ <instructions>
+ <_wab>src/main/webapp/</_wab>
+ <Bundle-SymbolicName>
+ ${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,
+ com.sun.jersey.spi.container.servlet,
+ com.sun.jersey.server.impl.container.servlet,
+ com.fasterxml.jackson.databind,
+ com.fasterxml.jackson.databind.node,
+ com.google.common.*,
+ org.onlab.packet.*,
+ org.onlab.rest.*,
+ org.onlab.onos.*
+ </Import-Package>
+ <Web-ContextPath>${web.context}</Web-ContextPath>
+ </instructions>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
diff --git a/apps/demo/src/main/java/org/onlab/onos/demo/DemoAPI.java b/apps/demo/src/main/java/org/onlab/onos/demo/DemoAPI.java
new file mode 100644
index 0000000..2a267f3
--- /dev/null
+++ b/apps/demo/src/main/java/org/onlab/onos/demo/DemoAPI.java
@@ -0,0 +1,21 @@
+package org.onlab.onos.demo;
+
+/**
+ * Simple demo api interface.
+ */
+public interface DemoAPI {
+
+ enum InstallType { MESH, RANDOM };
+
+ /**
+ * Installs intents based on the installation type.
+ * @param type the installation type.
+ */
+ void setup(InstallType type);
+
+ /**
+ * Uninstalls all existing intents.
+ */
+ void tearDown();
+
+}
diff --git a/apps/demo/src/main/java/org/onlab/onos/demo/DemoInstaller.java b/apps/demo/src/main/java/org/onlab/onos/demo/DemoInstaller.java
new file mode 100644
index 0000000..ca2d6db
--- /dev/null
+++ b/apps/demo/src/main/java/org/onlab/onos/demo/DemoInstaller.java
@@ -0,0 +1,142 @@
+/*
+ * 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.demo;
+
+import com.google.common.collect.Lists;
+import com.google.common.util.concurrent.ThreadFactoryBuilder;
+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.core.ApplicationId;
+import org.onlab.onos.core.CoreService;
+import org.onlab.onos.net.Host;
+import org.onlab.onos.net.flow.DefaultTrafficSelector;
+import org.onlab.onos.net.flow.DefaultTrafficTreatment;
+import org.onlab.onos.net.flow.TrafficSelector;
+import org.onlab.onos.net.flow.TrafficTreatment;
+import org.onlab.onos.net.host.HostService;
+import org.onlab.onos.net.intent.HostToHostIntent;
+import org.onlab.onos.net.intent.Intent;
+import org.onlab.onos.net.intent.IntentService;
+import org.slf4j.Logger;
+
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Application to set up demos.
+ */
+@Component(immediate = true)
+@Service
+public class DemoInstaller implements DemoAPI {
+
+ private final Logger log = getLogger(getClass());
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected CoreService coreService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected IntentService intentService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected HostService hostService;
+
+ private ExecutorService worker;
+
+ private ApplicationId appId;
+
+ private final Set<Intent> existingIntents = new HashSet<>();
+
+
+
+ @Activate
+ public void activate() {
+ appId = coreService.registerApplication("org.onlab.onos.demo.installer");
+ worker = Executors.newFixedThreadPool(1,
+ new ThreadFactoryBuilder()
+ .setNameFormat("demo-app-worker")
+ .build());
+ log.info("Started with Application ID {}", appId.id());
+ }
+
+ @Deactivate
+ public void deactivate() {
+ worker.shutdownNow();
+ log.info("Stopped");
+ }
+
+ @Override
+ public void setup(InstallType type) {
+ switch (type) {
+ case MESH:
+ log.debug("Installing mesh intents");
+ worker.execute(new MeshInstaller());
+ break;
+ case RANDOM:
+ throw new IllegalArgumentException("Not yet implemented.");
+ default:
+ throw new IllegalArgumentException("What is it you want exactly?");
+ }
+ }
+
+ @Override
+ public void tearDown() {
+ worker.submit(new UnInstaller());
+ }
+
+
+ private class MeshInstaller implements Runnable {
+
+ @Override
+ public void run() {
+ TrafficSelector selector = DefaultTrafficSelector.builder().build();
+ TrafficTreatment treatment = DefaultTrafficTreatment.builder().build();
+
+ List<Host> hosts = Lists.newArrayList(hostService.getHosts());
+ while (!hosts.isEmpty()) {
+ Host src = hosts.remove(0);
+ for (Host dst : hosts) {
+ HostToHostIntent intent = new HostToHostIntent(appId, src.id(), dst.id(),
+ selector, treatment,
+ null);
+ existingIntents.add(intent);
+ intentService.submit(intent);
+ }
+ }
+ }
+ }
+
+
+ private class UnInstaller implements Runnable {
+ @Override
+ public void run() {
+ for (Intent i : existingIntents) {
+ intentService.withdraw(i);
+ }
+ }
+ }
+}
+
+
diff --git a/apps/demo/src/main/java/org/onlab/onos/demo/DemoResource.java b/apps/demo/src/main/java/org/onlab/onos/demo/DemoResource.java
new file mode 100644
index 0000000..f533072
--- /dev/null
+++ b/apps/demo/src/main/java/org/onlab/onos/demo/DemoResource.java
@@ -0,0 +1,54 @@
+package org.onlab.onos.demo;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.onlab.rest.BaseResource;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Rest API for demos.
+ */
+@Path("intents")
+public class DemoResource extends BaseResource {
+
+
+ @POST
+ @Path("setup")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response setup(InputStream input) throws IOException {
+ ObjectMapper mapper = new ObjectMapper();
+ JsonNode cfg = mapper.readTree(input);
+ if (!cfg.has("type")) {
+ return Response.status(Response.Status.BAD_REQUEST)
+ .entity("Expected type field containing either mesh or random.").build();
+ }
+
+ DemoAPI.InstallType type = DemoAPI.InstallType.valueOf(
+ cfg.get("type").asText().toUpperCase());
+ DemoAPI demo = get(DemoAPI.class);
+ demo.setup(type);
+
+ return Response.ok(mapper.createObjectNode().toString()).build();
+ }
+
+ @GET
+ @Path("teardown")
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response tearDown() throws IOException {
+ ObjectMapper mapper = new ObjectMapper();
+ DemoAPI demo = get(DemoAPI.class);
+ demo.tearDown();
+ return Response.ok(mapper.createObjectNode().toString()).build();
+ }
+
+}
diff --git a/apps/demo/src/main/java/org/onlab/onos/demo/package-info.java b/apps/demo/src/main/java/org/onlab/onos/demo/package-info.java
new file mode 100644
index 0000000..b60eb15
--- /dev/null
+++ b/apps/demo/src/main/java/org/onlab/onos/demo/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+
+/**
+ * Demo applications live here.
+ */
+package org.onlab.onos.demo;
diff --git a/apps/demo/src/main/webapp/WEB-INF/web.xml b/apps/demo/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..429287e
--- /dev/null
+++ b/apps/demo/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ 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.
+ -->
+<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee"
+ xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
+ xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
+ id="ONOS" version="2.5">
+ <display-name>ONOS DEMO APP API v1.0</display-name>
+
+ <servlet>
+ <servlet-name>JAX-RS Service</servlet-name>
+ <servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
+ <init-param>
+ <param-name>com.sun.jersey.config.property.resourceConfigClass</param-name>
+ <param-value>com.sun.jersey.api.core.ClassNamesResourceConfig</param-value>
+ </init-param>
+ <init-param>
+ <param-name>com.sun.jersey.config.property.classnames</param-name>
+ <param-value>
+ org.onlab.onos.demo.DemoResource
+ </param-value>
+ </init-param>
+ <load-on-startup>1</load-on-startup>
+ </servlet>
+
+ <servlet-mapping>
+ <servlet-name>JAX-RS Service</servlet-name>
+ <url-pattern>/*</url-pattern>
+ </servlet-mapping>
+
+</web-app>
\ No newline at end of file
diff --git a/apps/pom.xml b/apps/pom.xml
index 3084916..c021ca4 100644
--- a/apps/pom.xml
+++ b/apps/pom.xml
@@ -44,6 +44,7 @@
<module>optical</module>
<module>metrics</module>
<module>oecfg</module>
+ <module>demo</module>
</modules>
<properties>
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 2fcd1fe..7262c42 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
@@ -37,24 +37,31 @@
*/
public class SdnIpConfigReader implements SdnIpConfigService {
- private static final Logger log = LoggerFactory.getLogger(SdnIpConfigReader.class);
+ private final Logger log = LoggerFactory.getLogger(getClass());
- private static final String DEFAULT_CONFIG_FILE = "config/sdnip.json";
+ // Current working dir seems to be /opt/onos/apache-karaf-3.0.2
+ // TODO: Set the path to /opt/onos/config
+ private static final String CONFIG_DIR = "../config";
+ private static final String DEFAULT_CONFIG_FILE = "sdnip.json";
private String configFileName = DEFAULT_CONFIG_FILE;
+
private Map<String, BgpSpeaker> bgpSpeakers = new ConcurrentHashMap<>();
private Map<IpAddress, BgpPeer> bgpPeers = new ConcurrentHashMap<>();
/**
- * Reads the info contained in the configuration file.
+ * Reads SDN-IP related information contained in the configuration file.
*
- * @param configFilename The name of configuration file for SDN-IP application.
+ * @param configFilename the name of the configuration file for the SDN-IP
+ * application
*/
private void readConfiguration(String configFilename) {
- File gatewaysFile = new File(configFilename);
+ File configFile = new File(CONFIG_DIR, configFilename);
ObjectMapper mapper = new ObjectMapper();
try {
- Configuration config = mapper.readValue(gatewaysFile, Configuration.class);
+ log.info("Loading config: {}", configFile.getAbsolutePath());
+ Configuration config = mapper.readValue(configFile,
+ Configuration.class);
for (BgpSpeaker speaker : config.getBgpSpeakers()) {
bgpSpeakers.put(speaker.name(), speaker);
}
@@ -64,13 +71,11 @@
} catch (FileNotFoundException e) {
log.warn("Configuration file not found: {}", configFileName);
} catch (IOException e) {
- log.error("Error reading JSON file", e);
+ log.error("Error loading configuration", e);
}
}
public void init() {
- log.debug("Config file set to {}", configFileName);
-
readConfiguration(configFileName);
}
diff --git a/apps/sdnip/src/main/resources/config-examples/README b/apps/sdnip/src/main/resources/config-examples/README
index 502285e..7642a4d 100644
--- a/apps/sdnip/src/main/resources/config-examples/README
+++ b/apps/sdnip/src/main/resources/config-examples/README
@@ -1 +1,5 @@
-ONOS looks for these config files by default in $KARAF_LOG/config/
\ No newline at end of file
+The SDN-IP configuration files should be copied to directory
+ $ONOS_HOME/tools/package/config
+
+After deployment and starting up the ONOS cluster, ONOS looks for these
+configuration files in /opt/onos/config on each cluster member.
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 e4fc5aa..5c513e3 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
@@ -27,6 +27,7 @@
import org.onlab.onos.net.intent.constraint.LambdaConstraint;
import org.onlab.onos.net.resource.Bandwidth;
import org.onlab.packet.Ethernet;
+import org.onlab.packet.IpPrefix;
import org.onlab.packet.MacAddress;
import static com.google.common.base.Strings.isNullOrEmpty;
@@ -48,6 +49,26 @@
required = false, multiValued = false)
private String ethTypeString = "";
+ @Option(name = "--ipProto", description = "IP Protocol",
+ required = false, multiValued = false)
+ private String ipProtoString = null;
+
+ @Option(name = "--ipSrc", description = "Source IP Address",
+ required = false, multiValued = false)
+ private String srcIpString = null;
+
+ @Option(name = "--ipDst", description = "Destination IP Address",
+ required = false, multiValued = false)
+ private String dstIpString = null;
+
+ @Option(name = "--tcpSrc", description = "Source TCP Port",
+ required = false, multiValued = false)
+ private String srcTcpString = null;
+
+ @Option(name = "--tcpDst", description = "Destination TCP Port",
+ required = false, multiValued = false)
+ private String dstTcpString = null;
+
@Option(name = "-b", aliases = "--bandwidth", description = "Bandwidth",
required = false, multiValued = false)
private String bandwidthString = "";
@@ -79,6 +100,26 @@
selectorBuilder.matchEthDst(MacAddress.valueOf(dstMacString));
}
+ if (!isNullOrEmpty(ipProtoString)) {
+ selectorBuilder.matchIPProtocol((byte) Short.parseShort(ipProtoString));
+ }
+
+ if (!isNullOrEmpty(srcIpString)) {
+ selectorBuilder.matchIPSrc(IpPrefix.valueOf(srcIpString));
+ }
+
+ if (!isNullOrEmpty(dstIpString)) {
+ selectorBuilder.matchIPDst(IpPrefix.valueOf(dstIpString));
+ }
+
+ if (!isNullOrEmpty(srcTcpString)) {
+ selectorBuilder.matchTcpSrc((short) Integer.parseInt(srcTcpString));
+ }
+
+ if (!isNullOrEmpty(dstTcpString)) {
+ selectorBuilder.matchTcpSrc((short) Integer.parseInt(dstTcpString));
+ }
+
return selectorBuilder.build();
}
diff --git a/core/api/src/main/java/org/onlab/onos/net/AnnotationKeys.java b/core/api/src/main/java/org/onlab/onos/net/AnnotationKeys.java
new file mode 100644
index 0000000..5777022
--- /dev/null
+++ b/core/api/src/main/java/org/onlab/onos/net/AnnotationKeys.java
@@ -0,0 +1,50 @@
+/*
+ * 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;
+
+/**
+ * Collection of keys for annotation.
+ * Definitions of annotation keys needs to be here to avoid scattering.
+ */
+public final class AnnotationKeys {
+
+ // Prohibit instantiation
+ private AnnotationKeys() {}
+
+ /**
+ * Annotation key for latency.
+ */
+ public static final String LATENCY = "latency";
+
+ /**
+ * Returns the value annotated object for the specified annotation key.
+ * The annotated value is expected to be String that can be parsed as double.
+ * If parsing fails, the returned value will be 1.0.
+ *
+ * @param annotated annotated object whose annotated value is obtained
+ * @param key key of annotation
+ * @return double value of annotated object for the specified key
+ */
+ public static double getAnnotatedValue(Annotated annotated, String key) {
+ double value;
+ try {
+ value = Double.parseDouble(annotated.annotations().value(key));
+ } catch (NumberFormatException e) {
+ value = 1.0;
+ }
+ return value;
+ }
+}
diff --git a/core/api/src/main/java/org/onlab/onos/net/flow/DefaultFlowEntry.java b/core/api/src/main/java/org/onlab/onos/net/flow/DefaultFlowEntry.java
index c2d901b..c6cb361 100644
--- a/core/api/src/main/java/org/onlab/onos/net/flow/DefaultFlowEntry.java
+++ b/core/api/src/main/java/org/onlab/onos/net/flow/DefaultFlowEntry.java
@@ -80,6 +80,7 @@
this.state = FlowEntryState.FAILED;
this.errType = errType;
this.errCode = errCode;
+ this.lastSeen = System.currentTimeMillis();
}
@Override
diff --git a/core/api/src/main/java/org/onlab/onos/net/flow/DefaultFlowRule.java b/core/api/src/main/java/org/onlab/onos/net/flow/DefaultFlowRule.java
index 6e59cf5..f31f3c3 100644
--- a/core/api/src/main/java/org/onlab/onos/net/flow/DefaultFlowRule.java
+++ b/core/api/src/main/java/org/onlab/onos/net/flow/DefaultFlowRule.java
@@ -58,7 +58,7 @@
}
public DefaultFlowRule(DeviceId deviceId, TrafficSelector selector,
- TrafficTreatment treatement, int priority, ApplicationId appId,
+ TrafficTreatment treatment, int priority, ApplicationId appId,
int timeout, boolean permanent) {
if (priority < FlowRule.MIN_PRIORITY) {
@@ -68,7 +68,7 @@
this.deviceId = deviceId;
this.priority = priority;
this.selector = selector;
- this.treatment = treatement;
+ this.treatment = treatment;
this.appId = appId.id();
this.timeout = timeout;
this.permanent = permanent;
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 413473f..673773a 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
@@ -63,7 +63,7 @@
@Override
public int hashCode() {
- return Objects.hash(criteria);
+ return criteria.hashCode();
}
@Override
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 de2c7fd..64a56ca 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
@@ -18,7 +18,7 @@
import org.onlab.onos.core.ApplicationId;
import org.onlab.onos.net.provider.Provider;
-import com.google.common.util.concurrent.ListenableFuture;
+import java.util.concurrent.Future;
/**
* Abstraction of a flow rule provider.
@@ -58,6 +58,6 @@
* @param batch a batch of flow rules
* @return a future indicating the status of this execution
*/
- ListenableFuture<CompletedBatchOperation> executeBatch(BatchOperation<FlowRuleBatchEntry> batch);
+ Future<CompletedBatchOperation> executeBatch(BatchOperation<FlowRuleBatchEntry> batch);
}
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 bac1bab..61fe54d 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
@@ -196,7 +196,7 @@
@Override
public int hashCode() {
- return Objects.hash(port, type());
+ return Objects.hash(type(), port);
}
@Override
@@ -242,7 +242,7 @@
@Override
public int hashCode() {
- return Objects.hash(mac, type);
+ return Objects.hash(type, mac);
}
@Override
@@ -288,7 +288,7 @@
@Override
public int hashCode() {
- return Objects.hash(ethType, type());
+ return Objects.hash(type(), ethType);
}
@Override
@@ -336,7 +336,7 @@
@Override
public int hashCode() {
- return Objects.hash(ip, type);
+ return Objects.hash(type, ip);
}
@Override
@@ -382,7 +382,7 @@
@Override
public int hashCode() {
- return Objects.hash(proto, type());
+ return Objects.hash(type(), proto);
}
@Override
@@ -427,7 +427,7 @@
@Override
public int hashCode() {
- return Objects.hash(vlanPcp);
+ return Objects.hash(type(), vlanPcp);
}
@Override
@@ -474,7 +474,7 @@
@Override
public int hashCode() {
- return Objects.hash(vlanId, type());
+ return Objects.hash(type(), vlanId);
}
@Override
@@ -522,7 +522,7 @@
@Override
public int hashCode() {
- return Objects.hash(tcpPort, type);
+ return Objects.hash(type, tcpPort);
}
@Override
@@ -568,7 +568,7 @@
@Override
public int hashCode() {
- return Objects.hash(lambda, type);
+ return Objects.hash(type, lambda);
}
@Override
@@ -612,7 +612,7 @@
@Override
public int hashCode() {
- return Objects.hash(signalType, type);
+ return Objects.hash(type, signalType);
}
@Override
diff --git a/core/api/src/main/java/org/onlab/onos/net/flow/instructions/Instructions.java b/core/api/src/main/java/org/onlab/onos/net/flow/instructions/Instructions.java
index 0e77f4a..7dc0f8d 100644
--- a/core/api/src/main/java/org/onlab/onos/net/flow/instructions/Instructions.java
+++ b/core/api/src/main/java/org/onlab/onos/net/flow/instructions/Instructions.java
@@ -190,7 +190,7 @@
@Override
public int hashCode() {
- return Objects.hash(port, type());
+ return Objects.hash(type(), port);
}
@Override
diff --git a/core/api/src/main/java/org/onlab/onos/net/flow/instructions/L0ModificationInstruction.java b/core/api/src/main/java/org/onlab/onos/net/flow/instructions/L0ModificationInstruction.java
index 0d5cd81..25fe79f 100644
--- a/core/api/src/main/java/org/onlab/onos/net/flow/instructions/L0ModificationInstruction.java
+++ b/core/api/src/main/java/org/onlab/onos/net/flow/instructions/L0ModificationInstruction.java
@@ -70,7 +70,7 @@
@Override
public int hashCode() {
- return Objects.hash(lambda, type(), subtype);
+ return Objects.hash(type(), subtype, lambda);
}
@Override
diff --git a/core/api/src/main/java/org/onlab/onos/net/flow/instructions/L2ModificationInstruction.java b/core/api/src/main/java/org/onlab/onos/net/flow/instructions/L2ModificationInstruction.java
index abe19e3..20eaf6e 100644
--- a/core/api/src/main/java/org/onlab/onos/net/flow/instructions/L2ModificationInstruction.java
+++ b/core/api/src/main/java/org/onlab/onos/net/flow/instructions/L2ModificationInstruction.java
@@ -93,7 +93,7 @@
@Override
public int hashCode() {
- return Objects.hash(mac, type(), subtype);
+ return Objects.hash(type(), subtype, mac);
}
@Override
@@ -142,7 +142,7 @@
@Override
public int hashCode() {
- return Objects.hash(vlanId, type(), subtype());
+ return Objects.hash(type(), subtype(), vlanId);
}
@Override
@@ -191,7 +191,7 @@
@Override
public int hashCode() {
- return Objects.hash(vlanPcp, type(), subtype());
+ return Objects.hash(type(), subtype(), vlanPcp);
}
@Override
diff --git a/core/api/src/main/java/org/onlab/onos/net/flow/instructions/L3ModificationInstruction.java b/core/api/src/main/java/org/onlab/onos/net/flow/instructions/L3ModificationInstruction.java
index 89a8cda..e8b72e7 100644
--- a/core/api/src/main/java/org/onlab/onos/net/flow/instructions/L3ModificationInstruction.java
+++ b/core/api/src/main/java/org/onlab/onos/net/flow/instructions/L3ModificationInstruction.java
@@ -85,7 +85,7 @@
@Override
public int hashCode() {
- return Objects.hash(ip, type(), subtype());
+ return Objects.hash(type(), subtype(), ip);
}
@Override
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 2269aa0..2a4aaeb 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.Collections;
import java.util.List;
import static com.google.common.base.Preconditions.checkNotNull;
@@ -61,7 +62,7 @@
Collection<NetworkResource> resources,
TrafficSelector selector,
TrafficTreatment treatment) {
- this(id, appId, resources, selector, treatment, null);
+ this(id, appId, resources, selector, treatment, Collections.emptyList());
}
/**
@@ -87,7 +88,7 @@
super(id, appId, resources);
this.selector = checkNotNull(selector);
this.treatment = checkNotNull(treatment);
- this.constraints = constraints;
+ this.constraints = checkNotNull(constraints);
}
/**
@@ -97,7 +98,7 @@
super();
this.selector = null;
this.treatment = null;
- this.constraints = null;
+ this.constraints = Collections.emptyList();
}
/**
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 893270a..24a3bca 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,7 @@
import org.onlab.onos.net.flow.TrafficSelector;
import org.onlab.onos.net.flow.TrafficTreatment;
+import java.util.Collections;
import java.util.List;
import static com.google.common.base.Preconditions.checkNotNull;
@@ -46,7 +47,7 @@
public HostToHostIntent(ApplicationId appId, HostId one, HostId two,
TrafficSelector selector,
TrafficTreatment treatment) {
- this(appId, one, two, selector, treatment, null);
+ this(appId, one, two, selector, treatment, Collections.emptyList());
}
/**
diff --git a/core/api/src/main/java/org/onlab/onos/net/intent/LinkCollectionIntent.java b/core/api/src/main/java/org/onlab/onos/net/intent/LinkCollectionIntent.java
index b4fdcfb..ce1f6b1 100644
--- a/core/api/src/main/java/org/onlab/onos/net/intent/LinkCollectionIntent.java
+++ b/core/api/src/main/java/org/onlab/onos/net/intent/LinkCollectionIntent.java
@@ -22,6 +22,7 @@
import org.onlab.onos.net.flow.TrafficSelector;
import org.onlab.onos.net.flow.TrafficTreatment;
+import java.util.Collections;
import java.util.List;
import java.util.Set;
@@ -51,7 +52,7 @@
TrafficTreatment treatment,
Set<Link> links,
ConnectPoint egressPoint) {
- this(appId, selector , treatment, links, egressPoint, null);
+ this(appId, selector , treatment, links, egressPoint, Collections.emptyList());
}
/**
diff --git a/core/api/src/main/java/org/onlab/onos/net/intent/MultiPointToSinglePointIntent.java b/core/api/src/main/java/org/onlab/onos/net/intent/MultiPointToSinglePointIntent.java
index 90907fb..8c8cbb0 100644
--- a/core/api/src/main/java/org/onlab/onos/net/intent/MultiPointToSinglePointIntent.java
+++ b/core/api/src/main/java/org/onlab/onos/net/intent/MultiPointToSinglePointIntent.java
@@ -22,6 +22,7 @@
import org.onlab.onos.net.flow.TrafficSelector;
import org.onlab.onos.net.flow.TrafficTreatment;
+import java.util.Collections;
import java.util.List;
import java.util.Set;
@@ -55,14 +56,7 @@
TrafficTreatment treatment,
Set<ConnectPoint> ingressPoints,
ConnectPoint egressPoint) {
- super(id(MultiPointToSinglePointIntent.class, selector, treatment,
- ingressPoints, egressPoint), appId, null, selector, treatment);
-
- checkNotNull(ingressPoints);
- checkArgument(!ingressPoints.isEmpty(), "Ingress point set cannot be empty");
-
- this.ingressPoints = Sets.newHashSet(ingressPoints);
- this.egressPoint = checkNotNull(egressPoint);
+ this(appId, selector, treatment, ingressPoints, egressPoint, Collections.emptyList());
}
/**
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 9f8816d..7a0e365 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
@@ -15,6 +15,7 @@
*/
package org.onlab.onos.net.intent;
+import java.util.Collections;
import java.util.List;
import com.google.common.base.MoreObjects;
@@ -42,9 +43,7 @@
*/
public PathIntent(ApplicationId appId, TrafficSelector selector,
TrafficTreatment treatment, Path path) {
- super(id(PathIntent.class, selector, treatment, path), appId,
- resources(path.links()), selector, treatment);
- this.path = path;
+ this(appId, selector, treatment, path, Collections.emptyList());
}
/**
diff --git a/core/api/src/main/java/org/onlab/onos/net/intent/constraint/AnnotationConstraint.java b/core/api/src/main/java/org/onlab/onos/net/intent/constraint/AnnotationConstraint.java
index ac76303..1767a41 100644
--- a/core/api/src/main/java/org/onlab/onos/net/intent/constraint/AnnotationConstraint.java
+++ b/core/api/src/main/java/org/onlab/onos/net/intent/constraint/AnnotationConstraint.java
@@ -21,6 +21,8 @@
import java.util.Objects;
+import static org.onlab.onos.net.AnnotationKeys.getAnnotatedValue;
+
/**
* Constraint that evaluates an arbitrary link annotated value is under the specified threshold.
*/
@@ -41,6 +43,12 @@
this.threshold = threshold;
}
+ // Constructor for serialization
+ private AnnotationConstraint() {
+ this.key = "";
+ this.threshold = 0;
+ }
+
/**
* Returns the key of link annotation this constraint designates.
* @return key of link annotation
@@ -65,25 +73,6 @@
return value <= threshold;
}
- /**
- * Returns the annotated value of the specified link. The annotated value
- * is expected to be String that can be parsed as double. If parsing fails,
- * the returned value will be 1.0.
- *
- * @param link link whose annotated value is obtained
- * @param key key of link annotation
- * @return double value of link annotation for the specified key
- */
- private double getAnnotatedValue(Link link, String key) {
- double value;
- try {
- value = Double.parseDouble(link.annotations().value(key));
- } catch (NumberFormatException e) {
- value = 1.0;
- }
- return value;
- }
-
@Override
public double cost(Link link, LinkResourceService resourceService) {
if (isValid(link, resourceService)) {
diff --git a/core/api/src/main/java/org/onlab/onos/net/intent/constraint/LatencyConstraint.java b/core/api/src/main/java/org/onlab/onos/net/intent/constraint/LatencyConstraint.java
index fcdf330..e4b4432 100644
--- a/core/api/src/main/java/org/onlab/onos/net/intent/constraint/LatencyConstraint.java
+++ b/core/api/src/main/java/org/onlab/onos/net/intent/constraint/LatencyConstraint.java
@@ -25,14 +25,14 @@
import java.time.temporal.ChronoUnit;
import java.util.Objects;
+import static org.onlab.onos.net.AnnotationKeys.LATENCY;
+import static org.onlab.onos.net.AnnotationKeys.getAnnotatedValue;
+
/**
* Constraint that evaluates the latency through a path.
*/
public class LatencyConstraint implements Constraint {
- // TODO: formalize the key for latency all over the codes.
- private static final String LATENCY_KEY = "latency";
-
private final Duration latency;
/**
@@ -43,22 +43,18 @@
this.latency = latency;
}
+ // Constructor for serialization
+ private LatencyConstraint() {
+ this.latency = Duration.ZERO;
+ }
+
public Duration latency() {
return latency;
}
@Override
public double cost(Link link, LinkResourceService resourceService) {
- String value = link.annotations().value(LATENCY_KEY);
-
- double latencyInMicroSec;
- try {
- latencyInMicroSec = Double.parseDouble(value);
- } catch (NumberFormatException e) {
- latencyInMicroSec = 1.0;
- }
-
- return latencyInMicroSec;
+ return getAnnotatedValue(link, LATENCY);
}
@Override
diff --git a/core/api/src/main/java/org/onlab/onos/net/intent/constraint/ObstacleConstraint.java b/core/api/src/main/java/org/onlab/onos/net/intent/constraint/ObstacleConstraint.java
index 6d73fc2..8472f3c 100644
--- a/core/api/src/main/java/org/onlab/onos/net/intent/constraint/ObstacleConstraint.java
+++ b/core/api/src/main/java/org/onlab/onos/net/intent/constraint/ObstacleConstraint.java
@@ -21,6 +21,7 @@
import org.onlab.onos.net.Link;
import org.onlab.onos.net.resource.LinkResourceService;
+import java.util.Collections;
import java.util.Objects;
import java.util.Set;
@@ -39,6 +40,11 @@
this.obstacles = ImmutableSet.copyOf(obstacles);
}
+ // Constructor for serialization
+ private ObstacleConstraint() {
+ this.obstacles = Collections.emptySet();
+ }
+
@Override
public boolean isValid(Link link, LinkResourceService resourceService) {
DeviceId src = link.src().deviceId();
diff --git a/core/api/src/main/java/org/onlab/onos/net/intent/constraint/WaypointConstraint.java b/core/api/src/main/java/org/onlab/onos/net/intent/constraint/WaypointConstraint.java
index 2a1e3e3..9e3cc20 100644
--- a/core/api/src/main/java/org/onlab/onos/net/intent/constraint/WaypointConstraint.java
+++ b/core/api/src/main/java/org/onlab/onos/net/intent/constraint/WaypointConstraint.java
@@ -17,12 +17,13 @@
import com.google.common.base.MoreObjects;
import com.google.common.collect.ImmutableList;
-import org.onlab.onos.net.ElementId;
+import org.onlab.onos.net.DeviceId;
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;
+import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
@@ -35,20 +36,25 @@
*/
public class WaypointConstraint implements Constraint {
- private final List<ElementId> waypoints;
+ private final List<DeviceId> waypoints;
/**
* Creates a new waypoint constraint.
*
* @param waypoints waypoints
*/
- public WaypointConstraint(ElementId... waypoints) {
+ public WaypointConstraint(DeviceId... waypoints) {
checkNotNull(waypoints, "waypoints cannot be null");
checkArgument(waypoints.length > 0, "length of waypoints should be more than 0");
this.waypoints = ImmutableList.copyOf(waypoints);
}
- public List<ElementId> waypoints() {
+ // Constructor for serialization
+ private WaypointConstraint() {
+ this.waypoints = Collections.emptyList();
+ }
+
+ public List<DeviceId> waypoints() {
return waypoints;
}
@@ -60,8 +66,8 @@
@Override
public boolean validate(Path path, LinkResourceService resourceService) {
- LinkedList<ElementId> waypoints = new LinkedList<>(this.waypoints);
- ElementId current = waypoints.poll();
+ LinkedList<DeviceId> waypoints = new LinkedList<>(this.waypoints);
+ DeviceId current = waypoints.poll();
// This is safe because Path class ensures the number of links are more than 0
Link firstLink = path.links().get(0);
if (firstLink.src().elementId().equals(current)) {
diff --git a/core/api/src/test/java/org/onlab/onos/net/intent/constraint/LatencyConstraintTest.java b/core/api/src/test/java/org/onlab/onos/net/intent/constraint/LatencyConstraintTest.java
index b9fa3ee..d1a280c 100644
--- a/core/api/src/test/java/org/onlab/onos/net/intent/constraint/LatencyConstraintTest.java
+++ b/core/api/src/test/java/org/onlab/onos/net/intent/constraint/LatencyConstraintTest.java
@@ -37,6 +37,7 @@
import static org.hamcrest.Matchers.closeTo;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;
+import static org.onlab.onos.net.AnnotationKeys.LATENCY;
import static org.onlab.onos.net.DefaultLinkTest.cp;
import static org.onlab.onos.net.DeviceId.deviceId;
import static org.onlab.onos.net.Link.Type.DIRECT;
@@ -51,7 +52,6 @@
private static final PortNumber PN3 = PortNumber.portNumber(3);
private static final PortNumber PN4 = PortNumber.portNumber(4);
private static final ProviderId PROVIDER_ID = new ProviderId("of", "foo");
- private static final String LATENCY_KEY = "latency";
private static final String LATENCY1 = "3.0";
private static final String LATENCY2 = "4.0";
@@ -66,8 +66,8 @@
public void setUp() {
linkResourceService = createMock(LinkResourceService.class);
- Annotations annotations1 = DefaultAnnotations.builder().set(LATENCY_KEY, LATENCY1).build();
- Annotations annotations2 = DefaultAnnotations.builder().set(LATENCY_KEY, LATENCY2).build();
+ Annotations annotations1 = DefaultAnnotations.builder().set(LATENCY, LATENCY1).build();
+ Annotations annotations2 = DefaultAnnotations.builder().set(LATENCY, LATENCY2).build();
link1 = new DefaultLink(PROVIDER_ID, cp(DID1, PN1), cp(DID2, PN2), DIRECT, annotations1);
link2 = new DefaultLink(PROVIDER_ID, cp(DID2, PN3), cp(DID3, PN4), DIRECT, annotations2);
diff --git a/core/net/src/main/java/org/onlab/onos/net/device/impl/DeviceManager.java b/core/net/src/main/java/org/onlab/onos/net/device/impl/DeviceManager.java
index 68665e4..b15872b 100644
--- a/core/net/src/main/java/org/onlab/onos/net/device/impl/DeviceManager.java
+++ b/core/net/src/main/java/org/onlab/onos/net/device/impl/DeviceManager.java
@@ -18,9 +18,13 @@
import static com.google.common.base.Preconditions.checkNotNull;
import static org.onlab.onos.net.device.DeviceEvent.Type.DEVICE_MASTERSHIP_CHANGED;
import static org.onlab.onos.net.MastershipRole.*;
+import static org.onlab.util.Tools.namedThreads;
import static org.slf4j.LoggerFactory.getLogger;
import java.util.List;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
@@ -83,6 +87,8 @@
private final MastershipListener mastershipListener = new InternalMastershipListener();
+ private ScheduledExecutorService backgroundService;
+
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected DeviceStore store;
@@ -102,15 +108,31 @@
@Activate
public void activate() {
+ backgroundService = Executors.newSingleThreadScheduledExecutor(namedThreads("device-manager-background"));
+
store.setDelegate(delegate);
eventDispatcher.addSink(DeviceEvent.class, listenerRegistry);
mastershipService.addListener(mastershipListener);
termService = mastershipService.requestTermService();
+
+ backgroundService.scheduleWithFixedDelay(new Runnable() {
+
+ @Override
+ public void run() {
+ try {
+ mastershipCheck();
+ } catch (Exception e) {
+ log.error("Exception thrown during integrity check", e);
+ }
+ }
+ }, 1, 1, TimeUnit.MINUTES);
log.info("Started");
}
@Deactivate
public void deactivate() {
+ backgroundService.shutdown();
+
store.unsetDelegate(delegate);
mastershipService.removeListener(mastershipListener);
eventDispatcher.removeSink(DeviceEvent.class);
@@ -172,10 +194,6 @@
@Override
public void removeDevice(DeviceId deviceId) {
checkNotNull(deviceId, DEVICE_ID_NULL);
- // XXX is this intended to apply to the full global topology?
- // if so, we probably don't want the fact that we aren't
- // MASTER to get in the way, as it would do now.
- // FIXME: forward or broadcast and let the Master handler the event.
DeviceEvent event = store.removeDevice(deviceId);
if (event != null) {
log.info("Device {} administratively removed", deviceId);
@@ -199,6 +217,31 @@
return new InternalDeviceProviderService(provider);
}
+ /**
+ * Checks if all the reachable devices have a valid mastership role.
+ */
+ private void mastershipCheck() {
+ log.debug("Checking mastership");
+ for (Device device : getDevices()) {
+ final DeviceId deviceId = device.id();
+ log.debug("Checking device {}", deviceId);
+
+ if (!isReachable(deviceId)) {
+ continue;
+ }
+
+ if (mastershipService.getLocalRole(deviceId) != NONE) {
+ continue;
+ }
+
+ log.info("{} is reachable but did not have a valid role, reasserting", deviceId);
+
+ // isReachable but was not MASTER or STANDBY, get a role and apply
+ // Note: NONE triggers request to MastershipService
+ reassertRole(deviceId, NONE);
+ }
+ }
+
// Personalized device provider service issued to the supplied provider.
private class InternalDeviceProviderService
extends AbstractProviderService<DeviceProvider>
@@ -418,48 +461,112 @@
}
}
- // Intercepts mastership events
- private class InternalMastershipListener implements MastershipListener {
-
- // Applies the specified role to the device; ignores NONE
- /**
- * Apply role in reaction to mastership event.
- *
- * @param deviceId device identifier
- * @param newRole new role to apply to the device
- * @return true if the request was sent to provider
- */
- private boolean applyRole(DeviceId deviceId, MastershipRole newRole) {
- if (newRole.equals(MastershipRole.NONE)) {
- //no-op
- return true;
- }
-
- Device device = store.getDevice(deviceId);
- // FIXME: Device might not be there yet. (eventual consistent)
- // FIXME relinquish role
- if (device == null) {
- log.warn("{} was not there. Cannot apply role {}", deviceId, newRole);
- return false;
- }
-
- DeviceProvider provider = getProvider(device.providerId());
- if (provider == null) {
- log.warn("Provider for {} was not found. Cannot apply role {}", deviceId, newRole);
- return false;
- }
- provider.roleChanged(deviceId, newRole);
-
- if (newRole.equals(MastershipRole.MASTER)) {
- // only trigger event when request was sent to provider
- // TODO: consider removing this from Device event type?
- post(new DeviceEvent(DEVICE_MASTERSHIP_CHANGED, device));
-
- provider.triggerProbe(device);
- }
+ // Applies the specified role to the device; ignores NONE
+ /**
+ * Apply role to device and send probe if MASTER.
+ *
+ * @param deviceId device identifier
+ * @param newRole new role to apply to the device
+ * @return true if the request was sent to provider
+ */
+ private boolean applyRoleAndProbe(DeviceId deviceId, MastershipRole newRole) {
+ if (newRole.equals(MastershipRole.NONE)) {
+ //no-op
return true;
}
+ Device device = store.getDevice(deviceId);
+ // FIXME: Device might not be there yet. (eventual consistent)
+ // FIXME relinquish role
+ if (device == null) {
+ log.warn("{} was not there. Cannot apply role {}", deviceId, newRole);
+ return false;
+ }
+
+ DeviceProvider provider = getProvider(device.providerId());
+ if (provider == null) {
+ log.warn("Provider for {} was not found. Cannot apply role {}", deviceId, newRole);
+ return false;
+ }
+ provider.roleChanged(deviceId, newRole);
+
+ if (newRole.equals(MastershipRole.MASTER)) {
+ // only trigger event when request was sent to provider
+ // TODO: consider removing this from Device event type?
+ post(new DeviceEvent(DEVICE_MASTERSHIP_CHANGED, device));
+
+ provider.triggerProbe(device);
+ }
+ return true;
+ }
+
+ /**
+ * Reaasert role for specified device connected to this node.
+ *
+ * @param did device identifier
+ * @param nextRole role to apply. If NONE is specified,
+ * it will ask mastership service for a role and apply it.
+ */
+ private void reassertRole(final DeviceId did,
+ final MastershipRole nextRole) {
+
+ final NodeId myNodeId = clusterService.getLocalNode().id();
+ MastershipRole myNextRole = nextRole;
+ if (myNextRole == NONE) {
+ mastershipService.requestRoleFor(did);
+ MastershipTerm term = termService.getMastershipTerm(did);
+ if (myNodeId.equals(term.master())) {
+ myNextRole = MASTER;
+ } else {
+ myNextRole = STANDBY;
+ }
+ }
+
+ switch (myNextRole) {
+ case MASTER:
+ final Device device = getDevice(did);
+ if ((device != null) && !isAvailable(did)) {
+ //flag the device as online. Is there a better way to do this?
+ DefaultDeviceDescription deviceDescription
+ = new DefaultDeviceDescription(did.uri(),
+ device.type(),
+ device.manufacturer(),
+ device.hwVersion(),
+ device.swVersion(),
+ device.serialNumber(),
+ device.chassisId());
+ DeviceEvent devEvent =
+ store.createOrUpdateDevice(device.providerId(), did,
+ deviceDescription);
+ post(devEvent);
+ }
+ // TODO: should apply role only if there is mismatch
+ log.info("Applying role {} to {}", myNextRole, did);
+ if (!applyRoleAndProbe(did, MASTER)) {
+ // immediately failed to apply role
+ mastershipService.relinquishMastership(did);
+ // FIXME disconnect?
+ }
+ break;
+ case STANDBY:
+ log.info("Applying role {} to {}", myNextRole, did);
+ if (!applyRoleAndProbe(did, STANDBY)) {
+ // immediately failed to apply role
+ mastershipService.relinquishMastership(did);
+ // FIXME disconnect?
+ }
+ break;
+ case NONE:
+ default:
+ // should never reach here
+ log.error("You didn't see anything. I did not exist.");
+ break;
+ }
+ }
+
+ // Intercepts mastership events
+ private class InternalMastershipListener implements MastershipListener {
+
@Override
public void event(MastershipEvent event) {
@@ -499,55 +606,12 @@
+ "Relinquishing role. ",
myNextRole, did);
mastershipService.relinquishMastership(did);
- // FIXME disconnect?
}
return;
}
// device is connected to this node:
-
- if (myNextRole == NONE) {
- mastershipService.requestRoleFor(did);
- MastershipTerm term = termService.getMastershipTerm(did);
- if (myNodeId.equals(term.master())) {
- myNextRole = MASTER;
- } else {
- myNextRole = STANDBY;
- }
- }
-
- switch (myNextRole) {
- case MASTER:
- final Device device = getDevice(did);
- if ((device != null) && !isAvailable(did)) {
- //flag the device as online. Is there a better way to do this?
- DefaultDeviceDescription deviceDescription
- = new DefaultDeviceDescription(did.uri(),
- device.type(),
- device.manufacturer(),
- device.hwVersion(),
- device.swVersion(),
- device.serialNumber(),
- device.chassisId());
- DeviceEvent devEvent =
- store.createOrUpdateDevice(device.providerId(), did,
- deviceDescription);
- post(devEvent);
- }
- // TODO: should apply role only if there is mismatch
- log.info("Applying role {} to {}", myNextRole, did);
- applyRole(did, MASTER);
- break;
- case STANDBY:
- log.info("Applying role {} to {}", myNextRole, did);
- applyRole(did, STANDBY);
- break;
- case NONE:
- default:
- // should never reach here
- log.error("You didn't see anything. I did not exist.");
- break;
- }
+ reassertRole(did, myNextRole);
}
}
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 e996dfc..2e53252 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
@@ -15,21 +15,12 @@
*/
package org.onlab.onos.net.flow.impl;
-import static com.google.common.base.Preconditions.checkNotNull;
-import static org.slf4j.LoggerFactory.getLogger;
-import static org.onlab.util.Tools.namedThreads;
-
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.CancellationException;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.Future;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-import java.util.concurrent.atomic.AtomicReference;
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Multimap;
+import com.google.common.collect.Sets;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
@@ -64,14 +55,22 @@
import org.onlab.onos.net.provider.AbstractProviderService;
import org.slf4j.Logger;
-import com.google.common.collect.ArrayListMultimap;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Maps;
-import com.google.common.collect.Multimap;
-import com.google.common.collect.Sets;
-import com.google.common.util.concurrent.Futures;
-import com.google.common.util.concurrent.ListenableFuture;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicReference;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onlab.util.Tools.namedThreads;
+import static org.slf4j.LoggerFactory.getLogger;
/**
* Provides implementation of the flow NB & SB APIs.
@@ -92,8 +91,7 @@
private final FlowRuleStoreDelegate delegate = new InternalStoreDelegate();
- private final ExecutorService futureListeners =
- Executors.newCachedThreadPool(namedThreads("provider-future-listeners"));
+ private ExecutorService futureService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected FlowRuleStore store;
@@ -106,6 +104,7 @@
@Activate
public void activate() {
+ futureService = Executors.newCachedThreadPool(namedThreads("provider-future-listeners"));
store.setDelegate(delegate);
eventDispatcher.addSink(FlowRuleEvent.class, listenerRegistry);
log.info("Started");
@@ -113,7 +112,7 @@
@Deactivate
public void deactivate() {
- futureListeners.shutdownNow();
+ futureService.shutdownNow();
store.unsetDelegate(delegate);
eventDispatcher.removeSink(FlowRuleEvent.class);
@@ -364,6 +363,9 @@
// Store delegate to re-post events emitted from the store.
private class InternalStoreDelegate implements FlowRuleStoreDelegate {
+
+ private static final int TIMEOUT = 5000; // ms
+
// TODO: Right now we only dispatch events at individual flowEntry level.
// It may be more efficient for also dispatch events as a batch.
@Override
@@ -384,15 +386,28 @@
FlowRuleProvider flowRuleProvider =
getProvider(batchOperation.getOperations().get(0).getTarget().deviceId());
- final ListenableFuture<CompletedBatchOperation> result =
+ final Future<CompletedBatchOperation> result =
flowRuleProvider.executeBatch(batchOperation);
- result.addListener(new Runnable() {
+ futureService.submit(new Runnable() {
@Override
public void run() {
- store.batchOperationComplete(FlowRuleBatchEvent.completed(request,
- Futures.getUnchecked(result)));
+ CompletedBatchOperation res;
+ try {
+ res = result.get(TIMEOUT, TimeUnit.MILLISECONDS);
+ store.batchOperationComplete(FlowRuleBatchEvent.completed(request, res));
+ } catch (TimeoutException | InterruptedException | ExecutionException e) {
+ log.warn("Something went wrong with the batch operation {}",
+ request.batchId(), e);
+
+ Set<FlowRule> failures = new HashSet<>(batchOperation.size());
+ for (FlowRuleBatchEntry op : batchOperation.getOperations()) {
+ failures.add(op.getTarget());
+ }
+ res = new CompletedBatchOperation(false, failures);
+ store.batchOperationComplete(FlowRuleBatchEvent.completed(request, res));
+ }
}
- }, futureListeners);
+ });
break;
case BATCH_OPERATION_COMPLETED:
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
index d66e849..405366d 100644
--- 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
@@ -15,6 +15,8 @@
*/
package org.onlab.onos.net.intent.impl;
+import com.google.common.base.Predicate;
+import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableList;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Reference;
@@ -94,11 +96,19 @@
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);
+ final List<Constraint> constraints = intent.constraints();
+ ImmutableList<Path> filtered = FluentIterable.from(paths)
+ .filter(new Predicate<Path>() {
+ @Override
+ public boolean apply(Path path) {
+ return checkPath(path, constraints);
+ }
+ }).toList();
+ if (filtered.isEmpty()) {
+ throw new PathNotFoundException("No packet path form " + one + " to " + two);
}
// TODO: let's be more intelligent about this eventually
- return paths.iterator().next();
+ return filtered.iterator().next();
}
/**
diff --git a/core/net/src/test/java/org/onlab/onos/net/flow/DefaultFlowEntryTest.java b/core/net/src/test/java/org/onlab/onos/net/flow/DefaultFlowEntryTest.java
new file mode 100644
index 0000000..b8d7799
--- /dev/null
+++ b/core/net/src/test/java/org/onlab/onos/net/flow/DefaultFlowEntryTest.java
@@ -0,0 +1,160 @@
+/*
+ * 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.flow;
+
+import java.util.concurrent.TimeUnit;
+
+import org.junit.Test;
+import org.onlab.onos.net.intent.IntentTestsMocks;
+
+import com.google.common.testing.EqualsTester;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.greaterThan;
+import static org.hamcrest.Matchers.is;
+import static org.onlab.onos.net.NetTestTools.did;
+
+/**
+ * Unit tests for the DefaultFlowEntry class.
+ */
+public class DefaultFlowEntryTest {
+ private static final IntentTestsMocks.MockSelector SELECTOR =
+ new IntentTestsMocks.MockSelector();
+ private static final IntentTestsMocks.MockTreatment TREATMENT =
+ new IntentTestsMocks.MockTreatment();
+
+ private static DefaultFlowEntry makeFlowEntry(int uniqueValue) {
+ return new DefaultFlowEntry(did("id" + Integer.toString(uniqueValue)),
+ SELECTOR,
+ TREATMENT,
+ uniqueValue,
+ FlowEntry.FlowEntryState.ADDED,
+ uniqueValue,
+ uniqueValue,
+ uniqueValue,
+ uniqueValue,
+ uniqueValue);
+ }
+
+ final DefaultFlowEntry defaultFlowEntry1 = makeFlowEntry(1);
+ final DefaultFlowEntry sameAsDefaultFlowEntry1 = makeFlowEntry(1);
+ final DefaultFlowEntry defaultFlowEntry2 = makeFlowEntry(2);
+
+ /**
+ * Tests the equals, hashCode and toString methods using Guava EqualsTester.
+ */
+ @Test
+ public void testEquals() {
+ new EqualsTester()
+ .addEqualityGroup(defaultFlowEntry1, sameAsDefaultFlowEntry1)
+ .addEqualityGroup(defaultFlowEntry2)
+ .testEquals();
+ }
+
+ /**
+ * Tests the construction of a default flow entry from a device id.
+ */
+ @Test
+ public void testDeviceBasedObject() {
+ assertThat(defaultFlowEntry1.deviceId(), is(did("id1")));
+ assertThat(defaultFlowEntry1.selector(), is(SELECTOR));
+ assertThat(defaultFlowEntry1.treatment(), is(TREATMENT));
+ assertThat(defaultFlowEntry1.timeout(), is(1));
+ assertThat(defaultFlowEntry1.life(), is(1L));
+ assertThat(defaultFlowEntry1.packets(), is(1L));
+ assertThat(defaultFlowEntry1.bytes(), is(1L));
+ assertThat(defaultFlowEntry1.state(), is(FlowEntry.FlowEntryState.ADDED));
+ assertThat(defaultFlowEntry1.lastSeen(),
+ greaterThan(System.currentTimeMillis() -
+ TimeUnit.MILLISECONDS.convert(1, TimeUnit.SECONDS)));
+ }
+
+ /**
+ * Tests the setters on a default flow entry object.
+ */
+ @Test
+ public void testSetters() {
+ final DefaultFlowEntry entry = makeFlowEntry(1);
+
+ entry.setLastSeen();
+ entry.setState(FlowEntry.FlowEntryState.PENDING_REMOVE);
+ entry.setPackets(11);
+ entry.setBytes(22);
+ entry.setLife(33);
+
+ assertThat(entry.deviceId(), is(did("id1")));
+ assertThat(entry.selector(), is(SELECTOR));
+ assertThat(entry.treatment(), is(TREATMENT));
+ assertThat(entry.timeout(), is(1));
+ assertThat(entry.life(), is(33L));
+ assertThat(entry.packets(), is(11L));
+ assertThat(entry.bytes(), is(22L));
+ assertThat(entry.state(), is(FlowEntry.FlowEntryState.PENDING_REMOVE));
+ assertThat(entry.lastSeen(),
+ greaterThan(System.currentTimeMillis() -
+ TimeUnit.MILLISECONDS.convert(1, TimeUnit.SECONDS)));
+ }
+
+ /**
+ * Tests a default flow rule built for an error.
+ */
+ @Test
+ public void testErrorObject() {
+ final DefaultFlowEntry errorEntry =
+ new DefaultFlowEntry(new IntentTestsMocks.MockFlowRule(1),
+ 111,
+ 222);
+ assertThat(errorEntry.errType(), is(111));
+ assertThat(errorEntry.errCode(), is(222));
+ assertThat(errorEntry.state(), is(FlowEntry.FlowEntryState.FAILED));
+ assertThat(errorEntry.lastSeen(),
+ greaterThan(System.currentTimeMillis() -
+ TimeUnit.MILLISECONDS.convert(1, TimeUnit.SECONDS)));
+ }
+
+ /**
+ * Tests a default flow entry constructed from a flow rule.
+ */
+ @Test
+ public void testFlowBasedObject() {
+ final DefaultFlowEntry entry =
+ new DefaultFlowEntry(new IntentTestsMocks.MockFlowRule(1));
+ assertThat(entry.priority(), is(1));
+ assertThat(entry.appId(), is((short) 0));
+ assertThat(entry.lastSeen(),
+ greaterThan(System.currentTimeMillis() -
+ TimeUnit.MILLISECONDS.convert(1, TimeUnit.SECONDS)));
+ }
+
+ /**
+ * Tests a default flow entry constructed from a flow rule plus extra
+ * parameters.
+ */
+ @Test
+ public void testFlowBasedObjectWithParameters() {
+ final DefaultFlowEntry entry =
+ new DefaultFlowEntry(new IntentTestsMocks.MockFlowRule(33),
+ FlowEntry.FlowEntryState.REMOVED,
+ 101, 102, 103);
+ assertThat(entry.state(), is(FlowEntry.FlowEntryState.REMOVED));
+ assertThat(entry.life(), is(101L));
+ assertThat(entry.packets(), is(102L));
+ assertThat(entry.bytes(), is(103L));
+ assertThat(entry.lastSeen(),
+ greaterThan(System.currentTimeMillis() -
+ TimeUnit.MILLISECONDS.convert(1, TimeUnit.SECONDS)));
+ }
+}
diff --git a/core/net/src/test/java/org/onlab/onos/net/flow/DefaultFlowRuleTest.java b/core/net/src/test/java/org/onlab/onos/net/flow/DefaultFlowRuleTest.java
new file mode 100644
index 0000000..f2c418f
--- /dev/null
+++ b/core/net/src/test/java/org/onlab/onos/net/flow/DefaultFlowRuleTest.java
@@ -0,0 +1,115 @@
+/*
+ * 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.flow;
+
+import org.junit.Test;
+import org.onlab.onos.net.intent.IntentTestsMocks;
+
+import com.google.common.testing.EqualsTester;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+import static org.onlab.junit.ImmutableClassChecker.assertThatClassIsImmutableBaseClass;
+import static org.onlab.onos.net.NetTestTools.APP_ID;
+import static org.onlab.onos.net.NetTestTools.did;
+
+/**
+ * Unit tests for the default flow rule class.
+ */
+public class DefaultFlowRuleTest {
+ private static final IntentTestsMocks.MockSelector SELECTOR =
+ new IntentTestsMocks.MockSelector();
+ private static final IntentTestsMocks.MockTreatment TREATMENT =
+ new IntentTestsMocks.MockTreatment();
+
+ final FlowRule flowRule1 = new IntentTestsMocks.MockFlowRule(1);
+ final FlowRule sameAsFlowRule1 = new IntentTestsMocks.MockFlowRule(1);
+ final FlowRule flowRule2 = new IntentTestsMocks.MockFlowRule(2);
+ final DefaultFlowRule defaultFlowRule1 = new DefaultFlowRule(flowRule1);
+ final DefaultFlowRule sameAsDefaultFlowRule1 = new DefaultFlowRule(sameAsFlowRule1);
+ final DefaultFlowRule defaultFlowRule2 = new DefaultFlowRule(flowRule2);
+
+ /**
+ * Checks that the DefaultFlowRule class is immutable but can be inherited
+ * from.
+ */
+ @Test
+ public void testImmutability() {
+ assertThatClassIsImmutableBaseClass(DefaultFlowRule.class);
+ }
+
+ /**
+ * Tests the equals, hashCode and toString methods using Guava EqualsTester.
+ */
+ @Test
+ public void testEquals() {
+ new EqualsTester()
+ .addEqualityGroup(defaultFlowRule1, sameAsDefaultFlowRule1)
+ .addEqualityGroup(defaultFlowRule2)
+ .testEquals();
+ }
+
+ /**
+ * Tests creation of a DefaultFlowRule using a FlowRule constructor.
+ */
+ @Test
+ public void testCreationFromFlowRule() {
+ assertThat(defaultFlowRule1.deviceId(), is(flowRule1.deviceId()));
+ assertThat(defaultFlowRule1.appId(), is(flowRule1.appId()));
+ assertThat(defaultFlowRule1.id(), is(flowRule1.id()));
+ assertThat(defaultFlowRule1.isPermanent(), is(flowRule1.isPermanent()));
+ assertThat(defaultFlowRule1.priority(), is(flowRule1.priority()));
+ assertThat(defaultFlowRule1.selector(), is(flowRule1.selector()));
+ assertThat(defaultFlowRule1.treatment(), is(flowRule1.treatment()));
+ assertThat(defaultFlowRule1.timeout(), is(flowRule1.timeout()));
+ }
+
+ /**
+ * Tests creation of a DefaultFlowRule using a FlowId constructor.
+ */
+ @Test
+ public void testCreationWithFlowId() {
+ final DefaultFlowRule rule =
+ new DefaultFlowRule(did("1"), SELECTOR,
+ TREATMENT, 22, 33,
+ 44, false);
+ assertThat(rule.deviceId(), is(did("1")));
+ assertThat(rule.id().value(), is(33L));
+ assertThat(rule.isPermanent(), is(false));
+ assertThat(rule.priority(), is(22));
+ assertThat(rule.selector(), is(SELECTOR));
+ assertThat(rule.treatment(), is(TREATMENT));
+ assertThat(rule.timeout(), is(44));
+ }
+
+ /**
+ * Tests the creation of a DefaultFlowRule using an AppId constructor.
+ */
+ @Test
+ public void testCreationWithAppId() {
+ final DefaultFlowRule rule =
+ new DefaultFlowRule(did("1"), SELECTOR,
+ TREATMENT, 22, APP_ID,
+ 44, false);
+ assertThat(rule.deviceId(), is(did("1")));
+ assertThat(rule.isPermanent(), is(false));
+ assertThat(rule.priority(), is(22));
+ assertThat(rule.selector(), is(SELECTOR));
+ assertThat(rule.treatment(), is(TREATMENT));
+ assertThat(rule.timeout(), is(44));
+ }
+}
diff --git a/core/net/src/test/java/org/onlab/onos/net/intent/IntentTestsMocks.java b/core/net/src/test/java/org/onlab/onos/net/intent/IntentTestsMocks.java
index e0fe09e..65df6b2 100644
--- a/core/net/src/test/java/org/onlab/onos/net/intent/IntentTestsMocks.java
+++ b/core/net/src/test/java/org/onlab/onos/net/intent/IntentTestsMocks.java
@@ -16,6 +16,7 @@
package org.onlab.onos.net.intent;
import static org.onlab.onos.net.NetTestTools.createPath;
+import static org.onlab.onos.net.NetTestTools.did;
import static org.onlab.onos.net.NetTestTools.link;
import java.util.ArrayList;
@@ -31,6 +32,8 @@
import org.onlab.onos.net.ElementId;
import org.onlab.onos.net.Link;
import org.onlab.onos.net.Path;
+import org.onlab.onos.net.flow.FlowId;
+import org.onlab.onos.net.flow.FlowRule;
import org.onlab.onos.net.flow.TrafficSelector;
import org.onlab.onos.net.flow.TrafficTreatment;
import org.onlab.onos.net.flow.criteria.Criterion;
@@ -271,4 +274,60 @@
}
}
+ private static final IntentTestsMocks.MockSelector SELECTOR =
+ new IntentTestsMocks.MockSelector();
+ private static final IntentTestsMocks.MockTreatment TREATMENT =
+ new IntentTestsMocks.MockTreatment();
+
+ public static class MockFlowRule implements FlowRule {
+
+ int priority;
+ public MockFlowRule(int priority) {
+ this.priority = priority;
+ }
+
+ @Override
+ public FlowId id() {
+ return FlowId.valueOf(1);
+ }
+
+ @Override
+ public short appId() {
+ return 0;
+ }
+
+ @Override
+ public int priority() {
+ return priority;
+ }
+
+ @Override
+ public DeviceId deviceId() {
+ return did("1");
+ }
+
+ @Override
+ public TrafficSelector selector() {
+ return SELECTOR;
+ }
+
+ @Override
+ public TrafficTreatment treatment() {
+ return TREATMENT;
+ }
+
+ @Override
+ public int timeout() {
+ return 0;
+ }
+
+ @Override
+ public boolean isPermanent() {
+ return false;
+ }
+
+
+ }
+
+
}
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 f65da3f..c94b6c0 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
@@ -85,6 +85,8 @@
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
+import com.google.common.cache.RemovalListener;
+import com.google.common.cache.RemovalNotification;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
@@ -132,8 +134,7 @@
private Cache<Integer, SettableFuture<CompletedBatchOperation>> pendingFutures =
CacheBuilder.newBuilder()
.expireAfterWrite(pendingFutureTimeoutMinutes, TimeUnit.MINUTES)
- // TODO Explicitly fail the future if expired?
- //.removalListener(listener)
+ .removalListener(new TimeoutFuture())
.build();
// Cache of SMaps used for backup data. each SMap contain device flow table
@@ -541,6 +542,17 @@
log.debug("removedFromPrimary {}", removed);
}
+ private static final class TimeoutFuture
+ implements RemovalListener<Integer, SettableFuture<CompletedBatchOperation>> {
+ @Override
+ public void onRemoval(RemovalNotification<Integer, SettableFuture<CompletedBatchOperation>> notification) {
+ // wrapping in ExecutionException to support Future.get
+ notification.getValue()
+ .setException(new ExecutionException("Timed out",
+ new TimeoutException()));
+ }
+ }
+
private final class OnStoreBatch implements ClusterMessageHandler {
private final NodeId local;
@@ -580,7 +592,18 @@
@Override
public void run() {
- CompletedBatchOperation result = Futures.getUnchecked(f);
+ CompletedBatchOperation result;
+ try {
+ result = f.get();
+ } catch (InterruptedException | ExecutionException e) {
+ log.error("Batch operation failed", e);
+ // create everything failed response
+ Set<FlowRule> failures = new HashSet<>(operation.size());
+ for (FlowRuleBatchEntry op : operation.getOperations()) {
+ failures.add(op.getTarget());
+ }
+ result = new CompletedBatchOperation(false, failures);
+ }
try {
message.respond(SERIALIZER.encode(result));
} catch (IOException 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
index 0d06e08..afeaef9 100644
--- 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
@@ -72,7 +72,7 @@
public static final String LOG_FILE_PREFIX = "/tmp/onos-copy-cat-log_";
// Current working dir seems to be /opt/onos/apache-karaf-3.0.2
- // TODO: Get the path to /opt/onos/config
+ // TODO: Set the path to /opt/onos/config
private static final String CONFIG_DIR = "../config";
private static final String DEFAULT_MEMBER_FILE = "tablets.json";
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 4cba9f0..5d689a3 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,10 +75,14 @@
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.AnnotationConstraint;
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.LatencyConstraint;
import org.onlab.onos.net.intent.constraint.LinkTypeConstraint;
+import org.onlab.onos.net.intent.constraint.ObstacleConstraint;
+import org.onlab.onos.net.intent.constraint.WaypointConstraint;
import org.onlab.onos.net.link.DefaultLinkDescription;
import org.onlab.onos.net.packet.DefaultOutboundPacket;
import org.onlab.onos.net.provider.ProviderId;
@@ -208,9 +212,14 @@
LinkResourceRequest.class,
Lambda.class,
Bandwidth.class,
+ // Constraints
LambdaConstraint.class,
BandwidthConstraint.class,
LinkTypeConstraint.class,
+ LatencyConstraint.class,
+ WaypointConstraint.class,
+ ObstacleConstraint.class,
+ AnnotationConstraint.class,
BooleanConstraint.class
)
.register(DefaultApplicationId.class, new DefaultApplicationIdSerializer())
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 b7d26fb..622e2de 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
@@ -18,6 +18,8 @@
import com.google.common.base.Function;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.RemovalListener;
+import com.google.common.cache.RemovalNotification;
import com.google.common.collect.FluentIterable;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.SettableFuture;
@@ -53,8 +55,10 @@
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicInteger;
import static org.apache.commons.lang3.concurrent.ConcurrentUtils.createIfAbsentUnchecked;
@@ -86,8 +90,7 @@
private Cache<Integer, SettableFuture<CompletedBatchOperation>> pendingFutures =
CacheBuilder.newBuilder()
.expireAfterWrite(pendingFutureTimeoutMinutes, TimeUnit.MINUTES)
- // TODO Explicitly fail the future if expired?
- //.removalListener(listener)
+ .removalListener(new TimeoutFuture())
.build();
@Activate
@@ -303,4 +306,15 @@
}
notifyDelegate(event);
}
+
+ private static final class TimeoutFuture
+ implements RemovalListener<Integer, SettableFuture<CompletedBatchOperation>> {
+ @Override
+ public void onRemoval(RemovalNotification<Integer, SettableFuture<CompletedBatchOperation>> notification) {
+ // wrapping in ExecutionException to support Future.get
+ notification.getValue()
+ .setException(new ExecutionException("Timed out",
+ new TimeoutException()));
+ }
+ }
}
diff --git a/features/features.xml b/features/features.xml
index efd31cf..5ede89c 100644
--- a/features/features.xml
+++ b/features/features.xml
@@ -197,6 +197,8 @@
<feature name="onos-app-sdnip" version="1.0.0"
description="SDN-IP peering application">
<feature>onos-api</feature>
+ <feature>onos-app-proxyarp</feature>
+ <feature>onos-app-config</feature>
<bundle>mvn:org.onlab.onos/onos-app-sdnip/1.0.0-SNAPSHOT</bundle>
</feature>
@@ -225,4 +227,12 @@
<bundle>mvn:org.onlab.onos/onos-app-metrics-topology/1.0.0-SNAPSHOT</bundle>
</feature>
+ <feature name="onos-app-demo" version="1.0.0"
+ description="ONOS demo applications">
+ <feature>onos-api</feature>
+ <bundle>mvn:org.onlab.onos/onos-app-demo/1.0.0-SNAPSHOT</bundle>
+ </feature>
+
+
+
</features>
diff --git a/providers/openflow/flow/src/main/java/org/onlab/onos/provider/of/flow/impl/OpenFlowRuleProvider.java b/providers/openflow/flow/src/main/java/org/onlab/onos/provider/of/flow/impl/OpenFlowRuleProvider.java
index 018d6f3..214bde3 100644
--- a/providers/openflow/flow/src/main/java/org/onlab/onos/provider/of/flow/impl/OpenFlowRuleProvider.java
+++ b/providers/openflow/flow/src/main/java/org/onlab/onos/provider/of/flow/impl/OpenFlowRuleProvider.java
@@ -15,21 +15,11 @@
*/
package org.onlab.onos.provider.of.flow.impl;
-import static org.slf4j.LoggerFactory.getLogger;
-
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.Executor;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-import java.util.concurrent.atomic.AtomicBoolean;
-
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Multimap;
+import com.google.common.collect.Sets;
+import com.google.common.util.concurrent.ExecutionList;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Deactivate;
@@ -80,15 +70,23 @@
import org.projectfloodlight.openflow.protocol.instruction.OFInstruction;
import org.projectfloodlight.openflow.protocol.instruction.OFInstructionApplyActions;
import org.projectfloodlight.openflow.types.OFPort;
-import org.projectfloodlight.openflow.types.U32;
import org.slf4j.Logger;
-import com.google.common.collect.ArrayListMultimap;
-import com.google.common.collect.Maps;
-import com.google.common.collect.Multimap;
-import com.google.common.collect.Sets;
-import com.google.common.util.concurrent.ExecutionList;
-import com.google.common.util.concurrent.ListenableFuture;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicLong;
+
+import static org.slf4j.LoggerFactory.getLogger;
/**
* Provider which uses an OpenFlow controller to detect network
@@ -124,6 +122,8 @@
private final Map<Dpid, FlowStatsCollector> collectors = Maps.newHashMap();
+ private final AtomicLong xidCounter = new AtomicLong(0);
+
/**
* Creates an OpenFlow host provider.
*/
@@ -154,6 +154,7 @@
log.info("Stopped");
}
+
@Override
public void applyFlowRule(FlowRule... flowRules) {
for (int i = 0; i < flowRules.length; i++) {
@@ -167,7 +168,6 @@
}
-
@Override
public void removeFlowRule(FlowRule... flowRules) {
for (int i = 0; i < flowRules.length; i++) {
@@ -188,11 +188,15 @@
}
@Override
- public ListenableFuture<CompletedBatchOperation> executeBatch(BatchOperation<FlowRuleBatchEntry> batch) {
+ public Future<CompletedBatchOperation> executeBatch(BatchOperation<FlowRuleBatchEntry> batch) {
final Set<Dpid> sws =
Collections.newSetFromMap(new ConcurrentHashMap<Dpid, Boolean>());
final Map<Long, FlowRuleBatchEntry> fmXids = new HashMap<Long, FlowRuleBatchEntry>();
- OFFlowMod mod = null;
+ /*
+ * Use identity hash map for reference equality as we could have equal
+ * flow mods for different switches.
+ */
+ Map<OFFlowMod, OpenFlowSwitch> mods = Maps.newIdentityHashMap();
for (FlowRuleBatchEntry fbe : batch.getOperations()) {
FlowRule flowRule = fbe.getTarget();
OpenFlowSwitch sw = controller.getSwitch(Dpid.dpid(flowRule.deviceId().uri()));
@@ -208,6 +212,7 @@
}
sws.add(new Dpid(sw.getId()));
FlowModBuilder builder = FlowModBuilder.builder(flowRule, sw.factory());
+ OFFlowMod mod = null;
switch (fbe.getOperator()) {
case ADD:
mod = builder.buildFlowAdd();
@@ -222,25 +227,29 @@
log.error("Unsupported batch operation {}", fbe.getOperator());
}
if (mod != null) {
- sw.sendMsg(mod);
- fmXids.put(mod.getXid(), fbe);
+ mods.put(mod, sw);
+ fmXids.put(xidCounter.getAndIncrement(), fbe);
} else {
log.error("Conversion of flowrule {} failed.", flowRule);
}
-
}
InstallationFuture installation = new InstallationFuture(sws, fmXids);
for (Long xid : fmXids.keySet()) {
pendingFMs.put(xid, installation);
}
- pendingFutures.put(U32.f(batch.hashCode()), installation);
- installation.verify(U32.f(batch.hashCode()));
+ pendingFutures.put(installation.xid(), installation);
+ for (Map.Entry<OFFlowMod, OpenFlowSwitch> entry : mods.entrySet()) {
+ OpenFlowSwitch sw = entry.getValue();
+ OFFlowMod mod = entry.getKey();
+ sw.sendMsg(mod);
+ }
+ installation.verify();
return installation;
}
private class InternalFlowProvider
- implements OpenFlowSwitchListener, OpenFlowEventListener {
+ implements OpenFlowSwitchListener, OpenFlowEventListener {
private final Multimap<DeviceId, FlowEntry> completeEntries =
@@ -274,36 +283,36 @@
public void handleMessage(Dpid dpid, OFMessage msg) {
InstallationFuture future = null;
switch (msg.getType()) {
- case FLOW_REMOVED:
- OFFlowRemoved removed = (OFFlowRemoved) msg;
+ case FLOW_REMOVED:
+ OFFlowRemoved removed = (OFFlowRemoved) msg;
- FlowEntry fr = new FlowEntryBuilder(dpid, removed).build();
- providerService.flowRemoved(fr);
- break;
- case STATS_REPLY:
- pushFlowMetrics(dpid, (OFStatsReply) msg);
- break;
- case BARRIER_REPLY:
- future = pendingFutures.get(msg.getXid());
- if (future != null) {
- future.satisfyRequirement(dpid);
- }
- break;
- case ERROR:
- future = pendingFMs.get(msg.getXid());
- if (future != null) {
- future.fail((OFErrorMsg) msg, dpid);
- }
- break;
- default:
- log.debug("Unhandled message type: {}", msg.getType());
+ FlowEntry fr = new FlowEntryBuilder(dpid, removed).build();
+ providerService.flowRemoved(fr);
+ break;
+ case STATS_REPLY:
+ pushFlowMetrics(dpid, (OFStatsReply) msg);
+ break;
+ case BARRIER_REPLY:
+ future = pendingFutures.get(msg.getXid());
+ if (future != null) {
+ future.satisfyRequirement(dpid);
+ }
+ break;
+ case ERROR:
+ future = pendingFMs.get(msg.getXid());
+ if (future != null) {
+ future.fail((OFErrorMsg) msg, dpid);
+ }
+ break;
+ default:
+ log.debug("Unhandled message type: {}", msg.getType());
}
}
@Override
public void receivedRoleReply(Dpid dpid, RoleState requested,
- RoleState response) {
+ RoleState response) {
// Do nothing here for now.
}
@@ -352,8 +361,9 @@
}
- private class InstallationFuture implements ListenableFuture<CompletedBatchOperation> {
+ private class InstallationFuture implements Future<CompletedBatchOperation> {
+ private final Long xid;
private final Set<Dpid> sws;
private final AtomicBoolean ok = new AtomicBoolean(true);
private final Map<Long, FlowRuleBatchEntry> fms;
@@ -361,18 +371,22 @@
private final Set<FlowEntry> offendingFlowMods = Sets.newHashSet();
private final CountDownLatch countDownLatch;
- private Long pendingXid;
private BatchState state;
private final ExecutionList executionList = new ExecutionList();
public InstallationFuture(Set<Dpid> sws, Map<Long, FlowRuleBatchEntry> fmXids) {
+ this.xid = xidCounter.getAndIncrement();
this.state = BatchState.STARTED;
this.sws = sws;
this.fms = fmXids;
countDownLatch = new CountDownLatch(sws.size());
}
+ public Long xid() {
+ return xid;
+ }
+
public void fail(OFErrorMsg msg, Dpid dpid) {
ok.set(false);
@@ -385,27 +399,27 @@
case BAD_ACTION:
OFBadActionErrorMsg bad = (OFBadActionErrorMsg) msg;
fe = new DefaultFlowEntry(offending, bad.getErrType().ordinal(),
- bad.getCode().ordinal());
+ bad.getCode().ordinal());
break;
case BAD_INSTRUCTION:
OFBadInstructionErrorMsg badins = (OFBadInstructionErrorMsg) msg;
fe = new DefaultFlowEntry(offending, badins.getErrType().ordinal(),
- badins.getCode().ordinal());
+ badins.getCode().ordinal());
break;
case BAD_MATCH:
OFBadMatchErrorMsg badMatch = (OFBadMatchErrorMsg) msg;
fe = new DefaultFlowEntry(offending, badMatch.getErrType().ordinal(),
- badMatch.getCode().ordinal());
+ badMatch.getCode().ordinal());
break;
case BAD_REQUEST:
OFBadRequestErrorMsg badReq = (OFBadRequestErrorMsg) msg;
fe = new DefaultFlowEntry(offending, badReq.getErrType().ordinal(),
- badReq.getCode().ordinal());
+ badReq.getCode().ordinal());
break;
case FLOW_MOD_FAILED:
OFFlowModFailedErrorMsg fmFail = (OFFlowModFailedErrorMsg) msg;
fe = new DefaultFlowEntry(offending, fmFail.getErrType().ordinal(),
- fmFail.getCode().ordinal());
+ fmFail.getCode().ordinal());
break;
case EXPERIMENTER:
case GROUP_MOD_FAILED:
@@ -434,13 +448,12 @@
}
- public void verify(Long id) {
- pendingXid = id;
+ public void verify() {
for (Dpid dpid : sws) {
OpenFlowSwitch sw = controller.getSwitch(dpid);
OFBarrierRequest.Builder builder = sw.factory()
.buildBarrierRequest()
- .setXid(id);
+ .setXid(xid);
sw.sendMsg(builder.build());
}
}
@@ -462,7 +475,6 @@
}
}
- invokeCallbacks();
return true;
}
@@ -481,6 +493,7 @@
countDownLatch.await();
this.state = BatchState.FINISHED;
CompletedBatchOperation result = new CompletedBatchOperation(ok.get(), offendingFlowMods);
+ //FIXME do cleanup here
return result;
}
@@ -491,6 +504,7 @@
if (countDownLatch.await(timeout, unit)) {
this.state = BatchState.FINISHED;
CompletedBatchOperation result = new CompletedBatchOperation(ok.get(), offendingFlowMods);
+ // FIXME do cleanup here
return result;
}
throw new TimeoutException();
@@ -498,9 +512,7 @@
private void cleanUp() {
if (isDone() || isCancelled()) {
- if (pendingXid != null) {
- pendingFutures.remove(pendingXid);
- }
+ pendingFutures.remove(xid);
for (Long xid : fms.keySet()) {
pendingFMs.remove(xid);
}
@@ -509,21 +521,10 @@
private void removeRequirement(Dpid dpid) {
countDownLatch.countDown();
- if (countDownLatch.getCount() == 0) {
- invokeCallbacks();
- }
sws.remove(dpid);
+ //FIXME don't do cleanup here
cleanUp();
}
-
- @Override
- public void addListener(Runnable runnable, Executor executor) {
- executionList.add(runnable, executor);
- }
-
- private void invokeCallbacks() {
- executionList.execute();
- }
}
}
diff --git a/tools/package/config/README b/tools/package/config/README
index 62b758d..970f87a 100644
--- a/tools/package/config/README
+++ b/tools/package/config/README
@@ -1,2 +1,2 @@
-onos-config command will copy files contained in this directory to ONOS instances according to cell definition
-
+The onos-config command will copy files contained in this directory to ONOS
+instances according to cell definition.
diff --git a/tools/test/bin/onos b/tools/test/bin/onos
index a0c126c..b18bae8 100755
--- a/tools/test/bin/onos
+++ b/tools/test/bin/onos
@@ -10,4 +10,5 @@
[ "$1" = "-w" ] && shift && onos-wait-for-start $1
[ -n "$1" ] && OCI=$(find_node $1) && shift
+unset KARAF_HOME
client -h $OCI -u karaf "$@" 2>/dev/null
diff --git a/tools/test/bin/onos-gui b/tools/test/bin/onos-gui
index a0c843d..517ed11 100755
--- a/tools/test/bin/onos-gui
+++ b/tools/test/bin/onos-gui
@@ -3,10 +3,7 @@
# Launches ONOS GUI on the specified node.
# -----------------------------------------------------------------------------
-[ ! -d "$ONOS_ROOT" ] && echo "ONOS_ROOT is not defined" >&2 && exit 1
-. $ONOS_ROOT/tools/build/envDefaults
-
host=${1:-$OCI}
host=${host:-localhost}
-open http://$host:8181/onos/tvue
+open http://$host:8181/onos/ui
diff --git a/tools/test/bin/onos-show-cell b/tools/test/bin/onos-show-cell
index eedea9e..5aee338 100755
--- a/tools/test/bin/onos-show-cell
+++ b/tools/test/bin/onos-show-cell
@@ -40,6 +40,7 @@
# Load the cell setup
. $ONOS_ROOT/tools/test/cells/${cell}
+echo "ONOS_CELL=${ONOS_CELL}"
echo "ONOS_NIC=${ONOS_NIC}"
for n in {0..9}; do
ocn="OC${n}"
diff --git a/tools/test/topos/oe-nonlinear-10.json b/tools/test/topos/oe-nonlinear-10.json
index f23bb9b..522215b 100644
--- a/tools/test/topos/oe-nonlinear-10.json
+++ b/tools/test/topos/oe-nonlinear-10.json
@@ -108,33 +108,26 @@
],
"links" : [
- { "src": "of:0000ffffffffff01/10", "dst": "of:0000ffffffffff02/10", "type": "OPTICAL", "annotations": { "optical.waves": 80, "optical.type": "WDM", "optical.kms": 1000, "durable": "true" } },
- { "src": "of:0000ffffffffff02/10", "dst": "of:0000ffffffffff03/10", "type": "OPTICAL", "annotations": { "optical.waves": 80, "optical.type": "WDM", "optical.kms": 1000, "durable": "true" } },
- { "src": "of:0000ffffffffff03/30", "dst": "of:0000ffffffffff04/10", "type": "OPTICAL", "annotations": { "optical.waves": 80, "optical.type": "WDM", "optical.kms": 1000, "durable": "true" } },
- { "src": "of:0000ffffffffff02/20", "dst": "of:0000ffffffffff05/10", "type": "OPTICAL", "annotations": { "optical.waves": 80, "optical.type": "WDM", "optical.kms": 1000, "durable": "true" } },
- { "src": "of:0000ffffffffff03/20", "dst": "of:0000ffffffffff06/10", "type": "OPTICAL", "annotations": { "optical.waves": 80, "optical.type": "WDM", "optical.kms": 1000, "durable": "true" } },
- { "src": "of:0000ffffffffff05/30", "dst": "of:0000ffffffffff06/20", "type": "OPTICAL", "annotations": { "optical.waves": 80, "optical.type": "WDM", "optical.kms": 1000, "durable": "true" } },
- { "src": "of:0000ffffffffff05/20", "dst": "of:0000ffffffffff07/21", "type": "OPTICAL", "annotations": { "optical.waves": 80, "optical.type": "WDM", "optical.kms": 1000, "durable": "true" } },
- { "src": "of:0000ffffffffff06/30", "dst": "of:0000ffffffffff08/10", "type": "OPTICAL", "annotations": { "optical.waves": 80, "optical.type": "WDM", "optical.kms": 1000, "durable": "true" } },
- { "src": "of:0000ffffffffff07/30", "dst": "of:0000ffffffffff08/20", "type": "OPTICAL", "annotations": { "optical.waves": 80, "optical.type": "WDM", "optical.kms": 1000, "durable": "true" } },
- { "src": "of:0000ffffffffff07/20", "dst": "of:0000ffffffffff09/10", "type": "OPTICAL", "annotations": { "optical.waves": 80, "optical.type": "WDM", "optical.kms": 1000, "durable": "true" } },
- { "src": "of:0000ffffffffff08/30", "dst": "of:0000ffffffffff0A/10", "type": "OPTICAL", "annotations": { "optical.waves": 80, "optical.type": "WDM", "optical.kms": 1000, "durable": "true" } },
+ { "src": "of:0000ffffffffff01/50", "dst": "of:0000ffffffffff02/30","type": "OPTICAL", "annotations": { "optical.waves": 80, "optical.type": "WDM", "optical.kms": 1000, "durable": "true" } },
+ { "src": "of:0000ffffffffff02/50", "dst": "of:0000ffffffffff03/30","type": "OPTICAL", "annotations": { "optical.waves": 80, "optical.type": "WDM", "optical.kms": 1000, "durable": "true" } },
+ { "src": "of:0000ffffffffff03/50", "dst": "of:0000ffffffffff04/50","type": "OPTICAL", "annotations": { "optical.waves": 80, "optical.type": "WDM", "optical.kms": 1000, "durable": "true" } },
+ { "src": "of:0000ffffffffff01/20", "dst": "of:0000ffffffffff05/50","type": "OPTICAL", "annotations": { "optical.waves": 80, "optical.type": "WDM", "optical.kms": 1000, "durable": "true" } },
+ { "src": "of:0000ffffffffff02/20", "dst": "of:0000ffffffffff05/20","type": "OPTICAL", "annotations": { "optical.waves": 80, "optical.type": "WDM", "optical.kms": 1000, "durable": "true" } },
+ { "src": "of:0000ffffffffff03/20", "dst": "of:0000ffffffffff06/50","type": "OPTICAL", "annotations": { "optical.waves": 80, "optical.type": "WDM", "optical.kms": 1000, "durable": "true" } },
+ { "src": "of:0000ffffffffff04/20", "dst": "of:0000ffffffffff06/20","type": "OPTICAL", "annotations": { "optical.waves": 80, "optical.type": "WDM", "optical.kms": 1000, "durable": "true" } },
+ { "src": "of:0000ffffffffff05/30", "dst": "of:0000ffffffffff06/40","type": "OPTICAL", "annotations": { "optical.waves": 80, "optical.type": "WDM", "optical.kms": 1000, "durable": "true" } },
+ { "src": "of:0000ffffffffff05/40", "dst": "of:0000ffffffffff07/50", "type": "OPTICAL", "annotations": { "optical.waves": 80, "optical.type": "WDM", "optical.kms": 1000, "durable": "true" } },
+ { "src": "of:0000ffffffffff06/30", "dst": "of:0000ffffffffff08/50", "type": "OPTICAL", "annotations": { "optical.waves": 80, "optical.type": "WDM", "optical.kms": 1000, "durable": "true" } },
+ { "src": "of:0000ffffffffff07/20", "dst": "of:0000ffffffffff08/30", "type": "OPTICAL", "annotations": { "optical.waves": 80, "optical.type": "WDM", "optical.kms": 1000, "durable": "true" } },
+ { "src": "of:0000ffffffffff07/30", "dst": "of:0000ffffffffff09/50", "type": "OPTICAL", "annotations": { "optical.waves": 80, "optical.type": "WDM", "optical.kms": 1000, "durable": "true" } },
+ { "src": "of:0000ffffffffff08/20", "dst": "of:0000ffffffffff0A/50", "type": "OPTICAL", "annotations": { "optical.waves": 80, "optical.type": "WDM", "optical.kms": 1000, "durable": "true" } },
{ "src": "of:0000ffffffffff09/20", "dst": "of:0000ffffffffff0A/20", "type": "OPTICAL", "annotations": { "optical.waves": 80, "optical.type": "WDM", "optical.kms": 1000, "durable": "true" } },
- { "src": "of:0000ffffffff0001/2", "dst": "of:0000ffffffffff01/1", "type": "OPTICAL", "annotations": { "bandwidth": 100000, "optical.type": "cross-connect", "durable": "true" } },
- { "src": "of:0000ffffffff0003/2", "dst": "of:0000ffffffffff03/1", "type": "OPTICAL", "annotations": { "bandwidth": 100000, "optical.type": "cross-connect", "durable": "true" } },
- { "src": "of:0000ffffffff0004/2", "dst": "of:0000ffffffffff04/1", "type": "OPTICAL", "annotations": { "bandwidth": 100000, "optical.type": "cross-connect", "durable": "true" } },
- { "src": "of:0000ffffffff0007/2", "dst": "of:0000ffffffffff07/1", "type": "OPTICAL", "annotations": { "bandwidth": 100000, "optical.type": "cross-connect", "durable": "true" } },
- { "src": "of:0000ffffffff0009/2", "dst": "of:0000ffffffffff09/1", "type": "OPTICAL", "annotations": { "bandwidth": 100000, "optical.type": "cross-connect", "durable": "true" } },
- { "src": "of:0000ffffffff000A/2", "dst": "of:0000ffffffffff0A/1", "type": "OPTICAL", "annotations": { "bandwidth": 100000, "optical.type": "cross-connect", "durable": "true" } }
- ],
-
- "hosts" : [
- { "mac": "00:00:00:00:00:01", "vlan": -1, "location": "of:0000ffffffff0001/1", "ip": "10.0.0.1" },
- { "mac": "00:00:00:00:00:03", "vlan": -1, "location": "of:0000ffffffff0003/1", "ip": "10.0.0.3" },
- { "mac": "00:00:00:00:00:04", "vlan": -1, "location": "of:0000ffffffff0004/1", "ip": "10.0.0.4" },
- { "mac": "00:00:00:00:00:07", "vlan": -1, "location": "of:0000ffffffff0007/1", "ip": "10.0.0.7" },
- { "mac": "00:00:00:00:00:09", "vlan": -1, "location": "of:0000ffffffff0009/1", "ip": "10.0.0.9" },
- { "mac": "00:00:00:00:00:0A", "vlan": -1, "location": "of:0000ffffffff000A/1", "ip": "10.0.0.10" }
+ { "src": "of:0000ffffffff0001/2", "dst": "of:0000ffffffffff01/10", "type": "OPTICAL", "annotations": { "bandwidth": 100000, "optical.type": "cross-connect", "durable": "true" } },
+ { "src": "of:0000ffffffff0002/2", "dst": "of:0000ffffffffff04/10", "type": "OPTICAL", "annotations": { "bandwidth": 100000, "optical.type": "cross-connect", "durable": "true" } },
+ { "src": "of:0000ffffffff0003/2", "dst": "of:0000ffffffffff06/10", "type": "OPTICAL", "annotations": { "bandwidth": 100000, "optical.type": "cross-connect", "durable": "true" } },
+ { "src": "of:0000ffffffff0004/2", "dst": "of:0000ffffffffff07/10", "type": "OPTICAL", "annotations": { "bandwidth": 100000, "optical.type": "cross-connect", "durable": "true" } },
+ { "src": "of:0000ffffffff0005/2", "dst": "of:0000ffffffffff09/10", "type": "OPTICAL", "annotations": { "bandwidth": 100000, "optical.type": "cross-connect", "durable": "true" } },
+ { "src": "of:0000ffffffff0006/2", "dst": "of:0000ffffffffff0A/10", "type": "OPTICAL", "annotations": { "bandwidth": 100000, "optical.type": "cross-connect", "durable": "true" } }
]
-}
\ No newline at end of file
+}
diff --git a/utils/junit/src/main/java/org/onlab/junit/ImmutableClassChecker.java b/utils/junit/src/main/java/org/onlab/junit/ImmutableClassChecker.java
index 2ac20b7..421a3e9 100644
--- a/utils/junit/src/main/java/org/onlab/junit/ImmutableClassChecker.java
+++ b/utils/junit/src/main/java/org/onlab/junit/ImmutableClassChecker.java
@@ -43,9 +43,9 @@
* @param clazz the class to check
* @return true if the given class is a properly specified immutable class.
*/
- private boolean isImmutableClass(Class<?> clazz) {
+ private boolean isImmutableClass(Class<?> clazz, boolean allowNonFinalClass) {
// class must be declared final
- if (!Modifier.isFinal(clazz.getModifiers())) {
+ if (!allowNonFinalClass && !Modifier.isFinal(clazz.getModifiers())) {
failureReason = "a class that is not final";
return false;
}
@@ -113,16 +113,16 @@
}
/**
- * Assert that the given class adheres to the utility class rules.
+ * Assert that the given class adheres to the immutable class rules.
*
* @param clazz the class to check
*
- * @throws java.lang.AssertionError if the class is not a valid
- * utility class
+ * @throws java.lang.AssertionError if the class is not an
+ * immutable class
*/
public static void assertThatClassIsImmutable(Class<?> clazz) {
final ImmutableClassChecker checker = new ImmutableClassChecker();
- if (!checker.isImmutableClass(clazz)) {
+ if (!checker.isImmutableClass(clazz, false)) {
final Description toDescription = new StringDescription();
final Description mismatchDescription = new StringDescription();
@@ -136,4 +136,31 @@
throw new AssertionError(reason);
}
}
+
+ /**
+ * Assert that the given class adheres to the immutable class rules, but
+ * is not declared final. Classes that need to be inherited from cannot be
+ * declared final.
+ *
+ * @param clazz the class to check
+ *
+ * @throws java.lang.AssertionError if the class is not an
+ * immutable class
+ */
+ public static void assertThatClassIsImmutableBaseClass(Class<?> clazz) {
+ final ImmutableClassChecker checker = new ImmutableClassChecker();
+ if (!checker.isImmutableClass(clazz, true)) {
+ final Description toDescription = new StringDescription();
+ final Description mismatchDescription = new StringDescription();
+
+ checker.describeTo(toDescription);
+ checker.describeMismatch(mismatchDescription);
+ final String reason =
+ "\n" +
+ "Expected: is \"" + toDescription.toString() + "\"\n" +
+ " but : was \"" + mismatchDescription.toString() + "\"";
+
+ throw new AssertionError(reason);
+ }
+ }
}
diff --git a/web/gui/src/main/java/org/onlab/onos/gui/TopologyMessages.java b/web/gui/src/main/java/org/onlab/onos/gui/TopologyMessages.java
index 0b54dd8..f747423 100644
--- a/web/gui/src/main/java/org/onlab/onos/gui/TopologyMessages.java
+++ b/web/gui/src/main/java/org/onlab/onos/gui/TopologyMessages.java
@@ -24,6 +24,7 @@
import org.onlab.onos.cluster.ControllerNode;
import org.onlab.onos.cluster.NodeId;
import org.onlab.onos.mastership.MastershipService;
+import org.onlab.onos.net.Annotated;
import org.onlab.onos.net.Annotations;
import org.onlab.onos.net.ConnectPoint;
import org.onlab.onos.net.DefaultEdgeLink;
@@ -45,6 +46,8 @@
import org.onlab.onos.net.provider.ProviderId;
import org.onlab.osgi.ServiceDirectory;
import org.onlab.packet.IpAddress;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import java.util.Iterator;
import java.util.Map;
@@ -68,6 +71,8 @@
*/
public abstract class TopologyMessages {
+ protected static final Logger log = LoggerFactory.getLogger(TopologyMessages.class);
+
private static final ProviderId PID = new ProviderId("core", "org.onlab.onos.core", true);
private static final String COMPACT = "%s/%s-%s/%s";
@@ -195,7 +200,7 @@
.put("id", device.id().toString())
.put("type", device.type().toString().toLowerCase())
.put("online", deviceService.isAvailable(device.id()))
- .put("master", mastershipService.getMasterFor(device.id()).toString());
+ .put("master", master(device.id()));
// Generate labels: id, chassis id, no-label, optional-name
ArrayNode labels = mapper.createArrayNode();
@@ -207,6 +212,7 @@
// Add labels, props and stuff the payload into envelope.
payload.set("labels", labels);
payload.set("props", props(device.annotations()));
+ addGeoLocation(device, payload);
addMetaUi(device.id().toString(), payload);
String type = (event.type() == DEVICE_ADDED) ? "addDevice" :
@@ -220,6 +226,7 @@
ObjectNode payload = mapper.createObjectNode()
.put("id", compactLinkString(link))
.put("type", link.type().toString().toLowerCase())
+ .put("online", true) // TODO: add link state field
.put("linkWidth", 2)
.put("src", link.src().deviceId().toString())
.put("srcPort", link.src().port().toString())
@@ -237,10 +244,11 @@
.put("id", host.id().toString())
.put("ingress", compactLinkString(edgeLink(host, true)))
.put("egress", compactLinkString(edgeLink(host, false)));
- payload.set("cp", location(mapper, host.location()));
+ payload.set("cp", hostConnect(mapper, host.location()));
payload.set("labels", labels(mapper, ip(host.ipAddresses()),
host.mac().toString()));
payload.set("props", props(host.annotations()));
+ addGeoLocation(host, payload);
addMetaUi(host.id().toString(), payload);
String type = (event.type() == HOST_ADDED) ? "addHost" :
@@ -249,7 +257,7 @@
}
// Encodes the specified host location into a JSON object.
- private ObjectNode location(ObjectMapper mapper, HostLocation location) {
+ private ObjectNode hostConnect(ObjectMapper mapper, HostLocation location) {
return mapper.createObjectNode()
.put("device", location.deviceId().toString())
.put("port", location.port().toLong());
@@ -264,6 +272,12 @@
return json;
}
+ // Returns the name of the master node for the specified device id.
+ private String master(DeviceId deviceId) {
+ NodeId master = mastershipService.getMasterFor(deviceId);
+ return master != null ? master.toString() : "";
+ }
+
// Generates an edge link from the specified host location.
private EdgeLink edgeLink(Host host, boolean ingress) {
return new DefaultEdgeLink(PID, new ConnectPoint(host.id(), portNumber(0)),
@@ -278,10 +292,28 @@
}
}
+ // Adds a geo location JSON to the specified payload object.
+ private void addGeoLocation(Annotated annotated, ObjectNode payload) {
+ Annotations annotations = annotated.annotations();
+ String slat = annotations.value("latitude");
+ String slng = annotations.value("longitude");
+ try {
+ if (slat != null && slng != null && !slat.isEmpty() && !slng.isEmpty()) {
+ double lat = Double.parseDouble(slat);
+ double lng = Double.parseDouble(slng);
+ ObjectNode loc = mapper.createObjectNode()
+ .put("type", "latlng").put("lat", lat).put("lng", lng);
+ payload.set("location", loc);
+ }
+ } catch (NumberFormatException e) {
+ log.warn("Invalid geo data latitude={}; longiture={}", slat, slng);
+ }
+ }
+
// Updates meta UI information for the specified object.
protected void updateMetaUi(ObjectNode event) {
ObjectNode payload = payload(event);
- metaUi.put(string(payload, "id"), payload);
+ metaUi.put(string(payload, "id"), (ObjectNode) payload.path("memento"));
}
// Returns device details response.
@@ -289,7 +321,6 @@
Device device = deviceService.getDevice(deviceId);
Annotations annot = device.annotations();
int portCount = deviceService.getPorts(deviceId).size();
- NodeId master = mastershipService.getMasterFor(device.id());
return envelope("showDetails", sid,
json(deviceId.toString(),
device.type().toString().toLowerCase(),
@@ -303,7 +334,7 @@
new Prop("Longitude", annot.value("longitude")),
new Prop("Ports", Integer.toString(portCount)),
new Separator(),
- new Prop("Master", master.toString())));
+ new Prop("Master", master(deviceId))));
}
// Returns host details response.
@@ -319,16 +350,15 @@
new Prop("Longitude", annot.value("longitude"))));
}
-
// Produces a path message to the client.
- protected ObjectNode pathMessage(Path path) {
+ protected ObjectNode pathMessage(Path path, String type) {
ObjectNode payload = mapper.createObjectNode();
ArrayNode links = mapper.createArrayNode();
for (Link link : path.links()) {
links.add(compactLinkString(link));
}
- payload.set("links", links);
+ payload.put("type", type).set("links", links);
return payload;
}
diff --git a/web/gui/src/main/java/org/onlab/onos/gui/TopologyWebSocket.java b/web/gui/src/main/java/org/onlab/onos/gui/TopologyWebSocket.java
index 83e54b4..2506b13 100644
--- a/web/gui/src/main/java/org/onlab/onos/gui/TopologyWebSocket.java
+++ b/web/gui/src/main/java/org/onlab/onos/gui/TopologyWebSocket.java
@@ -110,8 +110,8 @@
try {
ObjectNode event = (ObjectNode) mapper.reader().readTree(data);
String type = string(event, "event", "unknown");
- if (type.equals("showDetails")) {
- showDetails(event);
+ if (type.equals("requestDetails")) {
+ requestDetails(event);
} else if (type.equals("updateMeta")) {
updateMetaUi(event);
} else if (type.equals("requestPath")) {
@@ -122,7 +122,7 @@
cancelTraffic(event);
}
} catch (Exception e) {
- System.out.println("WTF?! " + data);
+ log.warn("Unable to parse GUI request {} due to {}", data, e);
e.printStackTrace();
}
}
@@ -165,9 +165,9 @@
}
// Sends back device or host details.
- private void showDetails(ObjectNode event) {
+ private void requestDetails(ObjectNode event) {
ObjectNode payload = payload(event);
- String type = string(payload, "type", "unknown");
+ String type = string(payload, "class", "unknown");
if (type.equals("device")) {
sendMessage(deviceDetails(deviceId(string(payload, "id")),
number(event, "sid")));
@@ -282,7 +282,8 @@
if (installable != null && !installable.isEmpty()) {
PathIntent pathIntent = (PathIntent) installable.iterator().next();
Path path = pathIntent.path();
- ObjectNode payload = pathMessage(path).put("intentId", intent.id().toString());
+ ObjectNode payload = pathMessage(path, "host")
+ .put("intentId", intent.id().toString());
sendMessage(envelope("showPath", sid, payload));
}
}
diff --git a/web/gui/src/main/webapp/WEB-INF/web.xml b/web/gui/src/main/webapp/WEB-INF/web.xml
index ab7a550..32f1ddd 100644
--- a/web/gui/src/main/webapp/WEB-INF/web.xml
+++ b/web/gui/src/main/webapp/WEB-INF/web.xml
@@ -21,7 +21,7 @@
<display-name>ONOS GUI</display-name>
<welcome-file-list>
- <welcome-file>index.html</welcome-file>
+ <welcome-file>index2.html</welcome-file>
</welcome-file-list>
<servlet>
diff --git a/web/gui/src/main/webapp/floatPanel.css b/web/gui/src/main/webapp/floatPanel.css
new file mode 100644
index 0000000..1c5a815
--- /dev/null
+++ b/web/gui/src/main/webapp/floatPanel.css
@@ -0,0 +1,45 @@
+/*
+ * 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 -- Floating Panels -- CSS file
+
+ @author Simon Hunt
+ */
+
+.fpanel {
+ 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;
+}
+
+/* TODO: light/dark themes */
+.light .fpanel {
+
+}
+.dark .fpanel {
+
+}
diff --git a/web/gui/src/main/webapp/index2.html b/web/gui/src/main/webapp/index2.html
index 03272e9..ca5b6a8 100644
--- a/web/gui/src/main/webapp/index2.html
+++ b/web/gui/src/main/webapp/index2.html
@@ -40,6 +40,7 @@
<link rel="stylesheet" href="base.css">
<link rel="stylesheet" href="onos2.css">
<link rel="stylesheet" href="mast2.css">
+ <link rel="stylesheet" href="floatPanel.css">
<!-- This is where contributed stylesheets get INJECTED -->
<!-- TODO: replace with template marker and inject refs server-side -->
@@ -62,8 +63,9 @@
<div id="view">
<!-- NOTE: views injected here by onos.js -->
</div>
- <div id="overlays">
- <!-- NOTE: overlays injected here, as needed -->
+ <div id="floatPanels">
+ <!-- NOTE: floating panels injected here, as needed -->
+ <!-- see onos.ui.addFloatingPanel -->
</div>
<div id="alerts">
<!-- NOTE: alert content injected here, as needed -->
diff --git a/web/gui/src/main/webapp/json/ev/_capture/rx/addDevice_ex1.json b/web/gui/src/main/webapp/json/ev/_capture/rx/addDevice_ex1.json
index f00cf2c..b82fda8 100644
--- a/web/gui/src/main/webapp/json/ev/_capture/rx/addDevice_ex1.json
+++ b/web/gui/src/main/webapp/json/ev/_capture/rx/addDevice_ex1.json
@@ -10,6 +10,10 @@
"",
null
],
- "props": {}
+ "props": {
+ "latitude": 123.5,
+ "longitude": 67.8,
+ "anotherProp": "foobar"
+ }
}
}
diff --git a/web/gui/src/main/webapp/json/ev/_capture/rx/addDevice_ex2_memo.json b/web/gui/src/main/webapp/json/ev/_capture/rx/addDevice_ex2_memo.json
new file mode 100644
index 0000000..5f519ff
--- /dev/null
+++ b/web/gui/src/main/webapp/json/ev/_capture/rx/addDevice_ex2_memo.json
@@ -0,0 +1,24 @@
+{
+ "event": "addDevice",
+ "payload": {
+ "id": "of:0000000000000003",
+ "type": "switch",
+ "online": true,
+ "labels": [
+ "of:0000000000000003",
+ "3",
+ "",
+ null
+ ],
+ "props": {
+ "latitude": 123.5,
+ "longitude": 67.8,
+ "anotherProp": "foobar"
+ },
+ "metaUi": {
+ "xpc": 57.3,
+ "ypc": 24.86,
+ "and": "other properties the UI wishes to remember..."
+ }
+ }
+}
diff --git a/web/gui/src/main/webapp/json/ev/_capture/rx/showDetails_ex1_host.json b/web/gui/src/main/webapp/json/ev/_capture/rx/showDetails_ex1_host.json
new file mode 100644
index 0000000..19d9959
--- /dev/null
+++ b/web/gui/src/main/webapp/json/ev/_capture/rx/showDetails_ex1_host.json
@@ -0,0 +1,22 @@
+{
+ "event": "showDetails",
+ "sid": 9,
+ "payload": {
+ "id": "CA:4B:EE:A4:B0:33/-1",
+ "type": "host",
+ "propOrder": [
+ "MAC",
+ "IP",
+ "-",
+ "Latitude",
+ "Longitude"
+ ],
+ "props": {
+ "MAC": "CA:4B:EE:A4:B0:33",
+ "IP": "[10.0.0.1]",
+ "-": "",
+ "Latitude": null,
+ "Longitude": null
+ }
+ }
+}
diff --git a/web/gui/src/main/webapp/json/ev/_capture/rx/showDetails_ex2_device.json b/web/gui/src/main/webapp/json/ev/_capture/rx/showDetails_ex2_device.json
new file mode 100644
index 0000000..8ac1f4f
--- /dev/null
+++ b/web/gui/src/main/webapp/json/ev/_capture/rx/showDetails_ex2_device.json
@@ -0,0 +1,33 @@
+{
+ "event": "showDetails",
+ "sid": 37,
+ "payload": {
+ "id": "of:000000000000000a",
+ "type": "switch",
+ "propOrder": [
+ "Name",
+ "Vendor",
+ "H/W Version",
+ "S/W Version",
+ "Serial Number",
+ "-",
+ "Latitude",
+ "Longitude",
+ "Ports",
+ "-",
+ "Master"
+ ],
+ "props": {
+ "Name": null,
+ "Vendor": "Nicira, Inc.",
+ "H/W Version": "Open vSwitch",
+ "S/W Version": "2.0.1",
+ "Serial Number": "None",
+ "-": "",
+ "Latitude": null,
+ "Longitude": null,
+ "Ports": "5",
+ "Master":"local"
+ }
+ }
+}
diff --git a/web/gui/src/main/webapp/json/ev/_capture/tx/requestDetails_ex1.json b/web/gui/src/main/webapp/json/ev/_capture/tx/requestDetails_ex1.json
new file mode 100644
index 0000000..6c88605
--- /dev/null
+++ b/web/gui/src/main/webapp/json/ev/_capture/tx/requestDetails_ex1.json
@@ -0,0 +1,9 @@
+{
+ "event": "requestDetails",
+ "sid": 15,
+ "payload": {
+ "id": "of:0000000000000003",
+ "class": "device"
+ }
+}
+
diff --git a/web/gui/src/main/webapp/json/ev/_capture/tx/requestDetails_ex2.json b/web/gui/src/main/webapp/json/ev/_capture/tx/requestDetails_ex2.json
new file mode 100644
index 0000000..2cc1bfa
--- /dev/null
+++ b/web/gui/src/main/webapp/json/ev/_capture/tx/requestDetails_ex2.json
@@ -0,0 +1,8 @@
+{
+ "event": "requestDetails",
+ "sid": 9,
+ "payload": {
+ "id": "CA:4B:EE:A4:B0:33/-1",
+ "class": "host"
+ }
+}
diff --git a/web/gui/src/main/webapp/json/ev/_capture/tx/updateMeta_ex1.json b/web/gui/src/main/webapp/json/ev/_capture/tx/updateMeta_ex1.json
index c04727e..6114583 100644
--- a/web/gui/src/main/webapp/json/ev/_capture/tx/updateMeta_ex1.json
+++ b/web/gui/src/main/webapp/json/ev/_capture/tx/updateMeta_ex1.json
@@ -4,7 +4,11 @@
"payload": {
"id": "62:4F:65:BF:FF:B3/-1",
"class": "host",
- "x": 197,
- "y": 177
+ "memento": {
+ "xpc": 57.3,
+ "ypc": 24.86,
+ "and": "other properties the UI wishes to remember..."
+ }
}
}
+
diff --git a/web/gui/src/main/webapp/json/ev/intentSketch/scenario.json b/web/gui/src/main/webapp/json/ev/intentSketch/scenario.json
index 136d027..f109dde 100644
--- a/web/gui/src/main/webapp/json/ev/intentSketch/scenario.json
+++ b/web/gui/src/main/webapp/json/ev/intentSketch/scenario.json
@@ -5,5 +5,9 @@
"title": "Host Intent Scenario",
"params": {
"lastAuto": 0
- }
+ },
+ "description": [
+ "Currently this is just a sketch of the event sequence,",
+ " but is NOT YET a runnable scenario."
+ ]
}
\ No newline at end of file
diff --git a/web/gui/src/main/webapp/json/ev/simple/ev_13_onos.json b/web/gui/src/main/webapp/json/ev/simple/ev_13_onos.json
new file mode 100644
index 0000000..5320841
--- /dev/null
+++ b/web/gui/src/main/webapp/json/ev/simple/ev_13_onos.json
@@ -0,0 +1,17 @@
+{
+ "event": "removeHost",
+ "payload": {
+ "id": "A6:96:E5:03:52:5F/-1",
+ "ingress": "A6:96:E5:03:52:5F/-1/0-of:0000ffffffff0008/1",
+ "egress": "of:0000ffffffff0008/1-A6:96:E5:03:52:5F/-1/0",
+ "cp": {
+ "device": "of:0000ffffffff0008",
+ "port": 1
+ },
+ "labels": [
+ "10.0.0.17",
+ "A6:96:E5:03:52:5F"
+ ],
+ "props": {}
+ }
+}
diff --git a/web/gui/src/main/webapp/json/ev/simple/ev_13_ui.json b/web/gui/src/main/webapp/json/ev/simple/ev_13_ui.json
deleted file mode 100644
index 9d6e737..0000000
--- a/web/gui/src/main/webapp/json/ev/simple/ev_13_ui.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
- "event": "noop",
- "payload": {
- "id": "xyyzy"
- }
-}
diff --git a/web/gui/src/main/webapp/json/ev/simple/ev_1_onos.json b/web/gui/src/main/webapp/json/ev/simple/ev_1_onos.json
index 8656a90..fa7ae6e 100644
--- a/web/gui/src/main/webapp/json/ev/simple/ev_1_onos.json
+++ b/web/gui/src/main/webapp/json/ev/simple/ev_1_onos.json
@@ -4,6 +4,11 @@
"id": "of:0000ffffffff0008",
"type": "switch",
"online": false,
+ "location": {
+ "type": "latlng",
+ "lat": 37.6,
+ "lng": 122.3
+ },
"labels": [
"0000ffffffff0008",
"FF:FF:FF:FF:00:08",
diff --git a/web/gui/src/main/webapp/json/ev/simple/scenario.json b/web/gui/src/main/webapp/json/ev/simple/scenario.json
index 5fb8869..4c55b2d 100644
--- a/web/gui/src/main/webapp/json/ev/simple/scenario.json
+++ b/web/gui/src/main/webapp/json/ev/simple/scenario.json
@@ -20,6 +20,6 @@
"10. update link (increase width, update props)",
"11. update link (reduce width, update props)",
"12. remove link",
- ""
+ "13. remove host (10.0.0.17)"
]
-}
\ No newline at end of file
+}
diff --git a/web/gui/src/main/webapp/json/ev/startup/ev_10_onos.json b/web/gui/src/main/webapp/json/ev/startup/ev_10_onos.json
index 9b30b4a..ec0f258 100644
--- a/web/gui/src/main/webapp/json/ev/startup/ev_10_onos.json
+++ b/web/gui/src/main/webapp/json/ev/startup/ev_10_onos.json
@@ -3,7 +3,7 @@
"payload": {
"id": "of:0000ffffffffff04",
"type": "roadm",
- "online": false,
+ "online": true,
"labels": [
"0000ffffffffff04",
"FF:FF:FF:FF:FF:04",
diff --git a/web/gui/src/main/webapp/json/ev/startup/ev_11_onos.json b/web/gui/src/main/webapp/json/ev/startup/ev_11_onos.json
index 3b361d5..04e6754 100644
--- a/web/gui/src/main/webapp/json/ev/startup/ev_11_onos.json
+++ b/web/gui/src/main/webapp/json/ev/startup/ev_11_onos.json
@@ -3,15 +3,15 @@
"payload": {
"id": "of:0000ffffffff000A",
"type": "switch",
- "online": false,
+ "online": true,
"labels": [
"0000ffffffff000A",
"FF:FF:FF:FF:00:0A",
"?"
],
"metaUi": {
- "Zx": 832,
- "Zy": 223
+ "x": 832,
+ "y": 223
}
}
}
diff --git a/web/gui/src/main/webapp/json/ev/startup/ev_12_onos.json b/web/gui/src/main/webapp/json/ev/startup/ev_12_onos.json
index e53a6cd..c778cd5 100644
--- a/web/gui/src/main/webapp/json/ev/startup/ev_12_onos.json
+++ b/web/gui/src/main/webapp/json/ev/startup/ev_12_onos.json
@@ -3,7 +3,7 @@
"payload": {
"id": "of:0000ffffffff0001",
"type": "switch",
- "online": false,
+ "online": true,
"labels": [
"0000ffffffff0001",
"FF:FF:FF:FF:00:01",
diff --git a/web/gui/src/main/webapp/json/ev/startup/ev_13_onos.json b/web/gui/src/main/webapp/json/ev/startup/ev_13_onos.json
index 6d2341f..a0cc21f 100644
--- a/web/gui/src/main/webapp/json/ev/startup/ev_13_onos.json
+++ b/web/gui/src/main/webapp/json/ev/startup/ev_13_onos.json
@@ -3,7 +3,7 @@
"payload": {
"id": "of:0000ffffffffff01",
"type": "roadm",
- "online": false,
+ "online": true,
"labels": [
"0000ffffffffff01",
"FF:FF:FF:FF:FF:01",
diff --git a/web/gui/src/main/webapp/json/ev/startup/ev_14_onos.json b/web/gui/src/main/webapp/json/ev/startup/ev_14_onos.json
index e196148..93127a8 100644
--- a/web/gui/src/main/webapp/json/ev/startup/ev_14_onos.json
+++ b/web/gui/src/main/webapp/json/ev/startup/ev_14_onos.json
@@ -3,7 +3,7 @@
"payload": {
"id": "of:0000ffffffff0004",
"type": "switch",
- "online": false,
+ "online": true,
"labels": [
"0000ffffffff0004",
"FF:FF:FF:FF:00:04",
diff --git a/web/gui/src/main/webapp/json/ev/startup/ev_15_onos.json b/web/gui/src/main/webapp/json/ev/startup/ev_15_onos.json
index 30ba9f3..f2d6891 100644
--- a/web/gui/src/main/webapp/json/ev/startup/ev_15_onos.json
+++ b/web/gui/src/main/webapp/json/ev/startup/ev_15_onos.json
@@ -3,15 +3,15 @@
"payload": {
"id": "of:0000ffffffffff0A",
"type": "roadm",
- "online": false,
+ "online": true,
"labels": [
"0000ffffffffff0A",
"FF:FF:FF:FF:FF:0A",
"?"
],
"metaUi": {
- "Zx": 840,
- "Zy": 290
+ "x": 840,
+ "y": 290
}
}
}
diff --git a/web/gui/src/main/webapp/json/ev/startup/ev_16_onos.json b/web/gui/src/main/webapp/json/ev/startup/ev_16_onos.json
index 274adc1..3b0db4b 100644
--- a/web/gui/src/main/webapp/json/ev/startup/ev_16_onos.json
+++ b/web/gui/src/main/webapp/json/ev/startup/ev_16_onos.json
@@ -3,7 +3,7 @@
"payload": {
"id": "of:0000ffffffffff09",
"type": "roadm",
- "online": false,
+ "online": true,
"labels": [
"0000ffffffffff09",
"FF:FF:FF:FF:FF:09",
diff --git a/web/gui/src/main/webapp/json/ev/startup/ev_17_onos.json b/web/gui/src/main/webapp/json/ev/startup/ev_17_onos.json
index 82272a4..69c62b9 100644
--- a/web/gui/src/main/webapp/json/ev/startup/ev_17_onos.json
+++ b/web/gui/src/main/webapp/json/ev/startup/ev_17_onos.json
@@ -1,12 +1,13 @@
{
"event": "addLink",
"payload": {
+ "id": "of:0000ffffffffff02/20-of:0000ffffffffff05/10",
+ "type": "optical",
+ "linkWidth": 4,
"src": "of:0000ffffffffff02",
"srcPort": "20",
"dst": "of:0000ffffffffff05",
"dstPort": "10",
- "type": "optical",
- "linkWidth": 6,
"props" : {
"BW": "80 G"
}
diff --git a/web/gui/src/main/webapp/json/ev/startup/ev_18_onos.json b/web/gui/src/main/webapp/json/ev/startup/ev_18_onos.json
index 5687698..d11f13e 100644
--- a/web/gui/src/main/webapp/json/ev/startup/ev_18_onos.json
+++ b/web/gui/src/main/webapp/json/ev/startup/ev_18_onos.json
@@ -1,12 +1,13 @@
{
"event": "addLink",
"payload": {
+ "id": "of:0000ffffffff000A/2-of:0000ffffffffff0A/1",
+ "type": "optical",
+ "linkWidth": 2,
"src": "of:0000ffffffff000A",
"srcPort": "2",
"dst": "of:0000ffffffffff0A",
"dstPort": "1",
- "type": "optical",
- "linkWidth": 2,
"props" : {
"BW": "100 G"
}
diff --git a/web/gui/src/main/webapp/json/ev/startup/ev_19_onos.json b/web/gui/src/main/webapp/json/ev/startup/ev_19_onos.json
index 24aeb2d..1349a3b 100644
--- a/web/gui/src/main/webapp/json/ev/startup/ev_19_onos.json
+++ b/web/gui/src/main/webapp/json/ev/startup/ev_19_onos.json
@@ -1,12 +1,13 @@
{
"event": "addLink",
"payload": {
+ "id": "of:0000ffffffffff03/10-of:0000ffffffffff02/10",
+ "type": "optical",
+ "linkWidth": 2,
"src": "of:0000ffffffffff03",
"srcPort": "10",
"dst": "of:0000ffffffffff02",
"dstPort": "10",
- "type": "optical",
- "linkWidth": 2,
"props" : {
"BW": "70 G"
}
diff --git a/web/gui/src/main/webapp/json/ev/startup/ev_1_onos.json b/web/gui/src/main/webapp/json/ev/startup/ev_1_onos.json
index d4c8ddb..00a3e17 100644
--- a/web/gui/src/main/webapp/json/ev/startup/ev_1_onos.json
+++ b/web/gui/src/main/webapp/json/ev/startup/ev_1_onos.json
@@ -3,7 +3,7 @@
"payload": {
"id": "of:0000ffffffffff08",
"type": "roadm",
- "online": false,
+ "online": true,
"labels": [
"0000ffffffffff08",
"FF:FF:FF:FF:FF:08",
diff --git a/web/gui/src/main/webapp/json/ev/startup/ev_20_onos.json b/web/gui/src/main/webapp/json/ev/startup/ev_20_onos.json
index f42b50e..e4d2161 100644
--- a/web/gui/src/main/webapp/json/ev/startup/ev_20_onos.json
+++ b/web/gui/src/main/webapp/json/ev/startup/ev_20_onos.json
@@ -1,12 +1,13 @@
{
"event": "addLink",
"payload": {
+ "id": "of:0000ffffffffff07/21-of:0000ffffffffff05/20",
+ "type": "optical",
+ "linkWidth": 2,
"src": "of:0000ffffffffff07",
"srcPort": "21",
"dst": "of:0000ffffffffff05",
"dstPort": "20",
- "type": "optical",
- "linkWidth": 2,
"props" : {
"BW": "70 G"
}
diff --git a/web/gui/src/main/webapp/json/ev/startup/ev_21_onos.json b/web/gui/src/main/webapp/json/ev/startup/ev_21_onos.json
index 5af0ac7..ccdad88 100644
--- a/web/gui/src/main/webapp/json/ev/startup/ev_21_onos.json
+++ b/web/gui/src/main/webapp/json/ev/startup/ev_21_onos.json
@@ -1,12 +1,13 @@
{
"event": "addLink",
"payload": {
+ "id": "of:0000ffffffff0001/2-of:0000ffffffffff01/1",
+ "type": "optical",
+ "linkWidth": 2,
"src": "of:0000ffffffff0001",
"srcPort": "2",
"dst": "of:0000ffffffffff01",
"dstPort": "1",
- "type": "optical",
- "linkWidth": 2,
"props" : {
"BW": "70 G"
}
diff --git a/web/gui/src/main/webapp/json/ev/startup/ev_22_onos.json b/web/gui/src/main/webapp/json/ev/startup/ev_22_onos.json
index 0d4cf2b..52a4a02 100644
--- a/web/gui/src/main/webapp/json/ev/startup/ev_22_onos.json
+++ b/web/gui/src/main/webapp/json/ev/startup/ev_22_onos.json
@@ -1,12 +1,13 @@
{
"event": "addLink",
"payload": {
+ "id": "of:0000ffffffffff09/20-of:0000ffffffffff0A/20",
+ "type": "optical",
+ "linkWidth": 2,
"src": "of:0000ffffffffff09",
"srcPort": "20",
"dst": "of:0000ffffffffff0A",
"dstPort": "20",
- "type": "optical",
- "linkWidth": 2,
"props" : {
"BW": "70 G"
}
diff --git a/web/gui/src/main/webapp/json/ev/startup/ev_23_onos.json b/web/gui/src/main/webapp/json/ev/startup/ev_23_onos.json
index fff0f2b..be778551 100644
--- a/web/gui/src/main/webapp/json/ev/startup/ev_23_onos.json
+++ b/web/gui/src/main/webapp/json/ev/startup/ev_23_onos.json
@@ -1,12 +1,13 @@
{
"event": "addLink",
"payload": {
- "src": "of:0000ffffffffff06",
- "srcPort": "20",
- "dst": "of:0000ffffffffff05",
- "dstPort": "30",
+ "id": "of:0000ffffffffff07/30-of:0000ffffffffff08/20",
"type": "optical",
- "linkWidth": 6,
+ "linkWidth": 4,
+ "src": "of:0000ffffffffff07",
+ "srcPort": "30",
+ "dst": "of:0000ffffffffff08",
+ "dstPort": "20",
"props" : {
"BW": "70 G"
}
diff --git a/web/gui/src/main/webapp/json/ev/startup/ev_24_onos.json b/web/gui/src/main/webapp/json/ev/startup/ev_24_onos.json
index 756b6c1..6ae5192 100644
--- a/web/gui/src/main/webapp/json/ev/startup/ev_24_onos.json
+++ b/web/gui/src/main/webapp/json/ev/startup/ev_24_onos.json
@@ -1,12 +1,13 @@
{
"event": "addLink",
"payload": {
- "src": "of:0000ffffffffff07",
- "srcPort": "30",
- "dst": "of:0000ffffffffff08",
- "dstPort": "20",
+ "id": "of:0000ffffffffff02/10-of:0000ffffffffff01/10",
"type": "optical",
- "linkWidth": 6,
+ "linkWidth": 2,
+ "src": "of:0000ffffffffff02",
+ "srcPort": "10",
+ "dst": "of:0000ffffffffff01",
+ "dstPort": "10",
"props" : {
"BW": "70 G"
}
diff --git a/web/gui/src/main/webapp/json/ev/startup/ev_25_onos.json b/web/gui/src/main/webapp/json/ev/startup/ev_25_onos.json
index adad8a6..7fabce6 100644
--- a/web/gui/src/main/webapp/json/ev/startup/ev_25_onos.json
+++ b/web/gui/src/main/webapp/json/ev/startup/ev_25_onos.json
@@ -1,14 +1,15 @@
{
"event": "addLink",
"payload": {
- "src": "of:0000ffffffffff03",
- "srcPort": "20",
- "dst": "of:0000ffffffffff06",
+ "id": "of:0000ffffffffff04/27-of:0000ffffffffff08/10",
+ "src": "of:0000ffffffffff04",
+ "srcPort": "27",
+ "dst": "of:0000ffffffffff08",
"dstPort": "10",
"type": "optical",
"linkWidth": 2,
"props" : {
- "BW": "70 G"
+ "BW": "30 G"
}
}
}
diff --git a/web/gui/src/main/webapp/json/ev/startup/ev_26_onos.json b/web/gui/src/main/webapp/json/ev/startup/ev_26_onos.json
index 245c823..b89a287 100644
--- a/web/gui/src/main/webapp/json/ev/startup/ev_26_onos.json
+++ b/web/gui/src/main/webapp/json/ev/startup/ev_26_onos.json
@@ -1,12 +1,13 @@
{
"event": "addLink",
"payload": {
- "src": "of:0000ffffffffff02",
- "srcPort": "10",
- "dst": "of:0000ffffffffff01",
- "dstPort": "10",
+ "id": "of:0000ffffffff0003/2-of:0000ffffffffff03/1",
"type": "optical",
"linkWidth": 2,
+ "src": "of:0000ffffffff0003",
+ "srcPort": "2",
+ "dst": "of:0000ffffffffff03",
+ "dstPort": "1",
"props" : {
"BW": "70 G"
}
diff --git a/web/gui/src/main/webapp/json/ev/startup/ev_27_onos.json b/web/gui/src/main/webapp/json/ev/startup/ev_27_onos.json
index b856573..112a33e 100644
--- a/web/gui/src/main/webapp/json/ev/startup/ev_27_onos.json
+++ b/web/gui/src/main/webapp/json/ev/startup/ev_27_onos.json
@@ -1,12 +1,13 @@
{
"event": "addLink",
"payload": {
+ "id": "of:0000ffffffffff09/1-of:0000ffffffff0009/2",
+ "type": "optical",
+ "linkWidth": 2,
"src": "of:0000ffffffffff09",
"srcPort": "1",
"dst": "of:0000ffffffff0009",
"dstPort": "2",
- "type": "optical",
- "linkWidth": 2,
"props" : {
"BW": "70 G"
}
diff --git a/web/gui/src/main/webapp/json/ev/startup/ev_28_onos.json b/web/gui/src/main/webapp/json/ev/startup/ev_28_onos.json
index 232dc3b..52207df 100644
--- a/web/gui/src/main/webapp/json/ev/startup/ev_28_onos.json
+++ b/web/gui/src/main/webapp/json/ev/startup/ev_28_onos.json
@@ -1,12 +1,13 @@
{
"event": "addLink",
"payload": {
+ "id": "of:0000ffffffffff03/30-of:0000ffffffffff04/10",
+ "type": "optical",
+ "linkWidth": 2,
"src": "of:0000ffffffffff03",
"srcPort": "30",
"dst": "of:0000ffffffffff04",
"dstPort": "10",
- "type": "optical",
- "linkWidth": 2,
"props" : {
"BW": "70 G"
}
diff --git a/web/gui/src/main/webapp/json/ev/startup/ev_29_onos.json b/web/gui/src/main/webapp/json/ev/startup/ev_29_onos.json
index 1a845ce..c4660a3 100644
--- a/web/gui/src/main/webapp/json/ev/startup/ev_29_onos.json
+++ b/web/gui/src/main/webapp/json/ev/startup/ev_29_onos.json
@@ -1,12 +1,13 @@
{
"event": "addLink",
"payload": {
+ "id": "of:0000ffffffffff07/20-of:0000ffffffffff09/10",
+ "type": "optical",
+ "linkWidth": 2,
"src": "of:0000ffffffffff07",
"srcPort": "20",
"dst": "of:0000ffffffffff09",
"dstPort": "10",
- "type": "optical",
- "linkWidth": 2,
"props" : {
"BW": "70 G"
}
diff --git a/web/gui/src/main/webapp/json/ev/startup/ev_2_onos.json b/web/gui/src/main/webapp/json/ev/startup/ev_2_onos.json
index fd446ba..8d4fbfa 100644
--- a/web/gui/src/main/webapp/json/ev/startup/ev_2_onos.json
+++ b/web/gui/src/main/webapp/json/ev/startup/ev_2_onos.json
@@ -3,7 +3,7 @@
"payload": {
"id": "of:0000ffffffffff03",
"type": "roadm",
- "online": false,
+ "online": true,
"labels": [
"0000ffffffffff03",
"FF:FF:FF:FF:FF:03",
diff --git a/web/gui/src/main/webapp/json/ev/startup/ev_30_onos.json b/web/gui/src/main/webapp/json/ev/startup/ev_30_onos.json
index a617f45..e23cc2a 100644
--- a/web/gui/src/main/webapp/json/ev/startup/ev_30_onos.json
+++ b/web/gui/src/main/webapp/json/ev/startup/ev_30_onos.json
@@ -1,12 +1,13 @@
{
"event": "addLink",
"payload": {
+ "id": "of:0000ffffffffff0A/10-of:0000ffffffffff08/30",
+ "type": "optical",
+ "linkWidth": 4,
"src": "of:0000ffffffffff0A",
"srcPort": "10",
"dst": "of:0000ffffffffff08",
"dstPort": "30",
- "type": "optical",
- "linkWidth": 6,
"props" : {
"BW": "70 G"
}
diff --git a/web/gui/src/main/webapp/json/ev/startup/ev_31_onos.json b/web/gui/src/main/webapp/json/ev/startup/ev_31_onos.json
index 438aa1b..a798e53 100644
--- a/web/gui/src/main/webapp/json/ev/startup/ev_31_onos.json
+++ b/web/gui/src/main/webapp/json/ev/startup/ev_31_onos.json
@@ -1,12 +1,13 @@
{
"event": "addLink",
"payload": {
+ "id": "of:0000ffffffff0004/2-of:0000ffffffffff04/1",
+ "type": "optical",
+ "linkWidth": 2,
"src": "of:0000ffffffff0004",
"srcPort": "2",
"dst": "of:0000ffffffffff04",
"dstPort": "1",
- "type": "optical",
- "linkWidth": 2,
"props" : {
"BW": "70 G"
}
diff --git a/web/gui/src/main/webapp/json/ev/startup/ev_32_onos.json b/web/gui/src/main/webapp/json/ev/startup/ev_32_onos.json
index c479f01..aac204f 100644
--- a/web/gui/src/main/webapp/json/ev/startup/ev_32_onos.json
+++ b/web/gui/src/main/webapp/json/ev/startup/ev_32_onos.json
@@ -1,12 +1,13 @@
{
"event": "addLink",
"payload": {
+ "id": "of:0000ffffffffff07/1-of:0000ffffffff0007/2",
+ "type": "optical",
+ "linkWidth": 2,
"src": "of:0000ffffffffff07",
"srcPort": "1",
"dst": "of:0000ffffffff0007",
"dstPort": "2",
- "type": "optical",
- "linkWidth": 2,
"props" : {
"BW": "70 G"
}
diff --git a/web/gui/src/main/webapp/json/ev/startup/ev_33_onos.json b/web/gui/src/main/webapp/json/ev/startup/ev_33_onos.json
index 2cc3a32..ab23e93 100644
--- a/web/gui/src/main/webapp/json/ev/startup/ev_33_onos.json
+++ b/web/gui/src/main/webapp/json/ev/startup/ev_33_onos.json
@@ -1,14 +1,17 @@
{
- "event": "addLink",
+ "event": "updateDevice",
"payload": {
- "src": "of:0000ffffffff0003",
- "srcPort": "2",
- "dst": "of:0000ffffffffff03",
- "dstPort": "1",
- "type": "optical",
- "linkWidth": 2,
- "props" : {
- "BW": "70 G"
+ "id": "of:0000ffffffffff06",
+ "type": "roadm",
+ "online": true,
+ "labels": [
+ "0000ffffffffff06",
+ "FF:FF:FF:FF:FF:06",
+ "?"
+ ],
+ "metaUi": {
+ "x": 336,
+ "y": 254
}
}
}
diff --git a/web/gui/src/main/webapp/json/ev/startup/ev_34_onos.json b/web/gui/src/main/webapp/json/ev/startup/ev_34_onos.json
index fa5e3bc..09be8e6 100644
--- a/web/gui/src/main/webapp/json/ev/startup/ev_34_onos.json
+++ b/web/gui/src/main/webapp/json/ev/startup/ev_34_onos.json
@@ -1,12 +1,13 @@
{
"event": "addLink",
"payload": {
+ "id": "of:0000ffffffffff06/20-of:0000ffffffffff05/30",
"src": "of:0000ffffffffff06",
- "srcPort": "30",
- "dst": "of:0000ffffffffff08",
- "dstPort": "10",
+ "srcPort": "20",
+ "dst": "of:0000ffffffffff05",
+ "dstPort": "30",
"type": "optical",
- "linkWidth": 6,
+ "linkWidth": 4,
"props" : {
"BW": "70 G"
}
diff --git a/web/gui/src/main/webapp/json/ev/startup/ev_35_onos.json b/web/gui/src/main/webapp/json/ev/startup/ev_35_onos.json
index c579e59..4b612e9 100644
--- a/web/gui/src/main/webapp/json/ev/startup/ev_35_onos.json
+++ b/web/gui/src/main/webapp/json/ev/startup/ev_35_onos.json
@@ -1,14 +1,15 @@
{
"event": "addLink",
"payload": {
- "src": "of:0000ffffffffff04",
- "srcPort": "27",
- "dst": "of:0000ffffffffff08",
- "dstPort": "10",
+ "id": "of:0000ffffffffff03/20-of:0000ffffffffff06/10",
"type": "optical",
"linkWidth": 2,
+ "src": "of:0000ffffffffff03",
+ "srcPort": "20",
+ "dst": "of:0000ffffffffff06",
+ "dstPort": "10",
"props" : {
- "BW": "30 G"
+ "BW": "70 G"
}
}
}
diff --git a/web/gui/src/main/webapp/json/ev/startup/ev_36_onos.json b/web/gui/src/main/webapp/json/ev/startup/ev_36_onos.json
new file mode 100644
index 0000000..cddb929
--- /dev/null
+++ b/web/gui/src/main/webapp/json/ev/startup/ev_36_onos.json
@@ -0,0 +1,15 @@
+{
+ "event": "addLink",
+ "payload": {
+ "id": "of:0000ffffffffff06/30-of:0000ffffffffff08/10",
+ "type": "optical",
+ "linkWidth": 4,
+ "src": "of:0000ffffffffff06",
+ "srcPort": "30",
+ "dst": "of:0000ffffffffff08",
+ "dstPort": "10",
+ "props" : {
+ "BW": "70 G"
+ }
+ }
+}
diff --git a/web/gui/src/main/webapp/json/ev/startup/ev_37_onos.json b/web/gui/src/main/webapp/json/ev/startup/ev_37_onos.json
new file mode 100644
index 0000000..6f608cd
--- /dev/null
+++ b/web/gui/src/main/webapp/json/ev/startup/ev_37_onos.json
@@ -0,0 +1,17 @@
+{
+ "event": "updateDevice",
+ "payload": {
+ "id": "of:0000ffffffffff08",
+ "type": "roadm",
+ "online": false,
+ "labels": [
+ "0000ffffffffff08",
+ "FF:FF:FF:FF:FF:08",
+ "?"
+ ],
+ "metaUi": {
+ "x": 539,
+ "y": 186
+ }
+ }
+}
diff --git a/web/gui/src/main/webapp/json/ev/startup/ev_38_onos.json b/web/gui/src/main/webapp/json/ev/startup/ev_38_onos.json
new file mode 100644
index 0000000..09a0339
--- /dev/null
+++ b/web/gui/src/main/webapp/json/ev/startup/ev_38_onos.json
@@ -0,0 +1,15 @@
+{
+ "event": "removeLink",
+ "payload": {
+ "id": "of:0000ffffffffff07/30-of:0000ffffffffff08/20",
+ "type": "optical",
+ "linkWidth": 4,
+ "src": "of:0000ffffffffff07",
+ "srcPort": "30",
+ "dst": "of:0000ffffffffff08",
+ "dstPort": "20",
+ "props" : {
+ "BW": "70 G"
+ }
+ }
+}
diff --git a/web/gui/src/main/webapp/json/ev/startup/ev_39_onos.json b/web/gui/src/main/webapp/json/ev/startup/ev_39_onos.json
new file mode 100644
index 0000000..a85cee6
--- /dev/null
+++ b/web/gui/src/main/webapp/json/ev/startup/ev_39_onos.json
@@ -0,0 +1,15 @@
+{
+ "event": "removeLink",
+ "payload": {
+ "id": "of:0000ffffffffff04/27-of:0000ffffffffff08/10",
+ "src": "of:0000ffffffffff04",
+ "srcPort": "27",
+ "dst": "of:0000ffffffffff08",
+ "dstPort": "10",
+ "type": "optical",
+ "linkWidth": 2,
+ "props" : {
+ "BW": "30 G"
+ }
+ }
+}
diff --git a/web/gui/src/main/webapp/json/ev/startup/ev_3_onos.json b/web/gui/src/main/webapp/json/ev/startup/ev_3_onos.json
index 23bf26a..b4c3537 100644
--- a/web/gui/src/main/webapp/json/ev/startup/ev_3_onos.json
+++ b/web/gui/src/main/webapp/json/ev/startup/ev_3_onos.json
@@ -3,7 +3,7 @@
"payload": {
"id": "of:0000ffffffff0007",
"type": "switch",
- "online": false,
+ "online": true,
"labels": [
"0000ffffffff0007",
"FF:FF:FF:FF:00:07",
diff --git a/web/gui/src/main/webapp/json/ev/startup/ev_40_onos.json b/web/gui/src/main/webapp/json/ev/startup/ev_40_onos.json
new file mode 100644
index 0000000..1b95d24
--- /dev/null
+++ b/web/gui/src/main/webapp/json/ev/startup/ev_40_onos.json
@@ -0,0 +1,15 @@
+{
+ "event": "removeLink",
+ "payload": {
+ "id": "of:0000ffffffffff0A/10-of:0000ffffffffff08/30",
+ "type": "optical",
+ "linkWidth": 4,
+ "src": "of:0000ffffffffff0A",
+ "srcPort": "10",
+ "dst": "of:0000ffffffffff08",
+ "dstPort": "30",
+ "props" : {
+ "BW": "70 G"
+ }
+ }
+}
diff --git a/web/gui/src/main/webapp/json/ev/startup/ev_41_onos.json b/web/gui/src/main/webapp/json/ev/startup/ev_41_onos.json
new file mode 100644
index 0000000..1efc1f6
--- /dev/null
+++ b/web/gui/src/main/webapp/json/ev/startup/ev_41_onos.json
@@ -0,0 +1,15 @@
+{
+ "event": "removeLink",
+ "payload": {
+ "id": "of:0000ffffffffff06/30-of:0000ffffffffff08/10",
+ "type": "optical",
+ "linkWidth": 4,
+ "src": "of:0000ffffffffff06",
+ "srcPort": "30",
+ "dst": "of:0000ffffffffff08",
+ "dstPort": "10",
+ "props" : {
+ "BW": "70 G"
+ }
+ }
+}
diff --git a/web/gui/src/main/webapp/json/ev/startup/ev_4_onos.json b/web/gui/src/main/webapp/json/ev/startup/ev_4_onos.json
index c600401..053b963 100644
--- a/web/gui/src/main/webapp/json/ev/startup/ev_4_onos.json
+++ b/web/gui/src/main/webapp/json/ev/startup/ev_4_onos.json
@@ -3,7 +3,7 @@
"payload": {
"id": "of:0000ffffffff0009",
"type": "switch",
- "online": false,
+ "online": true,
"labels": [
"0000ffffffff0009",
"FF:FF:FF:FF:00:09",
diff --git a/web/gui/src/main/webapp/json/ev/startup/ev_5_onos.json b/web/gui/src/main/webapp/json/ev/startup/ev_5_onos.json
index af912a7..332bfdb 100644
--- a/web/gui/src/main/webapp/json/ev/startup/ev_5_onos.json
+++ b/web/gui/src/main/webapp/json/ev/startup/ev_5_onos.json
@@ -3,7 +3,7 @@
"payload": {
"id": "of:0000ffffffffff02",
"type": "roadm",
- "online": false,
+ "online": true,
"labels": [
"0000ffffffffff02",
"FF:FF:FF:FF:FF:02",
diff --git a/web/gui/src/main/webapp/json/ev/startup/ev_6_onos.json b/web/gui/src/main/webapp/json/ev/startup/ev_6_onos.json
index 50273bc..c764bc1 100644
--- a/web/gui/src/main/webapp/json/ev/startup/ev_6_onos.json
+++ b/web/gui/src/main/webapp/json/ev/startup/ev_6_onos.json
@@ -3,7 +3,7 @@
"payload": {
"id": "of:0000ffffffff0003",
"type": "switch",
- "online": false,
+ "online": true,
"labels": [
"0000ffffffff0003",
"FF:FF:FF:FF:00:03",
diff --git a/web/gui/src/main/webapp/json/ev/startup/ev_7_onos.json b/web/gui/src/main/webapp/json/ev/startup/ev_7_onos.json
index 7cd29b0..25a6dce 100644
--- a/web/gui/src/main/webapp/json/ev/startup/ev_7_onos.json
+++ b/web/gui/src/main/webapp/json/ev/startup/ev_7_onos.json
@@ -3,7 +3,7 @@
"payload": {
"id": "of:0000ffffffffff07",
"type": "roadm",
- "online": false,
+ "online": true,
"labels": [
"0000ffffffffff07",
"FF:FF:FF:FF:FF:07",
diff --git a/web/gui/src/main/webapp/json/ev/startup/ev_9_onos.json b/web/gui/src/main/webapp/json/ev/startup/ev_9_onos.json
index 1e0f427..0f497c0 100644
--- a/web/gui/src/main/webapp/json/ev/startup/ev_9_onos.json
+++ b/web/gui/src/main/webapp/json/ev/startup/ev_9_onos.json
@@ -3,7 +3,7 @@
"payload": {
"id": "of:0000ffffffffff05",
"type": "roadm",
- "online": false,
+ "online": true,
"labels": [
"0000ffffffffff05",
"FF:FF:FF:FF:FF:05",
diff --git a/web/gui/src/main/webapp/json/ev/startup/scenario.json b/web/gui/src/main/webapp/json/ev/startup/scenario.json
index 37939ca0..089d53a 100644
--- a/web/gui/src/main/webapp/json/ev/startup/scenario.json
+++ b/web/gui/src/main/webapp/json/ev/startup/scenario.json
@@ -6,5 +6,15 @@
"title": "Startup Scenario",
"params": {
"lastAuto": 32
- }
+ },
+ "description": [
+ "Loads 16 devices (10 optical, 6 packet)",
+ " and their associated links.",
+ "",
+ "Press 'S' to load initial events.",
+ "",
+ "Press spacebar to complete the scenario...",
+ " * 4 events - device online, add 3 links",
+ " * 5 events - device offline, remove 4 links"
+ ]
}
\ No newline at end of file
diff --git a/web/gui/src/main/webapp/json/of_0000000000000001.json b/web/gui/src/main/webapp/json/of_0000000000000001.json
index 719af80..1f5c8e9 100644
--- a/web/gui/src/main/webapp/json/of_0000000000000001.json
+++ b/web/gui/src/main/webapp/json/of_0000000000000001.json
@@ -3,6 +3,11 @@
"id": "of:0000000000000001",
"type": "roadm",
"propOrder": [ "name", "type", "-", "dpid", "latitude", "longitude", "allowed" ],
+ "location": {
+ "type": "latlng",
+ "lat": 37.6,
+ "lng": 122.3
+ },
"props": {
"allowed": true,
"latitude": 37.6,
diff --git a/web/gui/src/main/webapp/json/of_0000000000000002.json b/web/gui/src/main/webapp/json/of_0000000000000002.json
index a6b0e7c..87fd1f2 100644
--- a/web/gui/src/main/webapp/json/of_0000000000000002.json
+++ b/web/gui/src/main/webapp/json/of_0000000000000002.json
@@ -3,6 +3,11 @@
"id": "of:0000000000000002",
"type": "switch",
"propOrder": [ "name", "type", "dpid", "latitude", "longitude", "allowed" ],
+ "location": {
+ "type": "latlng",
+ "lat": 37.6,
+ "lng": 122.3
+ },
"props": {
"allowed": true,
"latitude": 37.3,
diff --git a/web/gui/src/main/webapp/json/of_0000000000000003.json b/web/gui/src/main/webapp/json/of_0000000000000003.json
index 9fd2790..1315961 100644
--- a/web/gui/src/main/webapp/json/of_0000000000000003.json
+++ b/web/gui/src/main/webapp/json/of_0000000000000003.json
@@ -3,6 +3,11 @@
"id": "of:0000000000000003",
"type": "switch",
"propOrder": [ "name", "type", "dpid", "latitude", "longitude", "allowed" ],
+ "location": {
+ "type": "latlng",
+ "lat": 33.9,
+ "lng": 118.4
+ },
"props": {
"allowed": true,
"latitude": 33.9,
diff --git a/web/gui/src/main/webapp/json/of_0000000000000004.json b/web/gui/src/main/webapp/json/of_0000000000000004.json
index f3f2132..ba243baf 100644
--- a/web/gui/src/main/webapp/json/of_0000000000000004.json
+++ b/web/gui/src/main/webapp/json/of_0000000000000004.json
@@ -3,6 +3,11 @@
"id": "of:0000000000000004",
"type": "switch",
"propOrder": [ "name", "type", "dpid", "latitude", "longitude", "allowed" ],
+ "location": {
+ "type": "latlng",
+ "lat": 32.8,
+ "lng": 117.1
+ },
"props": {
"allowed": true,
"latitude": 32.8,
diff --git a/web/gui/src/main/webapp/onos2.js b/web/gui/src/main/webapp/onos2.js
index f38b35f..0644c32 100644
--- a/web/gui/src/main/webapp/onos2.js
+++ b/web/gui/src/main/webapp/onos2.js
@@ -50,6 +50,7 @@
// internal state
var views = {},
+ fpanels = {},
current = {
view: null,
ctx: '',
@@ -57,7 +58,7 @@
theme: settings.theme
},
built = false,
- errorCount = 0,
+ buildErrors = [],
keyHandler = {
globalKeys: {},
maskedKeys: {},
@@ -70,7 +71,11 @@
};
// DOM elements etc.
- var $view,
+ // TODO: verify existence of following elements...
+ var $view = d3.select('#view'),
+ $floatPanels = d3.select('#floatPanels'),
+ $alerts = d3.select('#alerts'),
+ // note, following elements added programmatically...
$mastRadio;
@@ -241,10 +246,22 @@
setView(view, hash, t);
}
+ function buildError(msg) {
+ buildErrors.push(msg);
+ }
+
function reportBuildErrors() {
traceFn('reportBuildErrors');
- // TODO: validate registered views / nav-item linkage etc.
- console.log('(no build errors)');
+ var nerr = buildErrors.length,
+ errmsg;
+ if (!nerr) {
+ console.log('(no build errors)');
+ } else {
+ errmsg = 'Build errors: ' + nerr + ' found...\n\n' +
+ buildErrors.join('\n');
+ doAlert(errmsg);
+ console.error(errmsg);
+ }
}
// returns the reference if it is a function, null otherwise
@@ -449,22 +466,20 @@
}
function createAlerts() {
- var al = d3.select('#alerts')
- .style('display', 'block');
- al.append('span')
+ $alerts.style('display', 'block');
+ $alerts.append('span')
.attr('class', 'close')
.text('X')
.on('click', closeAlerts);
- al.append('pre');
- al.append('p').attr('class', 'footnote')
+ $alerts.append('pre');
+ $alerts.append('p').attr('class', 'footnote')
.text('Press ESCAPE to close');
alerts.open = true;
alerts.count = 0;
}
function closeAlerts() {
- d3.select('#alerts')
- .style('display', 'none')
+ $alerts.style('display', 'none')
.html('');
alerts.open = false;
}
@@ -474,7 +489,7 @@
oldContent;
if (alerts.count) {
- oldContent = d3.select('#alerts pre').html();
+ oldContent = $alerts.select('pre').html();
}
lines = msg.split('\n');
@@ -485,7 +500,7 @@
lines += '\n----\n' + oldContent;
}
- d3.select('#alerts pre').html(lines);
+ $alerts.select('pre').html(lines);
alerts.count++;
}
@@ -691,6 +706,53 @@
libApi[libName] = api;
},
+ // TODO: implement floating panel as a class
+ // TODO: parameterize position (currently hard-coded to TopRight)
+ /*
+ * Creates div in floating panels block, with the given id.
+ * Returns panel token used to interact with the panel
+ */
+ addFloatingPanel: function (id, position) {
+ var pos = position || 'TR',
+ el,
+ fp;
+
+ if (fpanels[id]) {
+ buildError('Float panel with id "' + id + '" already exists.');
+ return null;
+ }
+
+ el = $floatPanels.append('div')
+ .attr('id', id)
+ .attr('class', 'fpanel');
+
+ fp = {
+ id: id,
+ el: el,
+ pos: pos,
+ show: function () {
+ console.log('show pane: ' + id);
+ el.transition().duration(750)
+ .style('right', '20px')
+ .style('opacity', 1);
+ },
+ hide: function () {
+ console.log('hide pane: ' + id);
+ el.transition().duration(750)
+ .style('right', '-320px')
+ .style('opacity', 0);
+ },
+ empty: function () {
+ return el.html('');
+ },
+ append: function (what) {
+ return el.append(what);
+ }
+ };
+ fpanels[id] = fp;
+ return fp;
+ },
+
// TODO: it remains to be seen whether we keep this style of docs
/** @api ui addView( vid, nid, cb )
* Adds a view to the UI.
@@ -782,7 +844,6 @@
}
built = true;
- $view = d3.select('#view');
$mastRadio = d3.select('#mastRadio');
$(window).on('hashchange', hash);
diff --git a/web/gui/src/main/webapp/topo2.css b/web/gui/src/main/webapp/topo2.css
index 6c0c313..aeaad2d 100644
--- a/web/gui/src/main/webapp/topo2.css
+++ b/web/gui/src/main/webapp/topo2.css
@@ -96,3 +96,45 @@
fill: white;
stroke: red;
}
+
+
+/* detail topo-detail pane */
+
+#topo-detail {
+/* gets base CSS from .fpanel in floatPanel.css */
+}
+
+
+#topo-detail h2 {
+ margin: 8px 4px;
+ color: black;
+ vertical-align: middle;
+}
+
+#topo-detail h2 img {
+ height: 32px;
+ padding-right: 8px;
+ vertical-align: middle;
+}
+
+#topo-detail p, table {
+ margin: 4px 4px;
+}
+
+#topo-detail td.label {
+ font-style: italic;
+ color: #777;
+ padding-right: 12px;
+}
+
+#topo-detail td.value {
+
+}
+
+#topo-detail hr {
+ height: 1px;
+ color: #ccc;
+ background-color: #ccc;
+ border: 0;
+}
+
diff --git a/web/gui/src/main/webapp/topo2.js b/web/gui/src/main/webapp/topo2.js
index 94b2e9e..a23f48d 100644
--- a/web/gui/src/main/webapp/topo2.js
+++ b/web/gui/src/main/webapp/topo2.js
@@ -30,6 +30,7 @@
// configuration data
var config = {
useLiveData: true,
+ fnTrace: true,
debugOn: false,
debug: {
showNodeXY: true,
@@ -151,7 +152,7 @@
webSock,
deviceLabelIndex = 0,
hostLabelIndex = 0,
-
+ detailPane,
selectOrder = [],
selections = {},
@@ -180,12 +181,21 @@
return config.debugOn && config.debug[what];
}
+ function fnTrace(msg, id) {
+ if (config.fnTrace) {
+ console.log('FN: ' + msg + ' [' + id + ']');
+ }
+ }
// ==============================
// Key Callbacks
function testMe(view) {
view.alert('test');
+ detailPane.show();
+ setTimeout(function () {
+ detailPane.hide();
+ }, 3000);
}
function abortIfLive() {
@@ -220,7 +230,7 @@
var v = scenario.view,
frame;
if (stack.length === 0) {
- v.alert('Error:\n\nNo event #' + evn + ' found.');
+ v.alert('Oops!\n\nNo event #' + evn + ' found.');
return;
}
frame = stack.shift();
@@ -279,14 +289,6 @@
view.alert('unpin() callback')
}
- function requestPath(view) {
- var payload = {
- one: selections[selectOrder[0]].obj.id,
- two: selections[selectOrder[1]].obj.id
- }
- sendMessage('requestPath', payload);
- }
-
// ==============================
// Radio Button Callbacks
@@ -334,6 +336,7 @@
function logicError(msg) {
// TODO, report logic error to server, via websock, so it can be logged
network.view.alert('Logic Error:\n\n' + msg);
+ console.warn(msg);
}
var eventDispatch = {
@@ -345,11 +348,13 @@
updateHost: updateHost,
removeDevice: stillToImplement,
removeLink: removeLink,
- removeHost: stillToImplement,
+ removeHost: removeHost,
+ showDetails: showDetails,
showPath: showPath
};
function addDevice(data) {
+ fnTrace('addDevice', data.payload.id);
var device = data.payload,
nodeData = createDeviceNode(device);
network.nodes.push(nodeData);
@@ -359,6 +364,7 @@
}
function addLink(data) {
+ fnTrace('addLink', data.payload.id);
var link = data.payload,
lnk = createLink(link);
if (lnk) {
@@ -370,6 +376,7 @@
}
function addHost(data) {
+ fnTrace('addHost', data.payload.id);
var host = data.payload,
node = createHostNode(host),
lnk;
@@ -379,6 +386,7 @@
lnk = createHostLink(host);
if (lnk) {
+ node.linkData = lnk; // cache ref on its host
network.links.push(lnk);
network.lookup[host.ingress] = lnk;
network.lookup[host.egress] = lnk;
@@ -387,7 +395,9 @@
network.force.start();
}
+ // TODO: fold updateX(...) methods into one base method; remove duplication
function updateDevice(data) {
+ fnTrace('updateDevice', data.payload.id);
var device = data.payload,
id = device.id,
nodeData = network.lookup[id];
@@ -400,6 +410,7 @@
}
function updateLink(data) {
+ fnTrace('updateLink', data.payload.id);
var link = data.payload,
id = link.id,
linkData = network.lookup[id];
@@ -412,6 +423,7 @@
}
function updateHost(data) {
+ fnTrace('updateHost', data.payload.id);
var host = data.payload,
id = host.id,
hostData = network.lookup[id];
@@ -423,7 +435,9 @@
}
}
+ // TODO: fold removeX(...) methods into base method - remove dup code
function removeLink(data) {
+ fnTrace('removeLink', data.payload.id);
var link = data.payload,
id = link.id,
linkData = network.lookup[id];
@@ -434,7 +448,26 @@
}
}
+ function removeHost(data) {
+ fnTrace('removeHost', data.payload.id);
+ var host = data.payload,
+ id = host.id,
+ hostData = network.lookup[id];
+ if (hostData) {
+ removeHostElement(hostData);
+ } else {
+ logicError('removeHost lookup fail. ID = "' + id + '"');
+ }
+ }
+
+ function showDetails(data) {
+ fnTrace('showDetails', data.payload.id);
+ populateDetails(data.payload);
+ detailPane.show();
+ }
+
function showPath(data) {
+ fnTrace('showPath', data.payload.id);
var links = data.payload.links,
s = [ data.event + "\n" + links.length ];
links.forEach(function (d, i) {
@@ -470,6 +503,32 @@
}
// ==============================
+ // Out-going messages...
+
+ function getSel(idx) {
+ return selections[selectOrder[idx]];
+ }
+
+ // for now, just a host-to-host intent, (and implicit start-monitoring)
+ function requestPath() {
+ var payload = {
+ one: getSel(0).obj.id,
+ two: getSel(1).obj.id
+ };
+ sendMessage('requestPath', payload);
+ }
+
+ // request details for the selected element
+ function requestDetails() {
+ var data = getSel(0).obj,
+ payload = {
+ id: data.id,
+ class: data.class
+ };
+ sendMessage('requestDetails', payload);
+ }
+
+ // ==============================
// force layout modification functions
function translate(x, y) {
@@ -589,19 +648,18 @@
//link .foo() .bar() ...
// operate on exiting links:
- // TODO: better transition (longer as a dashed, grey line)
link.exit()
.attr({
'stroke-dasharray': '3, 3'
})
.style('opacity', 0.4)
.transition()
- .duration(2000)
+ .duration(1500)
.attr({
'stroke-dasharray': '3, 12'
})
.transition()
- .duration(1000)
+ .duration(500)
.style('opacity', 0.0)
.remove();
}
@@ -855,17 +913,37 @@
//node .foo() .bar() ...
// operate on exiting nodes:
- // TODO: figure out how to remove the node 'g' AND its children
- node.exit()
+ // Note that the node is removed after 2 seconds.
+ // Sub element animations should be shorter than 2 seconds.
+ var exiting = node.exit()
.transition()
- .duration(750)
- .attr({
- opacity: 0,
- cx: 0,
- cy: 0,
- r: 0
- })
+ .duration(2000)
+ .style('opacity', 0)
.remove();
+
+ // host node exits....
+ exiting.filter('.host').each(function (d) {
+ var node = d3.select(this);
+
+ node.select('text')
+ .style('opacity', 0.5)
+ .transition()
+ .duration(1000)
+ .style('opacity', 0);
+ // note, leave <g>.remove to remove this element
+
+ node.select('circle')
+ .style('stroke-fill', '#555')
+ .style('fill', '#888')
+ .style('opacity', 0.5)
+ .transition()
+ .duration(1500)
+ .attr('r', 0);
+ // note, leave <g>.remove to remove this element
+
+ });
+
+ // TODO: device node exits
}
function find(id, array) {
@@ -882,12 +960,27 @@
delete network.lookup[linkData.id];
// remove from links array
var idx = find(linkData.id, network.links);
-
- network.links.splice(linkData.index, 1);
+ network.links.splice(idx, 1);
// remove from SVG
updateLinks();
+ network.force.resume();
}
+ function removeHostElement(hostData) {
+ // first, remove associated hostLink...
+ removeLinkElement(hostData.linkData);
+
+ // remove from lookup cache
+ delete network.lookup[hostData.id];
+ // remove from nodes array
+ var idx = find(hostData.id, network.nodes);
+ network.nodes.splice(idx, 1);
+ // remove from SVG
+ updateNodes();
+ network.force.resume();
+ }
+
+
function tick() {
node.attr({
transform: function (d) { return translate(d.x, d.y); }
@@ -951,6 +1044,8 @@
var sid = 0;
+ // TODO: use cache of pending messages (key = sid) to reconcile responses
+
function sendMessage(evType, payload) {
var toSend = {
event: evType,
@@ -969,7 +1064,6 @@
wsTrace('rx', msg);
}
function wsTrace(rxtx, msg) {
-
console.log('[' + rxtx + '] ' + msg);
// TODO: integrate with trace view
//if (trace) {
@@ -998,7 +1092,7 @@
if (meta && n.classed('selected')) {
deselectObject(obj.id);
- //flyinPane(null);
+ updateDetailPane();
return;
}
@@ -1010,17 +1104,16 @@
selectOrder.push(obj.id);
n.classed('selected', true);
- //flyinPane(obj);
+ updateDetailPane();
}
function deselectObject(id) {
var obj = selections[id];
if (obj) {
d3.select(obj.el).classed('selected', false);
- selections[id] = null;
- // TODO: use splice to remove element
+ delete selections[id];
}
- //flyinPane(null);
+ updateDetailPane();
}
function deselectAll() {
@@ -1028,10 +1121,10 @@
node.classed('selected', false);
selections = {};
selectOrder = [];
- //flyinPane(null);
+ updateDetailPane();
}
- // TODO: this click handler does not get unloaded when the view does
+ // FIXME: this click handler does not get unloaded when the view does
$('#view').on('click', function(e) {
if (!$(e.target).closest('.node').length) {
if (!e.metaKey) {
@@ -1040,6 +1133,66 @@
}
});
+ // update the state of the detail pane, based on current selections
+ function updateDetailPane() {
+ var nSel = selectOrder.length;
+ if (!nSel) {
+ detailPane.hide();
+ } else if (nSel === 1) {
+ singleSelect();
+ } else {
+ multiSelect();
+ }
+ }
+
+ function singleSelect() {
+ requestDetails();
+ // NOTE: detail pane will be shown from showDetails event.
+ }
+
+ function multiSelect() {
+ // TODO: use detail pane for multi-select view.
+ //detailPane.show();
+ }
+
+ function populateDetails(data) {
+ detailPane.empty();
+
+ var title = detailPane.append("h2"),
+ table = detailPane.append("table"),
+ tbody = table.append("tbody");
+
+ $('<img src="img/' + data.type + '.png">').appendTo(title);
+ $('<span>').attr('class', 'icon').text(data.id).appendTo(title);
+
+ 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);
+ }
+ }
+
+ // ==============================
+ // Test harness code
function prepareScenario(view, ctx, dbg) {
var sc = scenario,
@@ -1058,13 +1211,12 @@
d3.json(urlSc, function(err, data) {
var p = data && data.params || {},
desc = data && data.description || null,
- intro;
+ intro = data && data.title;
if (err) {
view.alert('No scenario found:\n\n' + urlSc + '\n\n' + err);
} else {
sc.params = p;
- intro = "Scenario loaded: " + ctx + '\n\n' + data.title;
if (desc) {
intro += '\n\n ' + desc.join('\n ');
}
@@ -1140,16 +1292,18 @@
d.fixed = true;
d3.select(self).classed('fixed', true);
if (config.useLiveData) {
- tellServerCoords(d);
+ sendUpdateMeta(d);
}
}
- function tellServerCoords(d) {
+ function sendUpdateMeta(d) {
sendMessage('updateMeta', {
id: d.id,
'class': d.class,
- x: Math.floor(d.x),
- y: Math.floor(d.y)
+ 'memento': {
+ x: Math.floor(d.x),
+ y: Math.floor(d.y)
+ }
});
}
@@ -1207,4 +1361,6 @@
resize: resize
});
+ detailPane = onos.ui.addFloatingPanel('topo-detail');
+
}(ONOS));