Thomas Vachuska
Committed by Gerrit Code Review

ONOS-2486 Adding swagger-based REST API documentation.

Change-Id: I237d973d73549ad30ddc638c1c201f024d344c70
Showing 52 changed files with 1464 additions and 221 deletions
......@@ -39,7 +39,13 @@
<onos.version>1.3.0-SNAPSHOT</onos.version>
<onos.app.name>org.onosproject.acl</onos.app.name>
<onos.app.origin>DLUT</onos.app.origin>
<web.context>/onos/v1/acl</web.context>
<web.context>/onos/acl</web.context>
<api.version>1.0.0</api.version>
<api.title>ONOS ACL Application REST API</api.title>
<api.description>
APIs for interacting with the ACL application.
</api.description>
<api.package>org.onos.acl.impl</api.package>
</properties>
<dependencies>
......@@ -108,6 +114,10 @@
<configuration>
<instructions>
<_wab>src/main/webapp/</_wab>
<Include-Resource>
WEB-INF/classes/apidoc/swagger.json=target/swagger.json,
{maven-resources}
</Include-Resource>
<Bundle-SymbolicName>
${project.groupId}.${project.artifactId}
</Bundle-SymbolicName>
......
......@@ -40,8 +40,9 @@ import java.io.IOException;
import java.io.InputStream;
import java.util.List;
// FIXME: This does now follow REST-full principles and should be refactored.
/**
* REST resource for interacting with ACL application.
* Manage ACL rules.
*/
@Path("")
public class AclWebResource extends AbstractWebResource {
......
......@@ -33,6 +33,12 @@
<properties>
<onos.app.name>org.onosproject.cordfabric</onos.app.name>
<web.context>/onos/cordfabric</web.context>
<api.version>1.0.0</api.version>
<api.title>ONOS CORD Fabric REST API</api.title>
<api.description>
APIs for interacting with the CORD Fabric application.
</api.description>
<api.package>org.onosproject.cordfabric</api.package>
</properties>
<dependencies>
......@@ -94,6 +100,10 @@
<configuration>
<instructions>
<_wab>src/main/webapp/</_wab>
<Include-Resource>
WEB-INF/classes/apidoc/swagger.json=target/swagger.json,
{maven-resources}
</Include-Resource>
<Bundle-SymbolicName>
${project.groupId}.${project.artifactId}
</Bundle-SymbolicName>
......
......@@ -33,6 +33,12 @@
<properties>
<onos.app.name>org.onosproject.segmentrouting</onos.app.name>
<web.context>/onos/segmentrouting</web.context>
<api.version>1.0.0</api.version>
<api.title>ONOS Segment Routing REST API</api.title>
<api.description>
APIs for interacting with the Segment Routing application.
</api.description>
<api.package>org.onosproject.segmentrouting.web</api.package>
</properties>
<dependencies>
......@@ -94,6 +100,10 @@
<configuration>
<instructions>
<_wab>src/main/webapp/</_wab>
<Include-Resource>
WEB-INF/classes/apidoc/swagger.json=target/swagger.json,
{maven-resources}
</Include-Resource>
<Bundle-SymbolicName>
${project.groupId}.${project.artifactId}
</Bundle-SymbolicName>
......
/*
* Copyright 2015 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.onosproject.rest;
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;
/**
* Self-registering REST API provider.
*/
@Component(immediate = true, componentAbstract = true)
public abstract class AbstractApiDocRegistrator {
protected final ApiDocProvider provider;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected ApiDocService service;
/**
* Creates registrator for the specified REST API doc provider.
*
* @param provider REST API provider
*/
protected AbstractApiDocRegistrator(ApiDocProvider provider) {
this.provider = provider;
}
@Activate
protected void activate() {
service.register(provider);
}
@Deactivate
protected void deactivate() {
service.unregister(provider);
}
}
......@@ -13,9 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.ui.impl;
import org.onlab.rest.BaseResource;
package org.onosproject.rest;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
......@@ -28,7 +26,7 @@ import static com.google.common.base.Preconditions.checkArgument;
/**
* Resource for serving semi-static resources.
*/
public class AbstractInjectionResource extends BaseResource {
public class AbstractInjectionResource extends AbstractWebResource {
/**
* Returns the index into the supplied string where the end of the
......@@ -63,7 +61,7 @@ public class AbstractInjectionResource extends BaseResource {
protected class StreamEnumeration implements Enumeration<InputStream> {
private final Iterator<InputStream> iterator;
StreamEnumeration(List<InputStream> streams) {
public StreamEnumeration(List<InputStream> streams) {
this.iterator = streams.iterator();
}
......
/*
* Copyright 2015 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.onosproject.rest;
import com.google.common.annotations.Beta;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.InputStream;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Entity capable of providing REST API documentation resources.
*/
@Beta
public class ApiDocProvider {
private final Logger log = LoggerFactory.getLogger(getClass());
private static final String DOCS = "/apidoc/swagger.json";
private static final String MODEL = "/apidoc/model.json";
private final String key;
private final String name;
private final ClassLoader classLoader;
/**
* Creates a new REST API documentation provider.
*
* @param key REST API key
* @param name REST API name
* @param classLoader class loader
*/
public ApiDocProvider(String key, String name, ClassLoader classLoader) {
this.key = checkNotNull(key, "Key cannot be null");
this.name = checkNotNull(name, "Name cannot be null");
this.classLoader = checkNotNull(classLoader, "Class loader cannot be null");
}
/**
* Returns the REST API key.
*
* @return REST API key
*/
public String key() {
return key;
}
/**
* Returns the REST API name.
*
* @return REST API name
*/
public String name() {
return name;
}
/**
* Returns input stream containing Swagger UI compatible JSON.
*
* @return input stream with Swagger JSON data
*/
public InputStream docs() {
return get(DOCS);
}
/**
* Returns input stream containing JSON model schema.
*
* @return input stream with JSON model schema
*/
public InputStream model() {
return get(MODEL);
}
private InputStream get(String resource) {
InputStream stream = classLoader.getResourceAsStream(resource);
if (stream == null) {
log.warn("Unable to find JSON resource {}", resource);
}
return stream;
}
}
/*
* Copyright 2015 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.onosproject.rest;
import com.google.common.annotations.Beta;
import java.util.Set;
/**
* Service for registering REST API documentation resources.
*/
@Beta
public interface ApiDocService {
/**
* Registers the specified REST API documentation provider.
*
* @param provider REST API documentation provider
*/
void register(ApiDocProvider provider);
/**
* Unregisters the specified REST API documentation provider.
*
* @param provider REST API documentation provider
*/
void unregister(ApiDocProvider provider);
/**
* Returns the set of all registered REST API documentation providers.
*
* @return set of registered documentation providers
*/
Set<ApiDocProvider> getDocProviders();
/**
* Returns the specified REST API documentation provider with the specified
* key.
*
* @param key REST API key
* @return documentation provider
*/
ApiDocProvider getDocProvider(String key);
}
......@@ -30,7 +30,6 @@
<relativePath>tools/build/pom.xml</relativePath>
</parent>
<groupId>org.onosproject</groupId>
<artifactId>onos</artifactId>
<packaging>pom</packaging>
<version>1.3.0-SNAPSHOT</version>
......@@ -43,18 +42,23 @@
<module>core</module>
<module>web</module>
<module>cli</module>
<module>openflow</module>
<module>ovsdb</module>
<module>pcep</module>
<module>providers</module>
<module>drivers</module>
<module>openflow</module>
<module>apps</module>
<module>incubator</module>
<module>features</module>
<module>pcep</module>
<module>tools/package/archetypes</module>
<module>tools/package/archetypes</module>
<module>tools/package/branding</module>
<!-- FIXME remove before release -->
<module>tools/package/maven-plugin</module>
<module>ovsdb</module>
</modules>
<url>http://onosproject.org/</url>
......@@ -412,7 +416,7 @@
<dependency>
<groupId>org.onosproject</groupId>
<artifactId>onos-app-pcep-api</artifactId>
<artifactId>onos-app-pcep-api</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
......@@ -515,9 +519,11 @@
<artifactId>maven-surefire-plugin</artifactId>
<version>2.18.1</version>
<configuration>
<redirectTestOutputToFile>true</redirectTestOutputToFile>
<redirectTestOutputToFile>true
</redirectTestOutputToFile>
<printSummary>true</printSummary>
<excludedGroups>org.onlab.junit.IntegrationTest</excludedGroups>
<excludedGroups>org.onlab.junit.IntegrationTest
</excludedGroups>
<rerunFailingTestsCount>1</rerunFailingTestsCount>
</configuration>
</plugin>
......@@ -618,7 +624,7 @@
</execution>
<execution>
<id>swagger</id>
<phase>generate-resources</phase>
<phase>generate-sources</phase>
<goals>
<goal>swagger</goal>
</goals>
......
/*
* Auto-generated by OnosSwaggerMojo.
*
* Copyright 2015 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 ${api.package};
import org.apache.felix.scr.annotations.Component;
import org.onosproject.rest.AbstractApiDocRegistrator;
import org.onosproject.rest.ApiDocProvider;
@Component(immediate = true)
public class ApiDocRegistrator extends AbstractApiDocRegistrator {
public ApiDocRegistrator() {
super(new ApiDocProvider("${web.context}",
"${api.title}",
ApiDocRegistrator.class.getClassLoader()));
}
}
......@@ -29,7 +29,7 @@
<artifactId>onos-rest</artifactId>
<packaging>bundle</packaging>
<description>ONOS REST API</description>
<description>ONOS Core REST API</description>
<dependencies>
<dependency>
......@@ -57,6 +57,12 @@
<properties>
<web.context>/onos/v1</web.context>
<api.version>1.0.0</api.version>
<api.title>${project.description}</api.title>
<api.description>
Core APIs for external interactions with various ONOS subsystems.
</api.description>
<api.package>org.onosproject.rest.impl</api.package>
</properties>
<build>
......@@ -65,6 +71,43 @@
<groupId>org.onosproject</groupId>
<artifactId>onos-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<extensions>true</extensions>
<configuration>
<instructions>
<_wab>src/main/webapp/</_wab>
<Include-Resource>
WEB-INF/classes/apidoc/swagger.json=target/swagger.json,
{maven-resources}
</Include-Resource>
<Bundle-SymbolicName>
${project.groupId}.${project.artifactId}
</Bundle-SymbolicName>
<Import-Package>
org.slf4j,
org.osgi.framework,
javax.ws.rs,javax.ws.rs.core,javax.ws.rs.ext,
com.sun.jersey.api,
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.base.*,
com.google.common.collect.*,
com.google.common.io.*,
org.onlab.util.*,
org.onlab.osgi.*,
org.onlab.packet.*,
org.onlab.rest.*,
org.onosproject.*
</Import-Package>
<Web-ContextPath>${web.context}</Web-ContextPath>
</instructions>
</configuration>
</plugin>
</plugins>
</build>
......
/*
* Copyright 2015 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.onosproject.rest.impl;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Maps;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Deactivate;
import org.apache.felix.scr.annotations.Service;
import org.onosproject.rest.ApiDocProvider;
import org.onosproject.rest.ApiDocService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Map;
import java.util.Set;
/**
* Implementation of the REST API documentation tracker.
*/
@Component(immediate = true)
@Service
public class ApiDocManager implements ApiDocService {
private final Logger log = LoggerFactory.getLogger(getClass());
// Set of doc providers
private final Map<String, ApiDocProvider> providers = Maps.newConcurrentMap();
@Activate
public void activate() {
log.info("Started");
}
@Deactivate
public void deactivate() {
log.info("Stopped");
}
@Override
public void register(ApiDocProvider provider) {
providers.put(provider.key(), provider);
}
@Override
public void unregister(ApiDocProvider provider) {
providers.remove(provider.name());
}
@Override
public Set<ApiDocProvider> getDocProviders() {
return ImmutableSet.copyOf(providers.values());
}
@Override
public ApiDocProvider getDocProvider(String key) {
return providers.get(key);
}
}
/*
* Copyright 2015 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.
*/
/**
* REST API related service.
*/
package org.onosproject.rest.impl;
\ No newline at end of file
/*
* Copyright 2015 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.onosproject.rest.resources;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.onosproject.rest.AbstractInjectionResource;
import org.onosproject.rest.ApiDocService;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.core.Response;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.SequenceInputStream;
import static com.google.common.collect.ImmutableList.of;
import static com.google.common.io.ByteStreams.toByteArray;
import static javax.ws.rs.core.MediaType.*;
import static org.onlab.util.Tools.nullIsNotFound;
/**
* REST API documentation.
*/
@Path("docs")
public class ApiDocResource extends AbstractInjectionResource {
private static final String CONTENT_TYPE = "Content-Type";
private static final String STYLESHEET = "text/css";
private static final String SCRIPT = "text/javascript";
private static final String DOCS = "/docs/";
private static final String INJECT_START = "<!-- {API-START} -->";
private static final String INJECT_END = "<!-- {API-END} -->";
/**
* Get all registered REST API docs.
* Returns array of all registered API docs.
*
* @return 200 OK
*/
@GET
@Path("apis")
public Response getApiList() {
ObjectNode root = mapper().createObjectNode();
ArrayNode apis = newArray(root, "apis");
get(ApiDocService.class).getDocProviders().forEach(p -> apis.add(p.name()));
return ok(root.toString()).build();
}
/**
* Get Swagger UI JSON.
*
* @param key REST API web context
* @return 200 OK
*/
@GET
@Path("apis/{key: .*?}/swagger.json")
public Response getApi(@PathParam("key") String key) {
String k = key.startsWith("/") ? key : "/" + key;
InputStream stream = nullIsNotFound(get(ApiDocService.class).getDocProvider(k),
"REST API not found for " + k).docs();
return ok(nullIsNotFound(stream, "REST API docs not found for " + k))
.header(CONTENT_TYPE, APPLICATION_JSON).build();
}
/**
* Get REST API model schema.
*
* @param key REST API web context
* @return 200 OK
*/
@GET
@Path("apis/{key: .*?}/model.json")
public Response getApiModel(@PathParam("name") String key) {
String k = key.startsWith("/") ? key : "/" + key;
InputStream stream = nullIsNotFound(get(ApiDocService.class).getDocProvider(k),
"REST API not found for " + k).model();
return ok(nullIsNotFound(stream, "REST API model not found for " + k))
.header(CONTENT_TYPE, APPLICATION_JSON).build();
}
/**
* Get Swagger UI main index page.
*
* @return 200 OK
*/
@GET
@Path("/")
public Response getDefault() throws IOException {
return getIndex();
}
/**
* Get Swagger UI main index page.
*
* @return 200 OK
*/
@GET
@Path("index.html")
public Response getIndex() throws IOException {
InputStream stream = getClass().getClassLoader().getResourceAsStream(DOCS + "index.html");
nullIsNotFound(stream, "index.html not found");
String index = new String(toByteArray(stream));
int p1s = split(index, 0, INJECT_START);
int p1e = split(index, p1s, INJECT_END);
int p2s = split(index, p1e, null);
StreamEnumeration streams =
new StreamEnumeration(of(stream(index, 0, p1s),
includeOptions(get(ApiDocService.class)),
stream(index, p1e, p2s)));
return ok(new SequenceInputStream(streams))
.header(CONTENT_TYPE, TEXT_HTML).build();
}
private InputStream includeOptions(ApiDocService service) {
StringBuilder sb = new StringBuilder();
service.getDocProviders().forEach(p -> {
sb.append("<option value=\"").append(p.key()).append("\"")
.append(p.key().equals("/onos/v1") ? " selected>" : ">")
.append(p.name())
.append("</option>");
});
return new ByteArrayInputStream(sb.toString().getBytes());
}
/**
* Get Swagger UI resource.
*
* @return 200 OK
*/
@GET
@Path("{resource: .*}")
public Response getResource(@PathParam("resource") String resource) throws IOException {
InputStream stream = getClass().getClassLoader().getResourceAsStream(DOCS + resource);
return ok(nullIsNotFound(stream, resource + " not found"))
.header(CONTENT_TYPE, contentType(resource)).build();
}
static String contentType(String resource) {
return resource.endsWith(".html") ? TEXT_HTML :
resource.endsWith(".css") ? STYLESHEET :
resource.endsWith(".js") ? SCRIPT :
APPLICATION_OCTET_STREAM;
}
}
......@@ -35,32 +35,54 @@ import java.io.InputStream;
import java.util.Set;
/**
* REST resource for interacting with the inventory of applications.
* Manage inventory of applications.
*/
@Path("applications")
public class ApplicationsWebResource extends AbstractWebResource {
/**
* Get all installed applications.
* Returns array of all installed applications.
*
* @return 200 OK
*/
@GET
public Response getApplications() {
public Response getApps() {
ApplicationAdminService service = get(ApplicationAdminService.class);
Set<Application> apps = service.getApplications();
return ok(encodeArray(Application.class, "applications", apps)).build();
}
/**
* Get application details.
* Returns details of the specified application.
*
* @param name application name
* @return 200 OK; 404; 401
*/
@GET
@Path("{name}")
public Response getApplication(@PathParam("name") String name) {
public Response getApp(@PathParam("name") String name) {
ApplicationAdminService service = get(ApplicationAdminService.class);
ApplicationId appId = service.getId(name);
return response(service, appId);
}
/**
* Install a new application.
* Uploads application archive stream and optionally activates the
* application.
*
* @param activate true to activate app also
* @param stream application archive stream
* @return 200 OK; 404; 401
*/
@POST
@Consumes(MediaType.APPLICATION_OCTET_STREAM)
@Produces(MediaType.APPLICATION_JSON)
public Response installApplication(@QueryParam("activate")
@DefaultValue("false") boolean activate,
InputStream stream) {
public Response installApp(@QueryParam("activate")
@DefaultValue("false") boolean activate,
InputStream stream) {
ApplicationAdminService service = get(ApplicationAdminService.class);
Application app = service.install(stream);
if (activate) {
......@@ -69,30 +91,51 @@ public class ApplicationsWebResource extends AbstractWebResource {
return ok(codec(Application.class).encode(app, this)).build();
}
/**
* Uninstall application.
* Uninstalls the specified application deactivating it first if necessary.
*
* @param name application name
* @return 200 OK; 404; 401
*/
@DELETE
@Produces(MediaType.APPLICATION_JSON)
@Path("{name}")
public Response uninstallApplication(@PathParam("name") String name) {
public Response uninstallApp(@PathParam("name") String name) {
ApplicationAdminService service = get(ApplicationAdminService.class);
ApplicationId appId = service.getId(name);
service.uninstall(appId);
return Response.ok().build();
}
/**
* Activate application.
* Activates the specified application.
*
* @param name application name
* @return 200 OK; 404; 401
*/
@POST
@Produces(MediaType.APPLICATION_JSON)
@Path("{name}/active")
public Response activateApplication(@PathParam("name") String name) {
public Response activateApp(@PathParam("name") String name) {
ApplicationAdminService service = get(ApplicationAdminService.class);
ApplicationId appId = service.getId(name);
service.activate(appId);
return response(service, appId);
}
/**
* De-activate application.
* De-activates the specified application.
*
* @param name application name
* @return 200 OK; 404; 401
*/
@DELETE
@Produces(MediaType.APPLICATION_JSON)
@Path("{name}/active")
public Response deactivateApplication(@PathParam("name") String name) {
public Response deactivateApp(@PathParam("name") String name) {
ApplicationAdminService service = get(ApplicationAdminService.class);
ApplicationId appId = service.getId(name);
service.deactivate(appId);
......
......@@ -37,19 +37,32 @@ import java.util.List;
import static org.onlab.util.Tools.nullIsNotFound;
/**
* REST resource for interacting with the ONOS cluster subsystem.
* Manage cluster of ONOS instances.
*/
@Path("cluster")
public class ClusterWebResource extends AbstractWebResource {
public static final String NODE_NOT_FOUND = "Node is not found";
/**
* Get all cluster nodes.
* Returns array of all cluster nodes.
*
* @return 200 OK
*/
@GET
public Response getClusterNodes() {
Iterable<ControllerNode> nodes = get(ClusterService.class).getNodes();
return ok(encodeArray(ControllerNode.class, "nodes", nodes)).build();
}
/**
* Get cluster node details.
* Returns details of the specified cluster node.
*
* @param id cluster node identifier
* @return 200 OK
*/
@GET
@Path("{id}")
public Response getClusterNode(@PathParam("id") String id) {
......@@ -58,6 +71,13 @@ public class ClusterWebResource extends AbstractWebResource {
return ok(codec(ControllerNode.class).encode(node, this)).build();
}
/**
* Forms cluster of ONOS instances.
* Forms ONOS cluster using the uploaded JSON definition.
*
* @param config cluster definition
* @return 200 OK
*/
@POST
@Path("configuration")
public Response formCluster(InputStream config) throws IOException {
......
......@@ -33,11 +33,17 @@ import java.util.Set;
import static org.onlab.util.Tools.nullIsNotFound;
/**
* REST resource for cluster-wide component configuration.
* Manage component configurations.
*/
@Path("configuration")
public class ComponentConfigWebResource extends AbstractWebResource {
/**
* Get all component configurations.
* Returns collection of all registered component configurations.
*
* @return 200 OK
*/
@GET
public Response getComponentConfigs() {
ComponentConfigService service = get(ComponentConfigService.class);
......@@ -47,6 +53,12 @@ public class ComponentConfigWebResource extends AbstractWebResource {
return ok(root).build();
}
/**
* Get configuration of the specified component.
*
* @param component component name
* @return 200 OK
*/
@GET
@Path("{component}")
public Response getComponentConfigs(@PathParam("component") String component) {
......@@ -65,10 +77,17 @@ public class ComponentConfigWebResource extends AbstractWebResource {
props.forEach(p -> compNode.put(p.name(), p.value()));
}
/**
* Selectively set configuration properties.
* Sets only the properties present in the JSON request.
*
* @param component component name
* @return 200 OK
*/
@POST
@Path("{component}")
public Response setComponentConfigs(@PathParam("component") String component,
InputStream request) throws IOException {
public Response setConfigs(@PathParam("component") String component,
InputStream request) throws IOException {
ComponentConfigService service = get(ComponentConfigService.class);
ObjectNode props = (ObjectNode) mapper().readTree(request);
props.fieldNames().forEachRemaining(k -> service.setProperty(component, k,
......@@ -76,10 +95,17 @@ public class ComponentConfigWebResource extends AbstractWebResource {
return Response.noContent().build();
}
/**
* Selectively clear configuration properties.
* Clears only the properties present in the JSON request.
*
* @param component component name
* @return 200 OK
*/
@DELETE
@Path("{component}")
public Response unsetComponentConfigs(@PathParam("component") String component,
InputStream request) throws IOException {
public Response unsetConfigs(@PathParam("component") String component,
InputStream request) throws IOException {
ComponentConfigService service = get(ComponentConfigService.class);
ObjectNode props = (ObjectNode) mapper().readTree(request);
props.fieldNames().forEachRemaining(k -> service.unsetProperty(component, k));
......
......@@ -37,14 +37,20 @@ import java.io.InputStream;
import static javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR;
/**
* Resource that acts as an ancillary provider for uploading pre-configured
* devices, ports and links.
* Inject devices, ports, links and end-station hosts.
*/
@Path("config")
public class ConfigWebResource extends BaseResource {
private static Logger log = LoggerFactory.getLogger(ConfigWebResource.class);
/**
* Upload device, port, link and host data.
*
* @param input JSON blob
* @return 200 OK
* @throws IOException
*/
@POST
@Path("topology")
@Consumes(MediaType.APPLICATION_JSON)
......
......@@ -34,19 +34,32 @@ import static org.onlab.util.Tools.nullIsNotFound;
import static org.onosproject.net.DeviceId.deviceId;
/**
* REST resource for interacting with the inventory of infrastructure devices.
* Manage inventory of infrastructure devices.
*/
@Path("devices")
public class DevicesWebResource extends AbstractWebResource {
public static final String DEVICE_NOT_FOUND = "Device is not found";
/**
* Get all infrastructure devices.
* Returns array of all discovered infrastructure devices.
*
* @return 200 OK
*/
@GET
public Response getDevices() {
Iterable<Device> devices = get(DeviceService.class).getDevices();
return ok(encodeArray(Device.class, "devices", devices)).build();
}
/**
* Get details of infrastructure device.
* Returns details of the specified infrastructure device.
*
* @param id device identifier
* @return 200 OK
*/
@GET
@Path("{id}")
public Response getDevice(@PathParam("id") String id) {
......@@ -55,6 +68,14 @@ public class DevicesWebResource extends AbstractWebResource {
return ok(codec(Device.class).encode(device, this)).build();
}
/**
* Remove infrastructure device.
* Administratively deletes the specified device from the inventory of
* known devices.
*
* @param id device identifier
* @return 200 OK
*/
@DELETE
@Path("{id}")
public Response removeDevice(@PathParam("id") String id) {
......@@ -64,6 +85,13 @@ public class DevicesWebResource extends AbstractWebResource {
return ok(codec(Device.class).encode(device, this)).build();
}
/**
* Get ports of infrastructure device.
* Returns details of the specified infrastructure device.
*
* @param id device identifier
* @return 200 OK
*/
@GET
@Path("{id}/ports")
public Response getDevicePorts(@PathParam("id") String id) {
......
......@@ -45,7 +45,7 @@ import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
/**
* REST resource for interacting with the inventory of flows.
* Query and program flow rules.
*/
@Path("flows")
......@@ -57,14 +57,13 @@ public class FlowsWebResource extends AbstractWebResource {
final ArrayNode flowsNode = root.putArray("flows");
/**
* Gets an array containing all the intents in the system.
* Get all flow entries. Returns array of all flow rules in the system.
*
* @return array of all the intents in the system
*/
@GET
@Produces(MediaType.APPLICATION_JSON)
public Response getFlows() {
final Iterable<Device> devices = get(DeviceService.class).getDevices();
for (final Device device : devices) {
final Iterable<FlowEntry> deviceEntries = service.getFlowEntries(device.id());
......@@ -79,9 +78,10 @@ public class FlowsWebResource extends AbstractWebResource {
}
/**
* Gets the flows for a device, where the device is specified by Id.
* Get flow entries of a device. Returns array of all flow rules for the
* specified device.
*
* @param deviceId Id of device to look up
* @param deviceId device identifier
* @return flow data as an array
*/
@GET
......@@ -101,10 +101,11 @@ public class FlowsWebResource extends AbstractWebResource {
}
/**
* Gets the flows for a device, where the device is specified by Id.
* Get flow rule. Returns the flow entry specified by the device id and
* flow rule id.
*
* @param deviceId Id of device to look up
* @param flowId Id of flow to look up
* @param deviceId device identifier
* @param flowId flow rule identifier
* @return flow data as an array
*/
@GET
......@@ -127,10 +128,11 @@ public class FlowsWebResource extends AbstractWebResource {
}
/**
* Creates a flow rule from a POST of a JSON string and attempts to apply it.
* Create new flow rule. Creates and installs a new flow rule for the
* specified device.
*
* @param deviceId device identifier
* @param stream input JSON
* @param stream flow rule JSON
* @return status of the request - CREATED if the JSON is correct,
* BAD_REQUEST if the JSON is invalid
*/
......@@ -163,16 +165,16 @@ public class FlowsWebResource extends AbstractWebResource {
}
/**
* Removes the flows for a given device with the given flow id.
* Remove flow rule. Removes the specified flow rule.
*
* @param deviceId Id of device to look up
* @param flowId Id of flow to look up
* @param deviceId device identifier
* @param flowId flow rule identifier
*/
@DELETE
@Produces(MediaType.APPLICATION_JSON)
@Path("{deviceId}/{flowId}")
public void deleteFlowByDeviceIdAndFlowId(@PathParam("deviceId") String deviceId,
@PathParam("flowId") long flowId) {
@PathParam("flowId") long flowId) {
final Iterable<FlowEntry> deviceEntries =
service.getFlowEntries(DeviceId.deviceId(deviceId));
......
......@@ -56,7 +56,7 @@ import static org.onlab.util.Tools.nullIsNotFound;
import static org.onosproject.net.HostId.hostId;
/**
* REST resource for interacting with the inventory of hosts.
* Manage inventory of end-station hosts.
*/
@Path("hosts")
public class HostsWebResource extends AbstractWebResource {
......@@ -65,6 +65,12 @@ public class HostsWebResource extends AbstractWebResource {
UriInfo uriInfo;
public static final String HOST_NOT_FOUND = "Host is not found";
/**
* Get all end-station hosts.
* Returns array of all known end-station hosts.
*
* @return 200 OK
*/
@GET
@Produces(MediaType.APPLICATION_JSON)
public Response getHosts() {
......@@ -73,6 +79,13 @@ public class HostsWebResource extends AbstractWebResource {
return ok(root).build();
}
/**
* Get details of end-station host.
* Returns detailed properties of the specified end-station host.
*
* @param id host identifier
* @return 200 OK
*/
@GET
@Produces(MediaType.APPLICATION_JSON)
@Path("{id}")
......@@ -83,6 +96,14 @@ public class HostsWebResource extends AbstractWebResource {
return ok(root).build();
}
/**
* Get details of end-station host with MAC/VLAN.
* Returns detailed properties of the specified end-station host.
*
* @param mac host MAC address
* @param vlan host VLAN identifier
* @return 200 OK
*/
@GET
@Produces(MediaType.APPLICATION_JSON)
@Path("{mac}/{vlan}")
......@@ -157,6 +178,7 @@ public class HostsWebResource extends AbstractWebResource {
/**
* Creates and adds new host based on given data and returns its host ID.
*
* @param node JsonNode containing host information
* @return host ID of new host created
*/
......
......@@ -55,9 +55,8 @@ import static org.onosproject.net.intent.IntentState.WITHDRAWN;
import static org.slf4j.LoggerFactory.getLogger;
/**
* REST resource for interacting with the inventory of intents.
* Query, submit and withdraw network intents.
*/
@Path("intents")
public class IntentsWebResource extends AbstractWebResource {
@Context
......@@ -69,7 +68,8 @@ public class IntentsWebResource extends AbstractWebResource {
public static final String INTENT_NOT_FOUND = "Intent is not found";
/**
* Gets an array containing all the intents in the system.
* Get all intents.
* Returns array containing all the intents in the system.
*
* @return array of all the intents in the system
*/
......@@ -82,10 +82,11 @@ public class IntentsWebResource extends AbstractWebResource {
}
/**
* Gets a single intent by Id.
* Get intent by application and key.
* Returns details of the specified intent.
*
* @param appId the Application ID
* @param key the Intent key value to look up
* @param appId application identifier
* @param key intent key
* @return intent data
*/
@GET
......@@ -125,31 +126,61 @@ public class IntentsWebResource extends AbstractWebResource {
@Override
public void event(IntentEvent event) {
if (Objects.equals(event.subject().key(), key) &&
(event.type() == IntentEvent.Type.WITHDRAWN ||
event.type() == IntentEvent.Type.FAILED)) {
(event.type() == IntentEvent.Type.WITHDRAWN ||
event.type() == IntentEvent.Type.FAILED)) {
latch.countDown();
}
}
}
/**
* Uninstalls a single intent by Id.
* Submit a new intent.
* Creates and submits intent from the JSON request.
*
* @param stream input JSON
* @return status of the request - CREATED if the JSON is correct,
* BAD_REQUEST if the JSON is invalid
*/
@POST
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Response createIntent(InputStream stream) {
try {
IntentService service = get(IntentService.class);
ObjectNode root = (ObjectNode) mapper().readTree(stream);
Intent intent = codec(Intent.class).decode(root, this);
service.submit(intent);
UriBuilder locationBuilder = uriInfo.getBaseUriBuilder()
.path("intents")
.path(Short.toString(intent.appId().id()))
.path(Long.toString(intent.id().fingerprint()));
return Response
.created(locationBuilder.build())
.build();
} catch (IOException ioe) {
throw new IllegalArgumentException(ioe);
}
}
/**
* Withdraw intent.
* Withdraws the specified intent from the system.
*
* @param appId the Application ID
* @param keyString the Intent key value to look up
* @param appId application identifier
* @param key intent key
*/
@DELETE
@Path("{appId}/{key}")
public void deleteIntentById(@PathParam("appId") String appId,
@PathParam("key") String keyString) {
@PathParam("key") String key) {
final ApplicationId app = get(CoreService.class).getAppId(appId);
Intent intent = get(IntentService.class).getIntent(Key.of(keyString, app));
Intent intent = get(IntentService.class).getIntent(Key.of(key, app));
IntentService service = get(IntentService.class);
if (intent == null) {
intent = service
.getIntent(Key.of(Long.decode(keyString), app));
.getIntent(Key.of(Long.decode(key), app));
}
if (intent == null) {
// No such intent. REST standards recommend a positive status code
......@@ -158,12 +189,12 @@ public class IntentsWebResource extends AbstractWebResource {
}
Key key = intent.key();
Key k = intent.key();
// set up latch and listener to track uninstall progress
CountDownLatch latch = new CountDownLatch(1);
IntentListener listener = new DeleteListener(key, latch);
IntentListener listener = new DeleteListener(k, latch);
service.addListener(listener);
try {
......@@ -173,10 +204,10 @@ public class IntentsWebResource extends AbstractWebResource {
try {
latch.await(WITHDRAW_EVENT_TIMEOUT_SECONDS, TimeUnit.SECONDS);
} catch (InterruptedException e) {
log.info("REST Delete operation timed out waiting for intent {}", key);
log.info("REST Delete operation timed out waiting for intent {}", k);
}
// double check the state
IntentState state = service.getIntentState(key);
IntentState state = service.getIntentState(k);
if (state == WITHDRAWN || state == FAILED) {
service.purge(intent);
}
......@@ -187,32 +218,4 @@ public class IntentsWebResource extends AbstractWebResource {
}
}
/**
* Creates an intent from a POST of a JSON string and attempts to apply it.
*
* @param stream input JSON
* @return status of the request - CREATED if the JSON is correct,
* BAD_REQUEST if the JSON is invalid
*/
@POST
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Response createIntent(InputStream stream) {
try {
IntentService service = get(IntentService.class);
ObjectNode root = (ObjectNode) mapper().readTree(stream);
Intent intent = codec(Intent.class).decode(root, this);
service.submit(intent);
UriBuilder locationBuilder = uriInfo.getBaseUriBuilder()
.path("intents")
.path(Short.toString(intent.appId().id()))
.path(Long.toString(intent.id().fingerprint()));
return Response
.created(locationBuilder.build())
.build();
} catch (IOException ioe) {
throw new IllegalArgumentException(ioe);
}
}
}
......
......@@ -30,7 +30,7 @@ import static org.onosproject.net.DeviceId.deviceId;
import static org.onosproject.net.PortNumber.portNumber;
/**
* REST resource for interacting with the inventory of infrastructure links.
* Manage inventory of infrastructure links.
*/
@Path("links")
public class LinksWebResource extends AbstractWebResource {
......@@ -41,6 +41,15 @@ public class LinksWebResource extends AbstractWebResource {
EGRESS
}
/**
* Get infrastructure links.
* Returns array of all links, or links for the specified device or port.
*
* @param deviceId (optional) device identifier
* @param port (optional) port number
* @param direction (optional) direction qualifier
* @return 200 OK
*/
@GET
public Response getLinks(@QueryParam("device") String deviceId,
@QueryParam("port") String port,
......
......@@ -33,13 +33,13 @@ import java.io.IOException;
import java.io.InputStream;
/**
* REST resource for injecting and retrieving common network configuration.
* Manage network configurations.
*/
@Path("network/configuration")
public class NetworkConfigWebResource extends AbstractWebResource {
/**
* Returns entire network configuration base.
* Get entire network configuration base.
*
* @return network configuration JSON
*/
......@@ -55,7 +55,7 @@ public class NetworkConfigWebResource extends AbstractWebResource {
}
/**
* Returns the network configuration for the specified subject class.
* Get all network configuration for a subject class.
*
* @param subjectKey subject class key
* @return network configuration JSON
......@@ -72,7 +72,7 @@ public class NetworkConfigWebResource extends AbstractWebResource {
}
/**
* Returns the network configuration for the specified subject.
* Get all network configuration for a subject.
*
* @param subjectKey subject class key
* @param subject subject key
......@@ -87,13 +87,12 @@ public class NetworkConfigWebResource extends AbstractWebResource {
NetworkConfigService service = get(NetworkConfigService.class);
ObjectNode root = mapper().createObjectNode();
produceSubjectJson(service, root,
service.getSubjectFactory(subjectKey).createSubject(subject));
service.getSubjectFactory(subjectKey).createSubject(subject));
return ok(root).build();
}
/**
* Returns the network configuration for the specified subject and given
* configuration class.
* Get specific network configuration for a subject.
*
* @param subjectKey subject class key
* @param subject subject key
......@@ -126,7 +125,7 @@ public class NetworkConfigWebResource extends AbstractWebResource {
/**
* Uploads network configuration in bulk.
* Upload bulk network configuration.
*
* @param request network configuration JSON rooted at the top node
* @throws IOException if unable to parse the request
......@@ -140,12 +139,12 @@ public class NetworkConfigWebResource extends AbstractWebResource {
ObjectNode root = (ObjectNode) mapper().readTree(request);
root.fieldNames()
.forEachRemaining(sk -> consumeJson(service, (ObjectNode) root.path(sk),
service.getSubjectFactory(sk)));
service.getSubjectFactory(sk)));
return Response.ok().build();
}
/**
* Uploads network configuration for the specified subject class.
* Upload multiple network configurations for a subject class.
*
* @param subjectKey subject class key
* @param request network configuration JSON rooted at the top node
......@@ -165,7 +164,7 @@ public class NetworkConfigWebResource extends AbstractWebResource {
}
/**
* Uploads network configuration for the specified subject.
* Upload mutliple network configurations for a subject.
*
* @param subjectKey subject class key
* @param subject subject key
......@@ -183,14 +182,13 @@ public class NetworkConfigWebResource extends AbstractWebResource {
NetworkConfigService service = get(NetworkConfigService.class);
ObjectNode root = (ObjectNode) mapper().readTree(request);
consumeSubjectJson(service, root,
service.getSubjectFactory(subjectKey).createSubject(subject),
subjectKey);
service.getSubjectFactory(subjectKey).createSubject(subject),
subjectKey);
return Response.ok().build();
}
/**
* Uploads network configuration for the specified subject and given
* configuration class.
* Upload specific network configuration for a subject.
*
* @param subjectKey subject class key
* @param subject subject key
......@@ -210,16 +208,16 @@ public class NetworkConfigWebResource extends AbstractWebResource {
NetworkConfigService service = get(NetworkConfigService.class);
ObjectNode root = (ObjectNode) mapper().readTree(request);
service.applyConfig(service.getSubjectFactory(subjectKey).createSubject(subject),
service.getConfigClass(subjectKey, configKey), root);
service.getConfigClass(subjectKey, configKey), root);
return Response.ok().build();
}
private void consumeJson(NetworkConfigService service, ObjectNode classNode,
SubjectFactory subjectFactory) {
classNode.fieldNames().forEachRemaining(s ->
consumeSubjectJson(service, (ObjectNode) classNode.path(s),
subjectFactory.createSubject(s),
subjectFactory.subjectKey()));
consumeSubjectJson(service, (ObjectNode) classNode.path(s),
subjectFactory.createSubject(s),
subjectFactory.subjectKey()));
}
private void consumeSubjectJson(NetworkConfigService service,
......@@ -227,84 +225,81 @@ public class NetworkConfigWebResource extends AbstractWebResource {
String subjectKey) {
subjectNode.fieldNames().forEachRemaining(c ->
service.applyConfig(subject, service.getConfigClass(subjectKey, c),
(ObjectNode) subjectNode.path(c)));
(ObjectNode) subjectNode.path(c)));
}
/**
* Clears network configuration for the specified subject.
* Clear entire network configuration base.
*
* @param subjectKey subject class key
* @param subject subject key
* @return empty response
*/
@DELETE
@Path("{subjectKey}/{subject}")
@SuppressWarnings("unchecked")
public Response delete(@PathParam("subjectKey") String subjectKey,
@PathParam("subject") String subject) {
public Response delete() {
NetworkConfigService service = get(NetworkConfigService.class);
Object s = service.getSubjectFactory(subjectKey).createSubject(subject);
service.getConfigs(s).forEach(c -> service.removeConfig(s, c.getClass()));
service.getSubjectClasses()
.forEach(subjectClass -> service.getSubjects(subjectClass)
.forEach(subject -> service.getConfigs(subject)
.forEach(config -> service
.removeConfig(subject, config.getClass()))));
return Response.ok().build();
}
/**
* Clears network configuration for the specified subject and given
* configuration class.
* Clear all network configurations for a subject class.
*
* @param subjectKey subject class key
* @param subject subject key
* @param configKey configuration class key
* @return empty response
*/
@DELETE
@Path("{subjectKey}/{subject}/{configKey}")
@Path("{subjectKey}")
@SuppressWarnings("unchecked")
public Response delete(@PathParam("subjectKey") String subjectKey,
@PathParam("subject") String subject,
@PathParam("configKey") String configKey) {
public Response delete(@PathParam("subjectKey") String subjectKey) {
NetworkConfigService service = get(NetworkConfigService.class);
service.removeConfig(service.getSubjectFactory(subjectKey).createSubject(subject),
service.getConfigClass(subjectKey, configKey));
service.getSubjects(service.getSubjectFactory(subjectKey).getClass())
.forEach(subject -> service.getConfigs(subject)
.forEach(config -> service
.removeConfig(subject, config.getClass())));
return Response.ok().build();
}
/**
* Clears all network configurations.
* Clear all network configurations for a subject.
*
* @param subjectKey subject class key
* @param subject subject key
* @return empty response
*/
@DELETE
@Path("{subjectKey}/{subject}")
@SuppressWarnings("unchecked")
public Response delete() {
public Response delete(@PathParam("subjectKey") String subjectKey,
@PathParam("subject") String subject) {
NetworkConfigService service = get(NetworkConfigService.class);
service.getSubjectClasses()
.forEach(subjectClass -> service.getSubjects(subjectClass)
.forEach(subject -> service.getConfigs(subject)
.forEach(config -> service
.removeConfig(subject, config.getClass()))));
Object s = service.getSubjectFactory(subjectKey).createSubject(subject);
service.getConfigs(s).forEach(c -> service.removeConfig(s, c.getClass()));
return Response.ok().build();
}
// TODO: this one below doesn't work correctly
/**
* Clears network configuration for the specified subject class.
* Clear specific network configuration for a subject.
*
* @param subjectKey subject class key
* @param subject subject key
* @param configKey configuration class key
* @return empty response
*/
@DELETE
@Path("{subjectKey}/")
@Path("{subjectKey}/{subject}/{configKey}")
@SuppressWarnings("unchecked")
public Response delete(@PathParam("subjectKey") String subjectKey) {
public Response delete(@PathParam("subjectKey") String subjectKey,
@PathParam("subject") String subject,
@PathParam("configKey") String configKey) {
NetworkConfigService service = get(NetworkConfigService.class);
service.getSubjects(service.getSubjectFactory(subjectKey).getClass())
.forEach(subject -> service.getConfigs(subject)
.forEach(config -> service
.removeConfig(subject, config.getClass())));
service.removeConfig(service.getSubjectFactory(subjectKey).createSubject(subject),
service.getConfigClass(subjectKey, configKey));
return Response.ok().build();
}
}
......
......@@ -33,7 +33,7 @@ import com.fasterxml.jackson.databind.node.ObjectNode;
import org.onosproject.rest.AbstractWebResource;
/**
* REST resource for interacting with path calculations.
* Compute paths in the network graph.
*/
@Path("paths")
public class PathsWebResource extends AbstractWebResource {
......@@ -46,19 +46,15 @@ public class PathsWebResource extends AbstractWebResource {
* @return HostId if the id is valid, null otherwise
*/
private HostId isHostId(String id) {
if (!id.matches("..:..:..:..:..:../.*")) {
return null;
}
return HostId.hostId(id);
return id.matches("..:..:..:..:..:../.*") ? HostId.hostId(id) : null;
}
/**
* Gets the paths between two elements.
* Get all shortest paths between any two hosts or devices.
* Returns array of all shortest paths between any two elements.
*
* @param src source
* @param dst destination
* @param src source identifier
* @param dst destination identifier
* @return path data
*/
@GET
......@@ -81,10 +77,8 @@ public class PathsWebResource extends AbstractWebResource {
dstElement = DeviceId.deviceId(dst);
}
Set<org.onosproject.net.Path> paths =
pathService.getPaths(srcElement, dstElement);
ObjectNode root =
encodeArray(org.onosproject.net.Path.class, "paths", paths);
Set<org.onosproject.net.Path> paths = pathService.getPaths(srcElement, dstElement);
ObjectNode root = encodeArray(org.onosproject.net.Path.class, "paths", paths);
return ok(root).build();
}
......
......@@ -44,7 +44,7 @@ import static org.onosproject.net.DeviceId.deviceId;
import static org.onosproject.net.PortNumber.portNumber;
/**
* Statistics REST APIs.
* Query flow statistics.
*/
@Path("statistics")
public class StatisticsWebResource extends AbstractWebResource {
......@@ -52,7 +52,7 @@ public class StatisticsWebResource extends AbstractWebResource {
UriInfo uriInfo;
/**
* Gets the Load statistics for all links, or for a specific link.
* Get load statistics for all links or for a specific link.
*
* @param deviceId (optional) device ID for a specific link
* @param port (optional) port number for a specified link
......@@ -77,7 +77,6 @@ public class StatisticsWebResource extends AbstractWebResource {
JsonCodec<Load> loadCodec = codec(Load.class);
StatisticService statsService = getService(StatisticService.class);
StreamSupport.stream(Spliterators.spliteratorUnknownSize(
links.iterator(), Spliterator.ORDERED), false)
.forEach(link -> {
......
......@@ -39,16 +39,15 @@ import java.util.List;
import static org.onlab.util.Tools.nullIsNotFound;
/**
* REST resource for interacting with the inventory of clusters.
* Query network topology graph and its components.
*/
@Path("topology")
public class TopologyWebResource extends AbstractWebResource {
public static final String CLUSTER_NOT_FOUND = "Cluster is not found";
/**
* Gets the topology overview for a REST GET operation.
* Get overview of current topology.
*
* @return topology overview
*/
......@@ -61,7 +60,7 @@ public class TopologyWebResource extends AbstractWebResource {
}
/**
* Gets the topology clusters overview for a REST GET operation.
* Get overview of topology SCCs.
*
* @return topology clusters overview
*/
......@@ -77,7 +76,7 @@ public class TopologyWebResource extends AbstractWebResource {
}
/**
* Gets details for a topology cluster for a REST GET operation.
* Get details of a specific SCC.
*
* @param clusterId id of the cluster to query
* @return topology cluster details
......@@ -100,7 +99,7 @@ public class TopologyWebResource extends AbstractWebResource {
}
/**
* Gets devices for a topology cluster for a REST GET operation.
* Get devices in a specific SCC.
*
* @param clusterId id of the cluster to query
* @return topology cluster devices
......@@ -123,7 +122,7 @@ public class TopologyWebResource extends AbstractWebResource {
}
/**
* Gets links for a topology cluster for a REST GET operation.
* Get links in specific SCC.
*
* @param clusterId id of the cluster to query
* @return topology cluster links
......@@ -171,18 +170,16 @@ public class TopologyWebResource extends AbstractWebResource {
}
/**
* Gets the broadcast flag of a connect point for a REST GET operation.
* Test if a connect point is in broadcast set.
*
* @param connectPointString string representation of the connect point to query.
* Format is deviceid:portnumber
* @param connectPointString deviceid:portnumber
* @return JSON representation of true if the connect point is broadcast,
* false otherwise
*/
@GET
@Produces(MediaType.APPLICATION_JSON)
@Path("broadcast/{connectPoint}")
public Response getConnectPointBroadcast(
@PathParam("connectPoint") String connectPointString) {
public Response getConnectPointBroadcast(@PathParam("connectPoint") String connectPointString) {
Topology topology = get(TopologyService.class).currentTopology();
DeviceId deviceId = DeviceId.deviceId(getDeviceId(connectPointString));
......@@ -197,18 +194,16 @@ public class TopologyWebResource extends AbstractWebResource {
}
/**
* Gets the infrastructure flag of a connect point for a REST GET operation.
* Test if a connect point is infrastructure or edge.
*
* @param connectPointString string representation of the connect point to query.
* Format is deviceid:portnumber
* @param connectPointString deviceid:portnumber
* @return JSON representation of true if the connect point is broadcast,
* false otherwise
*/
@GET
@Produces(MediaType.APPLICATION_JSON)
@Path("infrastructure/{connectPoint}")
public Response getConnectPointInfrastructure(
@PathParam("connectPoint") String connectPointString) {
public Response getConnectPointInfrastructure(@PathParam("connectPoint") String connectPointString) {
Topology topology = get(TopologyService.class).currentTopology();
DeviceId deviceId = DeviceId.deviceId(getDeviceId(connectPointString));
......
This diff is collapsed. Click to expand it.
/* http://meyerweb.com/eric/tools/css/reset/ v2.0 | 20110126 */
html,
body,
div,
span,
applet,
object,
iframe,
h1,
h2,
h3,
h4,
h5,
h6,
p,
blockquote,
pre,
a,
abbr,
acronym,
address,
big,
cite,
code,
del,
dfn,
em,
img,
ins,
kbd,
q,
s,
samp,
small,
strike,
strong,
sub,
sup,
tt,
var,
b,
u,
i,
center,
dl,
dt,
dd,
ol,
ul,
li,
fieldset,
form,
label,
legend,
table,
caption,
tbody,
tfoot,
thead,
tr,
th,
td,
article,
aside,
canvas,
details,
embed,
figure,
figcaption,
footer,
header,
hgroup,
menu,
nav,
output,
ruby,
section,
summary,
time,
mark,
audio,
video {
margin: 0;
padding: 0;
border: 0;
font-size: 100%;
font: inherit;
vertical-align: baseline;
}
/* HTML5 display-role reset for older browsers */
article,
aside,
details,
figcaption,
figure,
footer,
header,
hgroup,
menu,
nav,
section {
display: block;
}
body {
line-height: 1;
}
ol,
ul {
list-style: none;
}
blockquote,
q {
quotes: none;
}
blockquote:before,
blockquote:after,
q:before,
q:after {
content: '';
content: none;
}
table {
border-collapse: collapse;
border-spacing: 0;
}
This diff is collapsed. Click to expand it.
<!-- Adapted from Swagger UI -->
<!DOCTYPE html>
<html>
<head>
<title>ONOS API Docs</title>
<link rel="icon" type="image/png" href="images/onos-logo.png"/>
<link href='css/typography.css' media='screen' rel='stylesheet' type='text/css'/>
<link href='css/reset.css' media='screen' rel='stylesheet' type='text/css'/>
<link href='css/screen.css' media='screen' rel='stylesheet' type='text/css'/>
<link href='css/reset.css' media='print' rel='stylesheet' type='text/css'/>
<link href='css/print.css' media='print' rel='stylesheet' type='text/css'/>
<script src='lib/jquery-1.8.0.min.js' type='text/javascript'></script>
<script src='lib/jquery.slideto.min.js' type='text/javascript'></script>
<script src='lib/jquery.wiggle.min.js' type='text/javascript'></script>
<script src='lib/jquery.ba-bbq.min.js' type='text/javascript'></script>
<script src='lib/handlebars-2.0.0.js' type='text/javascript'></script>
<script src='lib/underscore-min.js' type='text/javascript'></script>
<script src='lib/backbone-min.js' type='text/javascript'></script>
<script src='swagger-ui.js' type='text/javascript'></script>
<script src='lib/highlight.7.3.pack.js' type='text/javascript'></script>
<script src='lib/marked.js' type='text/javascript'></script>
<script src='lib/swagger-oauth.js' type='text/javascript'></script>
<script type="text/javascript">
$(function () {
var url = window.location.search.match(/url=([^&]+)/);
if (url && url.length > 1) {
url = decodeURIComponent(url[1]);
} else {
url = "/onos/v1/docs/apis/onos/v1/swagger.json";
}
window.swaggerUi = new SwaggerUi({
url: url,
dom_id: "swagger-ui-container",
supportedSubmitMethods: ['get', 'post', 'put', 'delete', 'patch'],
onComplete: function(swaggerApi, swaggerUi){
if(typeof initOAuth == "function") {
initOAuth({
clientId: "your-client-id",
realm: "your-realms",
appName: "your-app-name"
});
}
$('pre code').each(function(i, e) {
hljs.highlightBlock(e)
});
addApiKeyAuthorization();
},
onFailure: function(data) {
log("Unable to Load SwaggerUI");
},
docExpansion: "none",
apisSorter: "alpha",
showRequestHeaders: false
});
function addApiKeyAuthorization(){
var key = encodeURIComponent($('#input_apiKey')[0].value);
if(key && key.trim() != "") {
var apiKeyAuth = new SwaggerClient.ApiKeyAuthorization("api_key", key, "query");
window.swaggerUi.api.clientAuthorizations.add("api_key", apiKeyAuth);
log("added key " + key);
}
}
$('#input_apiKey').change(addApiKeyAuthorization);
// if you have an apiKey you would like to pre-populate on the page for demonstration purposes...
/*
var apiKey = "myApiKeyXXXX123456789";
$('#input_apiKey').val(apiKey);
*/
window.swaggerUi.load();
function log() {
if ('console' in window) {
console.log.apply(console, arguments);
}
}
});
function selectApi() {
$('#input_baseUrl').val('/onos/v1/docs/apis' + $('select').val() + '/swagger.json');
$('#explore').click();
}
</script>
</head>
<body class="swagger-section">
<div id='header'>
<div class="swagger-ui-wrap">
<a id="logo" href="#"></a>
<form id='api_selector'>
<div class='input' id='selector'>
<select onchange="selectApi()">
<!-- {API-START} -->
<option value="v1">ONOS v1 Core</option>
<option value="foo">ONOS Foo app</option>
<option value="bar">ONOS Bar app</option>
<!-- {API-END} -->
</select>
</div>
<div class='input'><input placeholder="http://example.com/api" id="input_baseUrl" name="baseUrl" type="text"/></div>
<div class='input'><input placeholder="api_key" id="input_apiKey" name="apiKey" type="text"/></div>
<div class='input'><a id="explore" href="#">Explore</a></div>
</form>
</div>
</div>
<div id="message-bar" class="swagger-ui-wrap">&nbsp;</div>
<div id="swagger-ui-container" class="swagger-ui-wrap"></div>
<div id="footer" class="swagger-ui-wrap"><a href="http://swagger.io/swagger-ui/">Swagger UI</a> adaptation</div>
</body>
</html>
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
var hljs=new function(){function l(o){return o.replace(/&/gm,"&amp;").replace(/</gm,"&lt;").replace(/>/gm,"&gt;")}function b(p){for(var o=p.firstChild;o;o=o.nextSibling){if(o.nodeName=="CODE"){return o}if(!(o.nodeType==3&&o.nodeValue.match(/\s+/))){break}}}function h(p,o){return Array.prototype.map.call(p.childNodes,function(q){if(q.nodeType==3){return o?q.nodeValue.replace(/\n/g,""):q.nodeValue}if(q.nodeName=="BR"){return"\n"}return h(q,o)}).join("")}function a(q){var p=(q.className+" "+q.parentNode.className).split(/\s+/);p=p.map(function(r){return r.replace(/^language-/,"")});for(var o=0;o<p.length;o++){if(e[p[o]]||p[o]=="no-highlight"){return p[o]}}}function c(q){var o=[];(function p(r,s){for(var t=r.firstChild;t;t=t.nextSibling){if(t.nodeType==3){s+=t.nodeValue.length}else{if(t.nodeName=="BR"){s+=1}else{if(t.nodeType==1){o.push({event:"start",offset:s,node:t});s=p(t,s);o.push({event:"stop",offset:s,node:t})}}}}return s})(q,0);return o}function j(x,v,w){var p=0;var y="";var r=[];function t(){if(x.length&&v.length){if(x[0].offset!=v[0].offset){return(x[0].offset<v[0].offset)?x:v}else{return v[0].event=="start"?x:v}}else{return x.length?x:v}}function s(A){function z(B){return" "+B.nodeName+'="'+l(B.value)+'"'}return"<"+A.nodeName+Array.prototype.map.call(A.attributes,z).join("")+">"}while(x.length||v.length){var u=t().splice(0,1)[0];y+=l(w.substr(p,u.offset-p));p=u.offset;if(u.event=="start"){y+=s(u.node);r.push(u.node)}else{if(u.event=="stop"){var o,q=r.length;do{q--;o=r[q];y+=("</"+o.nodeName.toLowerCase()+">")}while(o!=u.node);r.splice(q,1);while(q<r.length){y+=s(r[q]);q++}}}}return y+l(w.substr(p))}function f(q){function o(s,r){return RegExp(s,"m"+(q.cI?"i":"")+(r?"g":""))}function p(y,w){if(y.compiled){return}y.compiled=true;var s=[];if(y.k){var r={};function z(A,t){t.split(" ").forEach(function(B){var C=B.split("|");r[C[0]]=[A,C[1]?Number(C[1]):1];s.push(C[0])})}y.lR=o(y.l||hljs.IR,true);if(typeof y.k=="string"){z("keyword",y.k)}else{for(var x in y.k){if(!y.k.hasOwnProperty(x)){continue}z(x,y.k[x])}}y.k=r}if(w){if(y.bWK){y.b="\\b("+s.join("|")+")\\s"}y.bR=o(y.b?y.b:"\\B|\\b");if(!y.e&&!y.eW){y.e="\\B|\\b"}if(y.e){y.eR=o(y.e)}y.tE=y.e||"";if(y.eW&&w.tE){y.tE+=(y.e?"|":"")+w.tE}}if(y.i){y.iR=o(y.i)}if(y.r===undefined){y.r=1}if(!y.c){y.c=[]}for(var v=0;v<y.c.length;v++){if(y.c[v]=="self"){y.c[v]=y}p(y.c[v],y)}if(y.starts){p(y.starts,w)}var u=[];for(var v=0;v<y.c.length;v++){u.push(y.c[v].b)}if(y.tE){u.push(y.tE)}if(y.i){u.push(y.i)}y.t=u.length?o(u.join("|"),true):{exec:function(t){return null}}}p(q)}function d(D,E){function o(r,M){for(var L=0;L<M.c.length;L++){var K=M.c[L].bR.exec(r);if(K&&K.index==0){return M.c[L]}}}function s(K,r){if(K.e&&K.eR.test(r)){return K}if(K.eW){return s(K.parent,r)}}function t(r,K){return K.i&&K.iR.test(r)}function y(L,r){var K=F.cI?r[0].toLowerCase():r[0];return L.k.hasOwnProperty(K)&&L.k[K]}function G(){var K=l(w);if(!A.k){return K}var r="";var N=0;A.lR.lastIndex=0;var L=A.lR.exec(K);while(L){r+=K.substr(N,L.index-N);var M=y(A,L);if(M){v+=M[1];r+='<span class="'+M[0]+'">'+L[0]+"</span>"}else{r+=L[0]}N=A.lR.lastIndex;L=A.lR.exec(K)}return r+K.substr(N)}function z(){if(A.sL&&!e[A.sL]){return l(w)}var r=A.sL?d(A.sL,w):g(w);if(A.r>0){v+=r.keyword_count;B+=r.r}return'<span class="'+r.language+'">'+r.value+"</span>"}function J(){return A.sL!==undefined?z():G()}function I(L,r){var K=L.cN?'<span class="'+L.cN+'">':"";if(L.rB){x+=K;w=""}else{if(L.eB){x+=l(r)+K;w=""}else{x+=K;w=r}}A=Object.create(L,{parent:{value:A}});B+=L.r}function C(K,r){w+=K;if(r===undefined){x+=J();return 0}var L=o(r,A);if(L){x+=J();I(L,r);return L.rB?0:r.length}var M=s(A,r);if(M){if(!(M.rE||M.eE)){w+=r}x+=J();do{if(A.cN){x+="</span>"}A=A.parent}while(A!=M.parent);if(M.eE){x+=l(r)}w="";if(M.starts){I(M.starts,"")}return M.rE?0:r.length}if(t(r,A)){throw"Illegal"}w+=r;return r.length||1}var F=e[D];f(F);var A=F;var w="";var B=0;var v=0;var x="";try{var u,q,p=0;while(true){A.t.lastIndex=p;u=A.t.exec(E);if(!u){break}q=C(E.substr(p,u.index-p),u[0]);p=u.index+q}C(E.substr(p));return{r:B,keyword_count:v,value:x,language:D}}catch(H){if(H=="Illegal"){return{r:0,keyword_count:0,value:l(E)}}else{throw H}}}function g(s){var o={keyword_count:0,r:0,value:l(s)};var q=o;for(var p in e){if(!e.hasOwnProperty(p)){continue}var r=d(p,s);r.language=p;if(r.keyword_count+r.r>q.keyword_count+q.r){q=r}if(r.keyword_count+r.r>o.keyword_count+o.r){q=o;o=r}}if(q.language){o.second_best=q}return o}function i(q,p,o){if(p){q=q.replace(/^((<[^>]+>|\t)+)/gm,function(r,v,u,t){return v.replace(/\t/g,p)})}if(o){q=q.replace(/\n/g,"<br>")}return q}function m(r,u,p){var v=h(r,p);var t=a(r);if(t=="no-highlight"){return}var w=t?d(t,v):g(v);t=w.language;var o=c(r);if(o.length){var q=document.createElement("pre");q.innerHTML=w.value;w.value=j(o,c(q),v)}w.value=i(w.value,u,p);var s=r.className;if(!s.match("(\\s|^)(language-)?"+t+"(\\s|$)")){s=s?(s+" "+t):t}r.innerHTML=w.value;r.className=s;r.result={language:t,kw:w.keyword_count,re:w.r};if(w.second_best){r.second_best={language:w.second_best.language,kw:w.second_best.keyword_count,re:w.second_best.r}}}function n(){if(n.called){return}n.called=true;Array.prototype.map.call(document.getElementsByTagName("pre"),b).filter(Boolean).forEach(function(o){m(o,hljs.tabReplace)})}function k(){window.addEventListener("DOMContentLoaded",n,false);window.addEventListener("load",n,false)}var e={};this.LANGUAGES=e;this.highlight=d;this.highlightAuto=g;this.fixMarkup=i;this.highlightBlock=m;this.initHighlighting=n;this.initHighlightingOnLoad=k;this.IR="[a-zA-Z][a-zA-Z0-9_]*";this.UIR="[a-zA-Z_][a-zA-Z0-9_]*";this.NR="\\b\\d+(\\.\\d+)?";this.CNR="(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)";this.BNR="\\b(0b[01]+)";this.RSR="!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|\\.|-|-=|/|/=|:|;|<|<<|<<=|<=|=|==|===|>|>=|>>|>>=|>>>|>>>=|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~";this.BE={b:"\\\\[\\s\\S]",r:0};this.ASM={cN:"string",b:"'",e:"'",i:"\\n",c:[this.BE],r:0};this.QSM={cN:"string",b:'"',e:'"',i:"\\n",c:[this.BE],r:0};this.CLCM={cN:"comment",b:"//",e:"$"};this.CBLCLM={cN:"comment",b:"/\\*",e:"\\*/"};this.HCM={cN:"comment",b:"#",e:"$"};this.NM={cN:"number",b:this.NR,r:0};this.CNM={cN:"number",b:this.CNR,r:0};this.BNM={cN:"number",b:this.BNR,r:0};this.inherit=function(q,r){var o={};for(var p in q){o[p]=q[p]}if(r){for(var p in r){o[p]=r[p]}}return o}}();hljs.LANGUAGES.xml=function(a){var c="[A-Za-z0-9\\._:-]+";var b={eW:true,c:[{cN:"attribute",b:c,r:0},{b:'="',rB:true,e:'"',c:[{cN:"value",b:'"',eW:true}]},{b:"='",rB:true,e:"'",c:[{cN:"value",b:"'",eW:true}]},{b:"=",c:[{cN:"value",b:"[^\\s/>]+"}]}]};return{cI:true,c:[{cN:"pi",b:"<\\?",e:"\\?>",r:10},{cN:"doctype",b:"<!DOCTYPE",e:">",r:10,c:[{b:"\\[",e:"\\]"}]},{cN:"comment",b:"<!--",e:"-->",r:10},{cN:"cdata",b:"<\\!\\[CDATA\\[",e:"\\]\\]>",r:10},{cN:"tag",b:"<style(?=\\s|>|$)",e:">",k:{title:"style"},c:[b],starts:{e:"</style>",rE:true,sL:"css"}},{cN:"tag",b:"<script(?=\\s|>|$)",e:">",k:{title:"script"},c:[b],starts:{e:"<\/script>",rE:true,sL:"javascript"}},{b:"<%",e:"%>",sL:"vbscript"},{cN:"tag",b:"</?",e:"/?>",c:[{cN:"title",b:"[^ />]+"},b]}]}}(hljs);hljs.LANGUAGES.json=function(a){var e={literal:"true false null"};var d=[a.QSM,a.CNM];var c={cN:"value",e:",",eW:true,eE:true,c:d,k:e};var b={b:"{",e:"}",c:[{cN:"attribute",b:'\\s*"',e:'"\\s*:\\s*',eB:true,eE:true,c:[a.BE],i:"\\n",starts:c}],i:"\\S"};var f={b:"\\[",e:"\\]",c:[a.inherit(c,{cN:null})],i:"\\S"};d.splice(d.length,0,b,f);return{c:d,k:e,i:"\\S"}}(hljs);
\ No newline at end of file
This diff is collapsed. Click to expand it.
/*
* jQuery BBQ: Back Button & Query Library - v1.2.1 - 2/17/2010
* http://benalman.com/projects/jquery-bbq-plugin/
*
* Copyright (c) 2010 "Cowboy" Ben Alman
* Dual licensed under the MIT and GPL licenses.
* http://benalman.com/about/license/
*/
(function($,p){var i,m=Array.prototype.slice,r=decodeURIComponent,a=$.param,c,l,v,b=$.bbq=$.bbq||{},q,u,j,e=$.event.special,d="hashchange",A="querystring",D="fragment",y="elemUrlAttr",g="location",k="href",t="src",x=/^.*\?|#.*$/g,w=/^.*\#/,h,C={};function E(F){return typeof F==="string"}function B(G){var F=m.call(arguments,1);return function(){return G.apply(this,F.concat(m.call(arguments)))}}function n(F){return F.replace(/^[^#]*#?(.*)$/,"$1")}function o(F){return F.replace(/(?:^[^?#]*\?([^#]*).*$)?.*/,"$1")}function f(H,M,F,I,G){var O,L,K,N,J;if(I!==i){K=F.match(H?/^([^#]*)\#?(.*)$/:/^([^#?]*)\??([^#]*)(#?.*)/);J=K[3]||"";if(G===2&&E(I)){L=I.replace(H?w:x,"")}else{N=l(K[2]);I=E(I)?l[H?D:A](I):I;L=G===2?I:G===1?$.extend({},I,N):$.extend({},N,I);L=a(L);if(H){L=L.replace(h,r)}}O=K[1]+(H?"#":L||!K[1]?"?":"")+L+J}else{O=M(F!==i?F:p[g][k])}return O}a[A]=B(f,0,o);a[D]=c=B(f,1,n);c.noEscape=function(G){G=G||"";var F=$.map(G.split(""),encodeURIComponent);h=new RegExp(F.join("|"),"g")};c.noEscape(",/");$.deparam=l=function(I,F){var H={},G={"true":!0,"false":!1,"null":null};$.each(I.replace(/\+/g," ").split("&"),function(L,Q){var K=Q.split("="),P=r(K[0]),J,O=H,M=0,R=P.split("]["),N=R.length-1;if(/\[/.test(R[0])&&/\]$/.test(R[N])){R[N]=R[N].replace(/\]$/,"");R=R.shift().split("[").concat(R);N=R.length-1}else{N=0}if(K.length===2){J=r(K[1]);if(F){J=J&&!isNaN(J)?+J:J==="undefined"?i:G[J]!==i?G[J]:J}if(N){for(;M<=N;M++){P=R[M]===""?O.length:R[M];O=O[P]=M<N?O[P]||(R[M+1]&&isNaN(R[M+1])?{}:[]):J}}else{if($.isArray(H[P])){H[P].push(J)}else{if(H[P]!==i){H[P]=[H[P],J]}else{H[P]=J}}}}else{if(P){H[P]=F?i:""}}});return H};function z(H,F,G){if(F===i||typeof F==="boolean"){G=F;F=a[H?D:A]()}else{F=E(F)?F.replace(H?w:x,""):F}return l(F,G)}l[A]=B(z,0);l[D]=v=B(z,1);$[y]||($[y]=function(F){return $.extend(C,F)})({a:k,base:k,iframe:t,img:t,input:t,form:"action",link:k,script:t});j=$[y];function s(I,G,H,F){if(!E(H)&&typeof H!=="object"){F=H;H=G;G=i}return this.each(function(){var L=$(this),J=G||j()[(this.nodeName||"").toLowerCase()]||"",K=J&&L.attr(J)||"";L.attr(J,a[I](K,H,F))})}$.fn[A]=B(s,A);$.fn[D]=B(s,D);b.pushState=q=function(I,F){if(E(I)&&/^#/.test(I)&&F===i){F=2}var H=I!==i,G=c(p[g][k],H?I:{},H?F:2);p[g][k]=G+(/#/.test(G)?"":"#")};b.getState=u=function(F,G){return F===i||typeof F==="boolean"?v(F):v(G)[F]};b.removeState=function(F){var G={};if(F!==i){G=u();$.each($.isArray(F)?F:arguments,function(I,H){delete G[H]})}q(G,2)};e[d]=$.extend(e[d],{add:function(F){var H;function G(J){var I=J[D]=c();J.getState=function(K,L){return K===i||typeof K==="boolean"?l(I,K):l(I,L)[K]};H.apply(this,arguments)}if($.isFunction(F)){H=F;return G}else{H=F.handler;F.handler=G}}})})(jQuery,this);
/*
* jQuery hashchange event - v1.2 - 2/11/2010
* http://benalman.com/projects/jquery-hashchange-plugin/
*
* Copyright (c) 2010 "Cowboy" Ben Alman
* Dual licensed under the MIT and GPL licenses.
* http://benalman.com/about/license/
*/
(function($,i,b){var j,k=$.event.special,c="location",d="hashchange",l="href",f=$.browser,g=document.documentMode,h=f.msie&&(g===b||g<8),e="on"+d in i&&!h;function a(m){m=m||i[c][l];return m.replace(/^[^#]*#?(.*)$/,"$1")}$[d+"Delay"]=100;k[d]=$.extend(k[d],{setup:function(){if(e){return false}$(j.start)},teardown:function(){if(e){return false}$(j.stop)}});j=(function(){var m={},r,n,o,q;function p(){o=q=function(s){return s};if(h){n=$('<iframe src="javascript:0"/>').hide().insertAfter("body")[0].contentWindow;q=function(){return a(n.document[c][l])};o=function(u,s){if(u!==s){var t=n.document;t.open().close();t[c].hash="#"+u}};o(a())}}m.start=function(){if(r){return}var t=a();o||p();(function s(){var v=a(),u=q(t);if(v!==t){o(t=v,u);$(i).trigger(d)}else{if(u!==t){i[c][l]=i[c][l].replace(/#.*/,"")+"#"+u}}r=setTimeout(s,$[d+"Delay"])})()};m.stop=function(){if(!n){r&&clearTimeout(r);r=0}};return m})()})(jQuery,this);
\ No newline at end of file
(function(b){b.fn.slideto=function(a){a=b.extend({slide_duration:"slow",highlight_duration:3E3,highlight:true,highlight_color:"#FFFF99"},a);return this.each(function(){obj=b(this);b("body").animate({scrollTop:obj.offset().top},a.slide_duration,function(){a.highlight&&b.ui.version&&obj.effect("highlight",{color:a.highlight_color},a.highlight_duration)})})}})(jQuery);
/*
jQuery Wiggle
Author: WonderGroup, Jordan Thomas
URL: http://labs.wondergroup.com/demos/mini-ui/index.html
License: MIT (http://en.wikipedia.org/wiki/MIT_License)
*/
jQuery.fn.wiggle=function(o){var d={speed:50,wiggles:3,travel:5,callback:null};var o=jQuery.extend(d,o);return this.each(function(){var cache=this;var wrap=jQuery(this).wrap('<div class="wiggle-wrap"></div>').css("position","relative");var calls=0;for(i=1;i<=o.wiggles;i++){jQuery(this).animate({left:"-="+o.travel},o.speed).animate({left:"+="+o.travel*2},o.speed*2).animate({left:"-="+o.travel},o.speed,function(){calls++;if(jQuery(cache).parent().hasClass('wiggle-wrap')){jQuery(cache).parent().replaceWith(cache);}
if(calls==o.wiggles&&jQuery.isFunction(o.callback)){o.callback();}});}});};
\ No newline at end of file
This diff is collapsed. Click to expand it.
var appName;
var popupMask;
var popupDialog;
var clientId;
var realm;
var oauth2KeyName;
var redirect_uri;
function handleLogin() {
var scopes = [];
var auths = window.swaggerUi.api.authSchemes || window.swaggerUi.api.securityDefinitions;
if(auths) {
var key;
var defs = auths;
for(key in defs) {
var auth = defs[key];
if(auth.type === 'oauth2' && auth.scopes) {
oauth2KeyName = key;
var scope;
if(Array.isArray(auth.scopes)) {
// 1.2 support
var i;
for(i = 0; i < auth.scopes.length; i++) {
scopes.push(auth.scopes[i]);
}
}
else {
// 2.0 support
for(scope in auth.scopes) {
scopes.push({scope: scope, description: auth.scopes[scope]});
}
}
}
}
}
if(window.swaggerUi.api
&& window.swaggerUi.api.info) {
appName = window.swaggerUi.api.info.title;
}
popupDialog = $(
[
'<div class="api-popup-dialog">',
'<div class="api-popup-title">Select OAuth2.0 Scopes</div>',
'<div class="api-popup-content">',
'<p>Scopes are used to grant an application different levels of access to data on behalf of the end user. Each API may declare one or more scopes.',
'<a href="#">Learn how to use</a>',
'</p>',
'<p><strong>' + appName + '</strong> API requires the following scopes. Select which ones you want to grant to Swagger UI.</p>',
'<ul class="api-popup-scopes">',
'</ul>',
'<p class="error-msg"></p>',
'<div class="api-popup-actions"><button class="api-popup-authbtn api-button green" type="button">Authorize</button><button class="api-popup-cancel api-button gray" type="button">Cancel</button></div>',
'</div>',
'</div>'].join(''));
$(document.body).append(popupDialog);
popup = popupDialog.find('ul.api-popup-scopes').empty();
for (i = 0; i < scopes.length; i ++) {
scope = scopes[i];
str = '<li><input type="checkbox" id="scope_' + i + '" scope="' + scope.scope + '"/>' + '<label for="scope_' + i + '">' + scope.scope;
if (scope.description) {
str += '<br/><span class="api-scope-desc">' + scope.description + '</span>';
}
str += '</label></li>';
popup.append(str);
}
var $win = $(window),
dw = $win.width(),
dh = $win.height(),
st = $win.scrollTop(),
dlgWd = popupDialog.outerWidth(),
dlgHt = popupDialog.outerHeight(),
top = (dh -dlgHt)/2 + st,
left = (dw - dlgWd)/2;
popupDialog.css({
top: (top < 0? 0 : top) + 'px',
left: (left < 0? 0 : left) + 'px'
});
popupDialog.find('button.api-popup-cancel').click(function() {
popupMask.hide();
popupDialog.hide();
popupDialog.empty();
popupDialog = [];
});
$('button.api-popup-authbtn').unbind();
popupDialog.find('button.api-popup-authbtn').click(function() {
popupMask.hide();
popupDialog.hide();
var authSchemes = window.swaggerUi.api.authSchemes;
var host = window.location;
var pathname = location.pathname.substring(0, location.pathname.lastIndexOf("/"));
var defaultRedirectUrl = host.protocol + '//' + host.host + pathname + '/o2c.html';
var redirectUrl = window.oAuthRedirectUrl || defaultRedirectUrl;
var url = null;
for (var key in authSchemes) {
if (authSchemes.hasOwnProperty(key)) {
var flow = authSchemes[key].flow;
if(authSchemes[key].type === 'oauth2' && flow && (flow === 'implicit' || flow === 'accessCode')) {
var dets = authSchemes[key];
url = dets.authorizationUrl + '?response_type=' + (flow === 'implicit' ? 'token' : 'code');
window.swaggerUi.tokenName = dets.tokenName || 'access_token';
window.swaggerUi.tokenUrl = (flow === 'accessCode' ? dets.tokenUrl : null);
}
else if(authSchemes[key].grantTypes) {
// 1.2 support
var o = authSchemes[key].grantTypes;
for(var t in o) {
if(o.hasOwnProperty(t) && t === 'implicit') {
var dets = o[t];
var ep = dets.loginEndpoint.url;
url = dets.loginEndpoint.url + '?response_type=token';
window.swaggerUi.tokenName = dets.tokenName;
}
else if (o.hasOwnProperty(t) && t === 'accessCode') {
var dets = o[t];
var ep = dets.tokenRequestEndpoint.url;
url = dets.tokenRequestEndpoint.url + '?response_type=code';
window.swaggerUi.tokenName = dets.tokenName;
}
}
}
}
}
var scopes = []
var o = $('.api-popup-scopes').find('input:checked');
for(k =0; k < o.length; k++) {
var scope = $(o[k]).attr('scope');
if (scopes.indexOf(scope) === -1)
scopes.push(scope);
}
// Implicit auth recommends a state parameter.
var state = Math.random ();
window.enabledScopes=scopes;
redirect_uri = redirectUrl;
url += '&redirect_uri=' + encodeURIComponent(redirectUrl);
url += '&realm=' + encodeURIComponent(realm);
url += '&client_id=' + encodeURIComponent(clientId);
url += '&scope=' + encodeURIComponent(scopes.join(' '));
url += '&state=' + encodeURIComponent(state);
window.open(url);
});
popupMask.show();
popupDialog.show();
return;
}
function handleLogout() {
for(key in window.authorizations.authz){
window.authorizations.remove(key)
}
window.enabledScopes = null;
$('.api-ic.ic-on').addClass('ic-off');
$('.api-ic.ic-on').removeClass('ic-on');
// set the info box
$('.api-ic.ic-warning').addClass('ic-error');
$('.api-ic.ic-warning').removeClass('ic-warning');
}
function initOAuth(opts) {
var o = (opts||{});
var errors = [];
appName = (o.appName||errors.push('missing appName'));
popupMask = (o.popupMask||$('#api-common-mask'));
popupDialog = (o.popupDialog||$('.api-popup-dialog'));
clientId = (o.clientId||errors.push('missing client id'));
realm = (o.realm||errors.push('missing realm'));
if(errors.length > 0){
log('auth unable initialize oauth: ' + errors);
return;
}
$('pre code').each(function(i, e) {hljs.highlightBlock(e)});
$('.api-ic').unbind();
$('.api-ic').click(function(s) {
if($(s.target).hasClass('ic-off'))
handleLogin();
else {
handleLogout();
}
false;
});
}
window.processOAuthCode = function processOAuthCode(data) {
var params = {
'client_id': clientId,
'code': data.code,
'grant_type': 'authorization_code',
'redirect_uri': redirect_uri
}
$.ajax(
{
url : window.swaggerUi.tokenUrl,
type: "POST",
data: params,
success:function(data, textStatus, jqXHR)
{
onOAuthComplete(data);
},
error: function(jqXHR, textStatus, errorThrown)
{
onOAuthComplete("");
}
});
}
window.onOAuthComplete = function onOAuthComplete(token) {
if(token) {
if(token.error) {
var checkbox = $('input[type=checkbox],.secured')
checkbox.each(function(pos){
checkbox[pos].checked = false;
});
alert(token.error);
}
else {
var b = token[window.swaggerUi.tokenName];
if(b){
// if all roles are satisfied
var o = null;
$.each($('.auth #api_information_panel'), function(k, v) {
var children = v;
if(children && children.childNodes) {
var requiredScopes = [];
$.each((children.childNodes), function (k1, v1){
var inner = v1.innerHTML;
if(inner)
requiredScopes.push(inner);
});
var diff = [];
for(var i=0; i < requiredScopes.length; i++) {
var s = requiredScopes[i];
if(window.enabledScopes && window.enabledScopes.indexOf(s) == -1) {
diff.push(s);
}
}
if(diff.length > 0){
o = v.parentNode;
$(o.parentNode).find('.api-ic.ic-on').addClass('ic-off');
$(o.parentNode).find('.api-ic.ic-on').removeClass('ic-on');
// sorry, not all scopes are satisfied
$(o).find('.api-ic').addClass('ic-warning');
$(o).find('.api-ic').removeClass('ic-error');
}
else {
o = v.parentNode;
$(o.parentNode).find('.api-ic.ic-off').addClass('ic-on');
$(o.parentNode).find('.api-ic.ic-off').removeClass('ic-off');
// all scopes are satisfied
$(o).find('.api-ic').addClass('ic-info');
$(o).find('.api-ic').removeClass('ic-warning');
$(o).find('.api-ic').removeClass('ic-error');
}
}
});
window.swaggerUi.api.clientAuthorizations.add(oauth2KeyName, new SwaggerClient.ApiKeyAuthorization('Authorization', 'Bearer ' + b, 'header'));
}
}
}
}
This diff is collapsed. Click to expand it.
This diff could not be displayed because it is too large.
......@@ -61,6 +61,7 @@
org.onosproject.rest.exceptions.IllegalStateExceptionMapper,
org.onosproject.rest.resources.JsonBodyWriter,
org.onosproject.rest.resources.ApiDocResource,
org.onosproject.rest.resources.ApplicationsWebResource,
org.onosproject.rest.resources.ComponentConfigWebResource,
org.onosproject.rest.resources.NetworkConfigWebResource,
......
......@@ -18,6 +18,7 @@ package org.onosproject.ui.impl;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableList.Builder;
import org.onlab.osgi.ServiceNotFoundException;
import org.onosproject.rest.AbstractInjectionResource;
import org.onosproject.ui.UiExtensionService;
import javax.ws.rs.GET;
......
......@@ -15,6 +15,7 @@
*/
package org.onosproject.ui.impl;
import org.onosproject.rest.AbstractInjectionResource;
import org.onosproject.ui.UiExtension;
import org.onosproject.ui.UiExtensionService;
import org.onosproject.ui.UiView;
......
......@@ -15,6 +15,7 @@
*/
package org.onosproject.ui.impl;
import org.onosproject.rest.AbstractInjectionResource;
import org.onosproject.ui.UiExtension;
import org.onosproject.ui.UiExtensionService;
import org.onosproject.ui.UiView;
......
......@@ -15,6 +15,7 @@
*/
package org.onosproject.ui.impl;
import org.onosproject.rest.AbstractInjectionResource;
import org.onosproject.ui.UiExtension;
import org.onosproject.ui.UiExtensionService;
......
......@@ -75,8 +75,6 @@
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-servlet</artifactId>
......@@ -118,39 +116,6 @@
<groupId>org.apache.felix</groupId>
<artifactId>maven-scr-plugin</artifactId>
</plugin>
<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,javax.ws.rs.ext,
com.sun.jersey.api,
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.base.*,
com.google.common.collect.*,
com.google.common.io.*,
org.eclipse.jetty.websocket.*,
org.onlab.util.*,
org.onlab.osgi.*,
org.onlab.packet.*,
org.onlab.rest.*,
org.onosproject.*
</Import-Package>
<Web-ContextPath>${web.context}</Web-ContextPath>
</instructions>
</configuration>
</plugin>
</plugins>
</build>
......