Merge remote-tracking branch 'origin/master'
diff --git a/apps/optical/src/main/java/org/onlab/onos/optical/cfg/OpticalConfigProvider.java b/apps/optical/src/main/java/org/onlab/onos/optical/cfg/OpticalConfigProvider.java
index b2e273f..cfdeb1f 100644
--- a/apps/optical/src/main/java/org/onlab/onos/optical/cfg/OpticalConfigProvider.java
+++ b/apps/optical/src/main/java/org/onlab/onos/optical/cfg/OpticalConfigProvider.java
@@ -284,7 +284,7 @@
DefaultLinkDescription linkDescription =
new DefaultLinkDescription(srcPoint,
snkPoint,
- Link.Type.DIRECT,
+ Link.Type.OPTICAL,
extendedAttributes);
linkProviderService.linkDetected(linkDescription);
@@ -315,7 +315,7 @@
DefaultLinkDescription linkDescription =
new DefaultLinkDescription(srcPoint,
snkPoint,
- Link.Type.DIRECT,
+ Link.Type.OPTICAL,
extendedAttributes);
linkProviderService.linkDetected(linkDescription);
diff --git a/apps/optical/src/main/resources/demo-3-roadm-2-ps.json b/apps/optical/src/main/resources/demo-3-roadm-2-ps.json
index 6f2c2f5..20b7db2 100644
--- a/apps/optical/src/main/resources/demo-3-roadm-2-ps.json
+++ b/apps/optical/src/main/resources/demo-3-roadm-2-ps.json
@@ -1,5 +1,5 @@
{
- "opticalSwitches": [
+ "opticalSwitches": [
{
"allowed": true,
"latitude": 37.6,
@@ -12,7 +12,7 @@
"type": "Roadm"
},
- {
+ {
"allowed": true,
"latitude": 37.3,
"longitude": 121.9,
@@ -22,9 +22,9 @@
"numRegen": 0
},
"type": "Roadm"
- },
+ },
- {
+ {
"allowed": true,
"latitude": 33.9,
"longitude": 118.4,
@@ -34,10 +34,10 @@
"numRegen": 2
},
"type": "Roadm"
- }
+ }
],
- "opticalLinks": [
+ "opticalLinks": [
{
"allowed": true,
"nodeDpid1": "00:00:ff:ff:ff:ff:ff:01",
@@ -51,10 +51,38 @@
"port2": 30
},
"type": "wdmLink"
- },
-
- {
- "allowed": true,
+ },
+ {
+ "allowed": true,
+ "nodeDpid1": "00:00:ff:ff:ff:ff:ff:03",
+ "nodeDpid2": "00:00:ff:ff:ff:ff:ff:01",
+ "params": {
+ "distKms": 1000,
+ "nodeName1": "ROADM3",
+ "nodeName2": "ROADM1",
+ "numWaves": 80,
+ "port1": 30,
+ "port2": 10
+ },
+ "type": "wdmLink"
+ },
+
+ {
+ "allowed": true,
+ "nodeDpid1": "00:00:ff:ff:ff:ff:ff:02",
+ "nodeDpid2": "00:00:ff:ff:ff:ff:ff:03",
+ "params": {
+ "distKms": 2000,
+ "nodeName1": "ROADM2",
+ "nodeName2": "ROADM3",
+ "numWaves": 80,
+ "port1": 20,
+ "port2": 31
+ },
+ "type": "wdmLink"
+ },
+ {
+ "allowed": true,
"nodeDpid1": "00:00:ff:ff:ff:ff:ff:03",
"nodeDpid2": "00:00:ff:ff:ff:ff:ff:02",
"params": {
@@ -66,10 +94,9 @@
"port2": 20
},
"type": "wdmLink"
- },
+ },
-
- {
+ {
"allowed": true,
"nodeDpid1": "00:00:ff:ff:ff:ff:00:01",
"nodeDpid2": "00:00:ff:ff:ff:ff:ff:01",
@@ -82,8 +109,21 @@
},
"type": "pktOptLink"
},
+ {
+ "allowed": true,
+ "nodeDpid1": "00:00:ff:ff:ff:ff:ff:01",
+ "nodeDpid2": "00:00:ff:ff:ff:ff:00:01",
+ "params": {
+ "nodeName1": "ROADM1",
+ "nodeName2": "ROUTER1",
+ "bandWidth": 100000,
+ "port1": 11,
+ "port2": 10
+ },
+ "type": "pktOptLink"
+ },
- {
+ {
"allowed": true,
"nodeDpid1": "00:00:ff:ff:ff:ff:00:02",
"nodeDpid2": "00:00:ff:ff:ff:ff:ff:02",
@@ -95,7 +135,20 @@
"port2": 21
},
"type": "pktOptLink"
- }
+ },
+ {
+ "allowed": true,
+ "nodeDpid1": "00:00:ff:ff:ff:ff:ff:02",
+ "nodeDpid2": "00:00:ff:ff:ff:ff:00:02",
+ "params": {
+ "nodeName1": "ROADM2",
+ "nodeName2": "ROUTER2",
+ "bandWidth": 100000,
+ "port1": 21,
+ "port2": 10
+ },
+ "type": "pktOptLink"
+ }
]
}
diff --git a/cli/src/main/java/org/onlab/onos/cli/net/TopologyCommand.java b/cli/src/main/java/org/onlab/onos/cli/net/TopologyCommand.java
index ce3dc59..903b352 100644
--- a/cli/src/main/java/org/onlab/onos/cli/net/TopologyCommand.java
+++ b/cli/src/main/java/org/onlab/onos/cli/net/TopologyCommand.java
@@ -20,8 +20,10 @@
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.karaf.shell.commands.Command;
+import org.apache.karaf.shell.commands.Option;
import org.onlab.onos.cli.AbstractShellCommand;
import org.onlab.onos.net.topology.Topology;
+import org.onlab.onos.net.topology.TopologyProvider;
import org.onlab.onos.net.topology.TopologyService;
/**
@@ -35,6 +37,10 @@
private static final String FMT =
"time=%s, devices=%d, links=%d, clusters=%d, paths=%d";
+ @Option(name = "-r", aliases = "--recompute", description = "Trigger topology re-computation",
+ required = false, multiValued = false)
+ private boolean recompute = false;
+
protected TopologyService service;
protected Topology topology;
@@ -49,7 +55,10 @@
@Override
protected void execute() {
init();
- if (outputJson()) {
+ if (recompute) {
+ get(TopologyProvider.class).triggerRecompute();
+
+ } else if (outputJson()) {
print("%s", new ObjectMapper().createObjectNode()
.put("time", topology.time())
.put("deviceCount", topology.deviceCount())
diff --git a/core/api/src/main/java/org/onlab/onos/net/Link.java b/core/api/src/main/java/org/onlab/onos/net/Link.java
index 1ae5b9d..3e23dc1 100644
--- a/core/api/src/main/java/org/onlab/onos/net/Link.java
+++ b/core/api/src/main/java/org/onlab/onos/net/Link.java
@@ -25,7 +25,18 @@
/**
* Signifies that this link is an edge, i.e. host link.
*/
- EDGE
+ EDGE,
+
+ /**
+ * Signifies that this link represents a logical link backed by
+ * some form of a tunnel.
+ */
+ TUNNEL,
+
+ /**
+ * Signifies that this link is realized by optical connection.
+ */
+ OPTICAL
}
/**
@@ -49,6 +60,4 @@
*/
Type type();
- // LinkInfo info(); // Additional link information / decorations
-
}
diff --git a/core/api/src/main/java/org/onlab/onos/net/topology/TopologyProvider.java b/core/api/src/main/java/org/onlab/onos/net/topology/TopologyProvider.java
index 7ad8cc0..312e154 100644
--- a/core/api/src/main/java/org/onlab/onos/net/topology/TopologyProvider.java
+++ b/core/api/src/main/java/org/onlab/onos/net/topology/TopologyProvider.java
@@ -7,4 +7,9 @@
*/
public interface TopologyProvider extends Provider {
+ /**
+ * Triggers topology recomputation.
+ */
+ void triggerRecompute();
+
}
diff --git a/core/net/src/main/java/org/onlab/onos/net/link/impl/LinkManager.java b/core/net/src/main/java/org/onlab/onos/net/link/impl/LinkManager.java
index 835c47e..e59eb9f 100644
--- a/core/net/src/main/java/org/onlab/onos/net/link/impl/LinkManager.java
+++ b/core/net/src/main/java/org/onlab/onos/net/link/impl/LinkManager.java
@@ -208,7 +208,7 @@
LinkEvent event = store.createOrUpdateLink(provider().id(),
linkDescription);
if (event != null) {
- log.debug("Link {} detected", linkDescription);
+ log.info("Link {} detected", linkDescription);
post(event);
}
}
diff --git a/core/net/src/main/java/org/onlab/onos/net/topology/impl/DefaultTopologyProvider.java b/core/net/src/main/java/org/onlab/onos/net/topology/impl/DefaultTopologyProvider.java
index 9631c66..0efd08b 100644
--- a/core/net/src/main/java/org/onlab/onos/net/topology/impl/DefaultTopologyProvider.java
+++ b/core/net/src/main/java/org/onlab/onos/net/topology/impl/DefaultTopologyProvider.java
@@ -5,6 +5,7 @@
import org.apache.felix.scr.annotations.Deactivate;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.apache.felix.scr.annotations.Service;
import org.onlab.onos.event.AbstractEventAccumulator;
import org.onlab.onos.event.Event;
import org.onlab.onos.event.EventAccumulator;
@@ -39,6 +40,7 @@
* new topology snapshots.
*/
@Component(immediate = true)
+@Service
public class DefaultTopologyProvider extends AbstractProvider
implements TopologyProvider {
@@ -89,7 +91,7 @@
linkService.addListener(linkListener);
isStarted = true;
- triggerTopologyBuild(Collections.<Event>emptyList());
+ triggerRecompute();
log.info("Started");
}
@@ -108,6 +110,11 @@
log.info("Stopped");
}
+ @Override
+ public void triggerRecompute() {
+ triggerTopologyBuild(Collections.<Event>emptyList());
+ }
+
/**
* Triggers assembly of topology data citing the specified events as the
* reason.
@@ -177,7 +184,11 @@
@Override
public void run() {
- buildTopology(reasons);
+ try {
+ buildTopology(reasons);
+ } catch (Exception e) {
+ log.warn("Unable to compute topology due to: {}", e.getMessage());
+ }
}
}
diff --git a/core/net/src/test/java/org/onlab/onos/net/topology/impl/TopologyManagerTest.java b/core/net/src/test/java/org/onlab/onos/net/topology/impl/TopologyManagerTest.java
index d369073..ada7e78 100644
--- a/core/net/src/test/java/org/onlab/onos/net/topology/impl/TopologyManagerTest.java
+++ b/core/net/src/test/java/org/onlab/onos/net/topology/impl/TopologyManagerTest.java
@@ -195,6 +195,10 @@
public TestProvider() {
super(PID);
}
+
+ @Override
+ public void triggerRecompute() {
+ }
}
private static class TestListener implements TopologyListener {
diff --git a/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/SimpleLinkStore.java b/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/SimpleLinkStore.java
index 5c87921..09d6a62 100644
--- a/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/SimpleLinkStore.java
+++ b/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/SimpleLinkStore.java
@@ -192,14 +192,6 @@
// Creates and stores the link and returns the appropriate event.
// Guarded by linkDescs value (=locking each Link)
private LinkEvent createLink(LinkKey key, Link newLink) {
-
- if (newLink.providerId().isAncillary()) {
- // TODO: revisit ancillary only Link handling
-
- // currently treating ancillary only as down (not visible outside)
- return null;
- }
-
links.put(key, newLink);
srcLinks.put(newLink.src().deviceId(), key);
dstLinks.put(newLink.dst().deviceId(), key);
@@ -209,10 +201,8 @@
// Updates, if necessary the specified link and returns the appropriate event.
// Guarded by linkDescs value (=locking each Link)
private LinkEvent updateLink(LinkKey key, Link oldLink, Link newLink) {
-
if (newLink.providerId().isAncillary()) {
// TODO: revisit ancillary only Link handling
-
// currently treating ancillary only as down (not visible outside)
return null;
}
diff --git a/core/store/trivial/src/test/java/org/onlab/onos/store/trivial/impl/SimpleLinkStoreTest.java b/core/store/trivial/src/test/java/org/onlab/onos/store/trivial/impl/SimpleLinkStoreTest.java
index 02cb411..a7f40ac 100644
--- a/core/store/trivial/src/test/java/org/onlab/onos/store/trivial/impl/SimpleLinkStoreTest.java
+++ b/core/store/trivial/src/test/java/org/onlab/onos/store/trivial/impl/SimpleLinkStoreTest.java
@@ -1,18 +1,6 @@
package org.onlab.onos.store.trivial.impl;
-import static org.junit.Assert.*;
-import static org.onlab.onos.net.DeviceId.deviceId;
-import static org.onlab.onos.net.Link.Type.*;
-import static org.onlab.onos.net.link.LinkEvent.Type.*;
-import static org.onlab.onos.store.trivial.impl.SimpleDeviceStoreTest.assertAnnotationsEquals;
-
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
+import com.google.common.collect.Iterables;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
@@ -23,17 +11,27 @@
import org.onlab.onos.net.DefaultAnnotations;
import org.onlab.onos.net.DeviceId;
import org.onlab.onos.net.Link;
+import org.onlab.onos.net.Link.Type;
import org.onlab.onos.net.LinkKey;
import org.onlab.onos.net.PortNumber;
import org.onlab.onos.net.SparseAnnotations;
-import org.onlab.onos.net.Link.Type;
import org.onlab.onos.net.link.DefaultLinkDescription;
import org.onlab.onos.net.link.LinkEvent;
import org.onlab.onos.net.link.LinkStore;
import org.onlab.onos.net.link.LinkStoreDelegate;
import org.onlab.onos.net.provider.ProviderId;
-import com.google.common.collect.Iterables;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import static org.junit.Assert.*;
+import static org.onlab.onos.net.DeviceId.deviceId;
+import static org.onlab.onos.net.Link.Type.*;
+import static org.onlab.onos.net.link.LinkEvent.Type.*;
+import static org.onlab.onos.store.trivial.impl.SimpleDeviceStoreTest.assertAnnotationsEquals;
/**
* Test of the simple LinkStore implementation.
@@ -301,7 +299,7 @@
LinkEvent event = linkStore.createOrUpdateLink(PIDA,
new DefaultLinkDescription(src, dst, INDIRECT, A1));
- assertNull("Ancillary only link is ignored", event);
+ assertNotNull("Ancillary only link is ignored", event);
// add Primary link
LinkEvent event2 = linkStore.createOrUpdateLink(PID,
@@ -309,7 +307,7 @@
assertLink(DID1, P1, DID2, P2, INDIRECT, event2.subject());
assertAnnotationsEquals(event2.subject().annotations(), A2, A1);
- assertEquals(LINK_ADDED, event2.type());
+ assertEquals(LINK_UPDATED, event2.type());
// update link type
LinkEvent event3 = linkStore.createOrUpdateLink(PID,
@@ -375,7 +373,7 @@
}
@Test
- public final void testAncillaryOnlyNotVisible() {
+ public final void testAncillaryVisible() {
ConnectPoint src = new ConnectPoint(DID1, P1);
ConnectPoint dst = new ConnectPoint(DID2, P2);
@@ -384,18 +382,8 @@
new DefaultLinkDescription(src, dst, INDIRECT, A1));
// Ancillary only link should not be visible
- assertEquals(0, linkStore.getLinkCount());
-
- assertTrue(Iterables.isEmpty(linkStore.getLinks()));
-
- assertNull(linkStore.getLink(src, dst));
-
- assertEquals(Collections.emptySet(), linkStore.getIngressLinks(dst));
-
- assertEquals(Collections.emptySet(), linkStore.getEgressLinks(src));
-
- assertEquals(Collections.emptySet(), linkStore.getDeviceEgressLinks(DID1));
- assertEquals(Collections.emptySet(), linkStore.getDeviceIngressLinks(DID2));
+ assertEquals(1, linkStore.getLinkCount());
+ assertNotNull(linkStore.getLink(src, dst));
}
// If Delegates should be called only on remote events,
diff --git a/pom.xml b/pom.xml
index cef3b79..8aa5ce4 100644
--- a/pom.xml
+++ b/pom.xml
@@ -88,7 +88,6 @@
<version>18.0</version>
</dependency>
-
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty</artifactId>
diff --git a/tools/test/cells/single b/tools/test/cells/single
index 6b13756..125477a 100644
--- a/tools/test/cells/single
+++ b/tools/test/cells/single
@@ -7,4 +7,4 @@
export OCN="192.168.56.103"
export OCI="${OC1}"
-export ONOS_FEATURES="${ONOS_FEATURES:-webconsole,onos-api,onos-core-trivial,onos-cli,onos-openflow,onos-app-fwd,onos-app-proxyarp,onos-app-tvue}"
+export ONOS_FEATURES="${ONOS_FEATURES:-webconsole,onos-api,onos-core-trivial,onos-cli,onos-rest,onos-openflow,onos-app-fwd,onos-app-proxyarp,onos-app-tvue}"
diff --git a/tools/test/topos/oe-linear-3.json b/tools/test/topos/oe-linear-3.json
new file mode 100644
index 0000000..9214bd9
--- /dev/null
+++ b/tools/test/topos/oe-linear-3.json
@@ -0,0 +1,45 @@
+{
+ "devices" : [
+ {
+ "uri": "of:0000ffffffffff01", "mac": "ffffffffffff01", "type": "ROADM",
+ "mfr": "Linc", "hw": "OE", "sw": "?", "serial": "?",
+ "annotations": { "latitude": 37.6, "longitude": 122.3, "optical.regens": 0 }
+ },
+ {
+ "uri": "of:0000ffffffffff02", "mac": "ffffffffffff02", "type": "ROADM",
+ "mfr": "Linc", "hw": "OE", "sw": "?", "serial": "?",
+ "annotations": { "latitude": 37.3, "longitude": 121.9, "optical.regens": 0 }
+ },
+ {
+ "uri": "of:0000ffffffffff03", "mac": "ffffffffffff03", "type": "ROADM",
+ "mfr": "Linc", "hw": "OE", "sw": "?", "serial": "?",
+ "annotations": { "latitude": 33.9, "longitude": 118.4, "optical.regens": 2 }
+ },
+
+ {
+ "uri": "of:0000ffffffff0001", "mac": "ffffffffff0003", "type": "SWITCH",
+ "mfr": "Linc", "hw": "PK", "sw": "?", "serial": "?",
+ "annotations": { "latitude": 37.6, "longitude": 122.3 }
+ },
+ {
+ "uri": "of:0000ffffffff0002", "mac": "ffffffffff0002", "type": "SWITCH",
+ "mfr": "Linc", "hw": "PK", "sw": "?", "serial": "?",
+ "annotations": { "latitude": 37.3, "longitude": 121.9 }
+ }
+ ],
+
+ "links" : [
+ { "src": "of:0000ffffffffff01/10", "dst": "of:0000ffffffffff03/30", "type": "OPTICAL", "annotations": { "optical.waves": 80, "optical.type": "WDM" } },
+ { "src": "of:0000ffffffffff02/20", "dst": "of:0000ffffffffff03/31", "type": "OPTICAL", "annotations": { "optical.waves": 80, "optical.type": "WDM" } },
+
+ { "src": "of:0000ffffffff0001/10", "dst": "of:0000ffffffffff01/11", "type": "OPTICAL", "annotations": { "bandwidth": 100000, "optical.type": "cross-connect" } },
+ { "src": "of:0000ffffffff0002/10", "dst": "of:0000ffffffffff02/21", "type": "OPTICAL", "annotations": { "bandwidth": 100000, "optical.type": "cross-connect" } }
+ ],
+
+ "hosts" : [
+ { "mac": "a0:00:00:00:00:11", "vlan": -1, "location": "of:0000ffffffff0001/11", "ip": "1.2.3.4" },
+ { "mac": "a0:00:00:00:00:12", "vlan": -1, "location": "of:0000ffffffff0001/12", "ip": "1.2.3.5" },
+ { "mac": "a0:00:00:00:00:21", "vlan": -1, "location": "of:0000ffffffff0002/11", "ip": "2.2.3.4" },
+ { "mac": "a0:00:00:00:00:22", "vlan": -1, "location": "of:0000ffffffff0002/12", "ip": "2.2.3.5" }
+ ]
+}
\ No newline at end of file
diff --git a/utils/misc/src/main/java/org/onlab/packet/ChassisId.java b/utils/misc/src/main/java/org/onlab/packet/ChassisId.java
index 3029647..5b48e63 100644
--- a/utils/misc/src/main/java/org/onlab/packet/ChassisId.java
+++ b/utils/misc/src/main/java/org/onlab/packet/ChassisId.java
@@ -32,7 +32,7 @@
* @param value the value to use.
*/
public ChassisId(String value) {
- this.value = Long.valueOf(value);
+ this.value = Long.valueOf(value, 16);
}
/**
diff --git a/web/api/pom.xml b/web/api/pom.xml
index e6f9ff8..da8fd1c 100644
--- a/web/api/pom.xml
+++ b/web/api/pom.xml
@@ -23,12 +23,6 @@
<version>1.0.0-SNAPSHOT</version>
<scope>test</scope>
</dependency>
- <dependency>
- <groupId>com.google.guava</groupId>
- <artifactId>guava</artifactId>
- <scope>test</scope>
- </dependency>
-
</dependencies>
<properties>
diff --git a/web/api/src/main/java/org/onlab/onos/rest/ConfigProvider.java b/web/api/src/main/java/org/onlab/onos/rest/ConfigProvider.java
new file mode 100644
index 0000000..3120511
--- /dev/null
+++ b/web/api/src/main/java/org/onlab/onos/rest/ConfigProvider.java
@@ -0,0 +1,232 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.onlab.onos.rest;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import org.onlab.onos.net.ConnectPoint;
+import org.onlab.onos.net.DefaultAnnotations;
+import org.onlab.onos.net.Device;
+import org.onlab.onos.net.Host;
+import org.onlab.onos.net.HostId;
+import org.onlab.onos.net.HostLocation;
+import org.onlab.onos.net.Link;
+import org.onlab.onos.net.MastershipRole;
+import org.onlab.onos.net.SparseAnnotations;
+import org.onlab.onos.net.device.DefaultDeviceDescription;
+import org.onlab.onos.net.device.DeviceDescription;
+import org.onlab.onos.net.device.DeviceProvider;
+import org.onlab.onos.net.device.DeviceProviderRegistry;
+import org.onlab.onos.net.device.DeviceProviderService;
+import org.onlab.onos.net.host.DefaultHostDescription;
+import org.onlab.onos.net.host.HostProvider;
+import org.onlab.onos.net.host.HostProviderRegistry;
+import org.onlab.onos.net.host.HostProviderService;
+import org.onlab.onos.net.link.DefaultLinkDescription;
+import org.onlab.onos.net.link.LinkProvider;
+import org.onlab.onos.net.link.LinkProviderRegistry;
+import org.onlab.onos.net.link.LinkProviderService;
+import org.onlab.onos.net.provider.ProviderId;
+import org.onlab.packet.ChassisId;
+import org.onlab.packet.IpPrefix;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.VlanId;
+
+import java.net.URI;
+import java.util.Iterator;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onlab.onos.net.DeviceId.deviceId;
+import static org.onlab.onos.net.PortNumber.portNumber;
+
+/**
+ * Provider of devices and links parsed from a JSON configuration structure.
+ */
+class ConfigProvider implements DeviceProvider, LinkProvider, HostProvider {
+
+ private static final ProviderId PID =
+ new ProviderId("cfg", "org.onlab.onos.rest", true);
+
+ private final JsonNode cfg;
+ private final DeviceProviderRegistry deviceProviderRegistry;
+ private final LinkProviderRegistry linkProviderRegistry;
+ private final HostProviderRegistry hostProviderRegistry;
+
+ /**
+ * Creates a new configuration provider.
+ *
+ * @param cfg JSON configuration
+ * @param deviceProviderRegistry device provider registry
+ * @param linkProviderRegistry link provider registry
+ * @param hostProviderRegistry host provider registry
+ */
+ ConfigProvider(JsonNode cfg,
+ DeviceProviderRegistry deviceProviderRegistry,
+ LinkProviderRegistry linkProviderRegistry,
+ HostProviderRegistry hostProviderRegistry) {
+ this.cfg = checkNotNull(cfg, "Configuration cannot be null");
+ this.deviceProviderRegistry = checkNotNull(deviceProviderRegistry, "Device provider registry cannot be null");
+ this.linkProviderRegistry = checkNotNull(linkProviderRegistry, "Link provider registry cannot be null");
+ this.hostProviderRegistry = checkNotNull(hostProviderRegistry, "Host provider registry cannot be null");
+ }
+
+ /**
+ * Parses the given JSON and provides links as configured.
+ */
+ void parse() {
+ parseDevices();
+ parseLinks();
+ parseHosts();
+ }
+
+ // Parses the given JSON and provides devices.
+ private void parseDevices() {
+ try {
+ DeviceProviderService dps = deviceProviderRegistry.register(this);
+ JsonNode nodes = cfg.get("devices");
+ if (nodes != null) {
+ for (JsonNode node : nodes) {
+ parseDevice(dps, node);
+ }
+ }
+ } finally {
+ deviceProviderRegistry.unregister(this);
+ }
+ }
+
+ // Parses the given node with device data and supplies the device.
+ private void parseDevice(DeviceProviderService dps, JsonNode node) {
+ URI uri = URI.create(get(node, "uri"));
+ Device.Type type = Device.Type.valueOf(get(node, "type"));
+ String mfr = get(node, "mfr");
+ String hw = get(node, "hw");
+ String sw = get(node, "sw");
+ String serial = get(node, "serial");
+ ChassisId cid = new ChassisId(get(node, "mac"));
+ SparseAnnotations annotations = annotations(node.get("annotations"));
+
+ DeviceDescription desc =
+ new DefaultDeviceDescription(uri, type, mfr, hw, sw, serial,
+ cid, annotations);
+ dps.deviceConnected(deviceId(uri), desc);
+ }
+
+ // Parses the given JSON and provides links as configured.
+ private void parseLinks() {
+ try {
+ LinkProviderService lps = linkProviderRegistry.register(this);
+ JsonNode nodes = cfg.get("links");
+ if (nodes != null) {
+ for (JsonNode node : nodes) {
+ parseLink(lps, node, false);
+ if (!node.has("halfplex")) {
+ parseLink(lps, node, true);
+ }
+ }
+ }
+ } finally {
+ linkProviderRegistry.unregister(this);
+ }
+ }
+
+ // Parses the given node with link data and supplies the link.
+ private void parseLink(LinkProviderService lps, JsonNode node, boolean reverse) {
+ ConnectPoint src = connectPoint(get(node, "src"));
+ ConnectPoint dst = connectPoint(get(node, "dst"));
+ Link.Type type = Link.Type.valueOf(get(node, "type"));
+ SparseAnnotations annotations = annotations(node.get("annotations"));
+
+ DefaultLinkDescription desc = reverse ?
+ new DefaultLinkDescription(dst, src, type, annotations) :
+ new DefaultLinkDescription(src, dst, type, annotations);
+ lps.linkDetected(desc);
+ }
+
+ // Parses the given JSON and provides hosts as configured.
+ private void parseHosts() {
+ try {
+ HostProviderService hps = hostProviderRegistry.register(this);
+ JsonNode nodes = cfg.get("hosts");
+ if (nodes != null) {
+ for (JsonNode node : nodes) {
+ parseHost(hps, node);
+ }
+ }
+ } finally {
+ hostProviderRegistry.unregister(this);
+ }
+ }
+
+ // Parses the given node with host data and supplies the host.
+ private void parseHost(HostProviderService hps, JsonNode node) {
+ MacAddress mac = MacAddress.valueOf(get(node, "mac"));
+ VlanId vlanId = VlanId.vlanId(node.get("vlan").shortValue());
+ HostId hostId = HostId.hostId(mac, vlanId);
+ SparseAnnotations annotations = annotations(node.get("annotations"));
+ HostLocation location = new HostLocation(connectPoint(get(node, "location")), 0);
+ IpPrefix ip = IpPrefix.valueOf(get(node, "ip"));
+
+ DefaultHostDescription desc =
+ new DefaultHostDescription(mac, vlanId, location, ip, annotations);
+ hps.hostDetected(hostId, desc);
+ }
+
+ // Produces set of annotations from the given JSON node.
+ private SparseAnnotations annotations(JsonNode node) {
+ if (node == null) {
+ return null;
+ }
+
+ DefaultAnnotations.Builder builder = DefaultAnnotations.builder();
+ Iterator<String> it = node.fieldNames();
+ while (it.hasNext()) {
+ String k = it.next();
+ builder.set(k, node.get(k).asText());
+ }
+ return builder.build();
+ }
+
+ // Produces a connection point from the specified uri/port text.
+ private ConnectPoint connectPoint(String text) {
+ int i = text.lastIndexOf("/");
+ return new ConnectPoint(deviceId(text.substring(0, i)),
+ portNumber(text.substring(i + 1)));
+ }
+
+ // Returns string form of the named property in the given JSON object.
+ private String get(JsonNode node, String name) {
+ return node.path(name).asText();
+ }
+
+ @Override
+ public void triggerProbe(Device device) {
+ }
+
+ @Override
+ public void roleChanged(Device device, MastershipRole newRole) {
+ }
+
+ @Override
+ public void triggerProbe(Host host) {
+ }
+
+ @Override
+ public ProviderId id() {
+ return PID;
+ }
+}
diff --git a/web/api/src/main/java/org/onlab/onos/rest/ConfigResource.java b/web/api/src/main/java/org/onlab/onos/rest/ConfigResource.java
new file mode 100644
index 0000000..219abbd
--- /dev/null
+++ b/web/api/src/main/java/org/onlab/onos/rest/ConfigResource.java
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.onlab.onos.rest;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.onlab.onos.net.device.DeviceProviderRegistry;
+import org.onlab.onos.net.host.HostProviderRegistry;
+import org.onlab.onos.net.link.LinkProviderRegistry;
+import org.onlab.rest.BaseResource;
+
+import javax.ws.rs.Consumes;
+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;
+
+/**
+ * Resource that acts as an ancillary provider for uploading pre-configured
+ * devices, ports and links.
+ */
+@Path("config")
+public class ConfigResource extends BaseResource {
+
+ @POST
+ @Path("topology")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response topology(InputStream input) throws IOException {
+ ObjectMapper mapper = new ObjectMapper();
+ JsonNode cfg = mapper.readTree(input);
+ new ConfigProvider(cfg, get(DeviceProviderRegistry.class),
+ get(LinkProviderRegistry.class),
+ get(HostProviderRegistry.class)).parse();
+ return Response.ok(mapper.createObjectNode().toString()).build();
+ }
+
+}
diff --git a/web/api/src/test/java/org/onlab/onos/rest/topo.json b/web/api/src/test/java/org/onlab/onos/rest/topo.json
new file mode 100644
index 0000000..cdef976
--- /dev/null
+++ b/web/api/src/test/java/org/onlab/onos/rest/topo.json
@@ -0,0 +1,19 @@
+{
+ "devices" : [
+ {
+ "uri": "of:00000000000001", "type": "ROADM", "mfr": "Foo, Inc.", "hw": "Alpha", "sw": "1.2.3",
+ "serial": "ab321", "mac": "00000000000001", "annotations": {"foo": "bar"},
+ "ports": []
+ },
+ {
+ "uri": "of:00000000000002", "type": "ROADM", "mfr": "Foo, Inc.", "hw": "Alpha", "sw": "1.2.3",
+ "serial": "ab456", "mac": "00000000000002", "annotations": {"foo": "bar"},
+ "ports": []
+ }
+ ],
+
+ "links" : [
+ { "src": "of:00000000000001/1", "dst": "of:00000000000002/1", "type": "OPTICAL" },
+ { "src": "of:00000000000002/1", "dst": "of:00000000000001/1", "type": "OPTICAL" }
+ ]
+}
\ No newline at end of file
diff --git a/web/pom.xml b/web/pom.xml
index 3e9f2a0..ebe4d89 100644
--- a/web/pom.xml
+++ b/web/pom.xml
@@ -44,6 +44,11 @@
</dependency>
<dependency>
+ <groupId>com.google.guava</groupId>
+ <artifactId>guava</artifactId>
+ </dependency>
+
+ <dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-servlet</artifactId>
</dependency>
@@ -93,6 +98,7 @@
${project.groupId}.${project.artifactId}
</Bundle-SymbolicName>
<Import-Package>
+ org.slf4j,
org.osgi.framework,
javax.ws.rs,javax.ws.rs.core,
com.sun.jersey.api.core,
@@ -100,6 +106,8 @@
com.sun.jersey.server.impl.container.servlet,
com.fasterxml.jackson.databind,
com.fasterxml.jackson.databind.node,
+ com.google.common.base.*,
+ org.onlab.packet.*,
org.onlab.rest.*,
org.onlab.onos.*
</Import-Package>