Thomas Vachuska

Added a REST command to upload topology configuration.

......@@ -88,7 +88,6 @@
<version>18.0</version>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty</artifactId>
......
......@@ -7,4 +7,4 @@ export OC1="192.168.56.101"
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}"
......
{
"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
......@@ -32,7 +32,7 @@ public final class ChassisId {
* @param value the value to use.
*/
public ChassisId(String value) {
this.value = Long.valueOf(value);
this.value = Long.valueOf(value, 16);
}
/**
......
......@@ -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>
......
/*
* 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;
}
}
/*
* 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();
}
}
{
"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
......@@ -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>
......