blob: e4ff608e8885f61e02050c4cde2b0765b24b4ca1 [file] [log] [blame]
/*
* Copyright 2017-present Open Networking Foundation
*
* 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.mcast.web;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.annotations.Beta;
import org.onlab.packet.IpAddress;
import org.onosproject.mcast.api.McastRoute;
import org.onosproject.mcast.api.MulticastRouteService;
import org.onosproject.net.ConnectPoint;
import org.onosproject.net.HostId;
import org.onosproject.rest.AbstractWebResource;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
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;
import java.net.URI;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import static org.onlab.util.Tools.nullIsIllegal;
/**
* Manage the multicast routing information.
*/
@Beta
@Path("mcast")
public class McastRouteWebResource extends AbstractWebResource {
//TODO return error messages
private static final String SOURCES = "sources";
private static final String SINKS = "sinks";
private static final String ROUTES = "routes";
private static final String ROUTES_KEY_ERROR = "No routes";
private static final String ASM = "*";
private Optional<McastRoute> getStaticRoute(Set<McastRoute> mcastRoutes) {
return mcastRoutes.stream()
.filter(mcastRoute -> mcastRoute.type() == McastRoute.Type.STATIC)
.findAny();
}
/**
* Get all multicast routes.
* Returns array of all known multicast routes.
*
* @return 200 OK with array of all known multicast routes
*/
@GET
@Produces(MediaType.APPLICATION_JSON)
public Response getRoutes() {
Set<McastRoute> routes = get(MulticastRouteService.class).getRoutes();
ObjectNode root = encodeArray(McastRoute.class, ROUTES, routes);
return ok(root).build();
}
/**
* Gets a multicast route.
*
* @param group group IP address
* @param srcIp source IP address
* @return 200 OK with a multicast routes
*/
@GET
@Produces(MediaType.APPLICATION_JSON)
@Path("{group}/{srcIp}")
public Response getRoute(@PathParam("group") String group,
@PathParam("srcIp") String srcIp) {
Optional<McastRoute> route = getMcastRoute(group, srcIp);
if (route.isPresent()) {
ObjectNode root = encode(route.get(), McastRoute.class);
return ok(root).build();
}
return Response.noContent().build();
}
/**
* Get all sources connect points for a multicast route.
*
* @param group group IP address
* @param srcIp source IP address
* @return 200 OK with array of all sources for multicast route
*/
@GET
@Produces(MediaType.APPLICATION_JSON)
@Path("sources/{group}/{srcIp}")
public Response getSources(@PathParam("group") String group,
@PathParam("srcIp") String srcIp) {
Optional<McastRoute> route = getMcastRoute(group, srcIp);
if (route.isPresent()) {
ObjectNode sources = this.mapper().createObjectNode();
get(MulticastRouteService.class).routeData(route.get()).sources().forEach((k, v) -> {
ArrayNode node = this.mapper().createArrayNode();
v.forEach(source -> {
node.add(source.toString());
});
sources.putPOJO(k.toString(), node);
});
ObjectNode root = this.mapper().createObjectNode().putPOJO(SOURCES, sources);
return ok(root).build();
}
return Response.noContent().build();
}
/**
* Get all HostId sinks and their connect points for a multicast route.
*
* @param group group IP address
* @param srcIp source IP address
* @return 200 OK with array of all sinks for multicast route
*/
@GET
@Produces(MediaType.APPLICATION_JSON)
@Path("sinks/{group}/{srcIp}")
public Response getSinks(@PathParam("group") String group,
@PathParam("srcIp") String srcIp) {
Optional<McastRoute> route = getMcastRoute(group, srcIp);
if (route.isPresent()) {
ObjectNode sinks = this.mapper().createObjectNode();
get(MulticastRouteService.class).routeData(route.get()).sinks().forEach((k, v) -> {
ArrayNode node = this.mapper().createArrayNode();
v.forEach(sink -> {
node.add(sink.toString());
});
sinks.putPOJO(k.toString(), node);
});
ObjectNode root = this.mapper().createObjectNode().putPOJO(SINKS, sinks);
return ok(root).build();
}
return Response.noContent().build();
}
/**
* Get all source connect points for a given sink host in a multicast route.
*
* @param group group IP address
* @param srcIp source IP address
* @param hostId host Id
* @return 200 OK with array of all sources for multicast route
*/
@GET
@Produces(MediaType.APPLICATION_JSON)
@Path("sources/{group}/{srcIp}/{hostId}")
public Response getHostSources(@PathParam("group") String group,
@PathParam("srcIp") String srcIp,
@PathParam("hostId") String hostId) {
Optional<McastRoute> route = getMcastRoute(group, srcIp);
if (route.isPresent()) {
ArrayNode node = this.mapper().createArrayNode();
get(MulticastRouteService.class).sources(route.get(), HostId.hostId(hostId))
.forEach(source -> {
node.add(source.toString());
});
ObjectNode root = this.mapper().createObjectNode().putPOJO(SOURCES, node);
return ok(root).build();
}
return Response.noContent().build();
}
/**
* Get all sink connect points for a given sink host in a multicast route.
*
* @param group group IP address
* @param srcIp source IP address
* @param hostId host Id
* @return 200 OK with array of all sinks for multicast route
*/
@GET
@Produces(MediaType.APPLICATION_JSON)
@Path("sinks/{group}/{srcIp}/{hostId}")
public Response getHostSinks(@PathParam("group") String group,
@PathParam("srcIp") String srcIp,
@PathParam("hostId") String hostId) {
Optional<McastRoute> route = getMcastRoute(group, srcIp);
if (route.isPresent()) {
ArrayNode node = this.mapper().createArrayNode();
get(MulticastRouteService.class).sinks(route.get(), HostId.hostId(hostId))
.forEach(source -> {
node.add(source.toString());
});
ObjectNode root = this.mapper().createObjectNode().putPOJO(SINKS, node);
return ok(root).build();
}
return Response.noContent().build();
}
/**
* Creates a set of new multicast routes.
*
* @param stream multicast routes JSON
* @return status of the request - CREATED if the JSON is correct,
* BAD_REQUEST if the JSON is invalid
* @onos.rsModel McastRouteBulk
*/
@POST
@Consumes(MediaType.APPLICATION_JSON)
@Path("bulk/")
public Response createRoutes(InputStream stream) {
MulticastRouteService service = get(MulticastRouteService.class);
try {
ObjectNode jsonTree = (ObjectNode) mapper().readTree(stream);
ArrayNode routesArray = nullIsIllegal((ArrayNode) jsonTree.get(ROUTES),
ROUTES_KEY_ERROR);
routesArray.forEach(routeJson -> {
McastRoute route = codec(McastRoute.class).decode((ObjectNode) routeJson, this);
service.add(route);
Set<HostId> sources = new HashSet<>();
routeJson.path(SOURCES).elements().forEachRemaining(src -> {
sources.add(HostId.hostId(src.asText()));
});
Set<HostId> sinks = new HashSet<>();
routeJson.path(SINKS).elements().forEachRemaining(sink -> {
sinks.add(HostId.hostId(sink.asText()));
});
if (!sources.isEmpty()) {
sources.forEach(source -> {
service.addSource(route, source);
});
}
if (!sinks.isEmpty()) {
sinks.forEach(sink -> {
service.addSink(route, sink);
});
}
});
} catch (IOException ex) {
throw new IllegalArgumentException(ex);
}
return Response
.created(URI.create(""))
.build();
}
/**
* Create new multicast route.
*
* @param stream multicast route JSON
* @return status of the request - CREATED if the JSON is correct,
* BAD_REQUEST if the JSON is invalid
* @onos.rsModel McastRoute
*/
@POST
@Consumes(MediaType.APPLICATION_JSON)
public Response createRoute(InputStream stream) {
MulticastRouteService service = get(MulticastRouteService.class);
try {
ObjectNode jsonTree = (ObjectNode) mapper().readTree(stream);
McastRoute route = codec(McastRoute.class).decode(jsonTree, this);
service.add(route);
Set<HostId> sources = new HashSet<>();
jsonTree.path(SOURCES).elements().forEachRemaining(src -> {
sources.add(HostId.hostId(src.asText()));
});
Set<HostId> sinks = new HashSet<>();
jsonTree.path(SINKS).elements().forEachRemaining(sink -> {
sinks.add(HostId.hostId(sink.asText()));
});
if (!sources.isEmpty()) {
sources.forEach(source -> {
service.addSource(route, source);
});
}
if (!sinks.isEmpty()) {
sinks.forEach(sink -> {
service.addSink(route, sink);
});
}
} catch (IOException ex) {
throw new IllegalArgumentException(ex);
}
return Response
.created(URI.create(""))
.build();
}
/**
* Adds sources for a given existing multicast route.
*
* @param group group IP address
* @param srcIp source IP address
* @param stream host sinks JSON
* @return status of the request - CREATED if the JSON is correct,
* BAD_REQUEST if the JSON is invalid
* @onos.rsModel McastSourcesAdd
*/
@POST
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
@Path("sources/{group}/{srcIp}")
public Response addSources(@PathParam("group") String group,
@PathParam("srcIp") String srcIp,
InputStream stream) {
MulticastRouteService service = get(MulticastRouteService.class);
Optional<McastRoute> route = getMcastRoute(group, srcIp);
if (route.isPresent()) {
ArrayNode jsonTree;
try {
jsonTree = (ArrayNode) mapper().readTree(stream).get(SOURCES);
Set<HostId> sources = new HashSet<>();
jsonTree.elements().forEachRemaining(src -> {
sources.add(HostId.hostId(src.asText()));
});
if (!sources.isEmpty()) {
sources.forEach(src -> {
service.addSource(route.get(), src);
});
}
} catch (IOException e) {
throw new IllegalArgumentException(e);
}
return Response.ok().build();
}
return Response.noContent().build();
}
/**
* Adds sinks for a given existing multicast route.
*
* @param group group IP address
* @param srcIp source IP address
* @param stream host sinks JSON
* @return status of the request - CREATED if the JSON is correct,
* BAD_REQUEST if the JSON is invalid
* @onos.rsModel McastSinksAdd
*/
@POST
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
@Path("sinks/{group}/{srcIp}")
public Response addSinks(@PathParam("group") String group,
@PathParam("srcIp") String srcIp,
InputStream stream) {
MulticastRouteService service = get(MulticastRouteService.class);
Optional<McastRoute> route = getMcastRoute(group, srcIp);
if (route.isPresent()) {
ArrayNode jsonTree;
try {
jsonTree = (ArrayNode) mapper().readTree(stream).get(SINKS);
Set<HostId> sinks = new HashSet<>();
jsonTree.elements().forEachRemaining(sink -> {
sinks.add(HostId.hostId(sink.asText()));
});
if (!sinks.isEmpty()) {
sinks.forEach(sink -> {
service.addSink(route.get(), sink);
});
}
} catch (IOException e) {
throw new IllegalArgumentException(e);
}
return Response.ok().build();
}
return Response.noContent().build();
}
/**
* Adds a new set of connect points for an existing host source in a given multicast route.
*
* @param group group IP address
* @param srcIp source IP address
* @param hostId the host Id
* @param stream source connect points JSON
* @return status of the request - CREATED if the JSON is correct,
* BAD_REQUEST if the JSON is invalid
* @onos.rsModel McastHostSourcesAdd
*/
@POST
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
@Path("source/{group}/{srcIp}/{hostId}")
public Response addHostSource(@PathParam("group") String group,
@PathParam("srcIp") String srcIp,
@PathParam("hostId") String hostId,
InputStream stream) {
MulticastRouteService service = get(MulticastRouteService.class);
Optional<McastRoute> route = getMcastRoute(group, srcIp);
if (route.isPresent()) {
ArrayNode jsonTree;
try {
jsonTree = (ArrayNode) mapper().readTree(stream).get(SOURCES);
Set<ConnectPoint> sources = new HashSet<>();
jsonTree.elements().forEachRemaining(src -> {
sources.add(ConnectPoint.deviceConnectPoint(src.asText()));
});
if (!sources.isEmpty()) {
service.addSources(route.get(), HostId.hostId(hostId), sources);
}
} catch (IOException e) {
throw new IllegalArgumentException(e);
}
return Response.ok().build();
}
return Response.noContent().build();
}
/**
* Adds a new sink for an existing host in a given multicast route.
*
* @param group group IP address
* @param srcIp source IP address
* @param hostId the host Id
* @param stream sink connect points JSON
* @return status of the request - CREATED if the JSON is correct,
* BAD_REQUEST if the JSON is invalid
* @onos.rsModel McastHostSinksAdd
*/
@POST
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
@Path("sinks/{group}/{srcIp}/{hostId}")
public Response addHostSinks(@PathParam("group") String group,
@PathParam("srcIp") String srcIp,
@PathParam("hostId") String hostId,
InputStream stream) {
MulticastRouteService service = get(MulticastRouteService.class);
Optional<McastRoute> route = getMcastRoute(group, srcIp);
if (route.isPresent()) {
ArrayNode jsonTree;
try {
jsonTree = (ArrayNode) mapper().readTree(stream).get(SINKS);
Set<ConnectPoint> sinks = new HashSet<>();
jsonTree.elements().forEachRemaining(src -> {
sinks.add(ConnectPoint.deviceConnectPoint(src.asText()));
});
if (!sinks.isEmpty()) {
service.addSinks(route.get(), HostId.hostId(hostId), sinks);
}
} catch (IOException e) {
throw new IllegalArgumentException(e);
}
return Response.ok().build();
}
return Response.noContent().build();
}
/**
* Removes all the multicast routes.
*
* @return 204 NO CONTENT
*/
@DELETE
public Response deleteRoutes() {
MulticastRouteService service = get(MulticastRouteService.class);
service.getRoutes().forEach(service::remove);
return Response.noContent().build();
}
/**
* Removes all the given multicast routes.
*
* @param stream the set of multicast routes
* @return 204 NO CONTENT
*/
@DELETE
@Consumes(MediaType.APPLICATION_JSON)
@Path("bulk/")
public Response deleteRoutes(InputStream stream) {
MulticastRouteService service = get(MulticastRouteService.class);
try {
ObjectNode jsonTree = (ObjectNode) mapper().readTree(stream);
ArrayNode routesArray = nullIsIllegal((ArrayNode) jsonTree.get(ROUTES),
ROUTES_KEY_ERROR);
List<McastRoute> routes = codec(McastRoute.class).decode(routesArray, this);
routes.forEach(service::remove);
} catch (IOException ex) {
throw new IllegalArgumentException(ex);
}
return Response.noContent().build();
}
/**
* Deletes a specific route.
*
* @param group group IP address
* @param srcIp source IP address
* @return 204 NO CONTENT
*/
@DELETE
@Path("{group}/{srcIp}")
public Response deleteRoute(@PathParam("group") String group,
@PathParam("srcIp") String srcIp) {
Optional<McastRoute> route = getMcastRoute(group, srcIp);
route.ifPresent(mcastRoute -> {
get(MulticastRouteService.class).remove(mcastRoute);
});
return Response.noContent().build();
}
/**
* Deletes all the source connect points for a specific route.
* If the sources are empty the entire route is removed.
*
* @param group group IP address
* @param srcIp source IP address
* @return 204 NO CONTENT
*/
@DELETE
@Consumes(MediaType.APPLICATION_JSON)
@Path("sources/{group}/{srcIp}")
public Response deleteSources(@PathParam("group") String group,
@PathParam("srcIp") String srcIp) {
Optional<McastRoute> route = getMcastRoute(group, srcIp);
route.ifPresent(mcastRoute -> get(MulticastRouteService.class).removeSources(mcastRoute));
return Response.noContent().build();
}
/**
* Deletes a source hostId for a specific route.
* If the sources are empty the entire route is removed.
*
* @param group group IP address
* @param srcIp source IP address
* @param hostId source host id
* @return 204 NO CONTENT
*/
@DELETE
@Consumes(MediaType.APPLICATION_JSON)
@Path("sources/{group}/{srcIp}/{hostId}")
public Response deleteSource(@PathParam("group") String group,
@PathParam("srcIp") String srcIp,
@PathParam("hostId") String hostId) {
Optional<McastRoute> route = getMcastRoute(group, srcIp);
route.ifPresent(mcastRoute -> get(MulticastRouteService.class)
.removeSource(mcastRoute, HostId.hostId(hostId)));
return Response.noContent().build();
}
/**
* Deletes all the sinks for a specific route.
*
* @param group group IP address
* @param srcIp source IP address
* @return 204 NO CONTENT
*/
@DELETE
@Consumes(MediaType.APPLICATION_JSON)
@Path("sinks/{group}/{srcIp}")
public Response deleteHostsSinks(@PathParam("group") String group,
@PathParam("srcIp") String srcIp) {
Optional<McastRoute> route = getMcastRoute(group, srcIp);
route.ifPresent(mcastRoute -> get(MulticastRouteService.class)
.removeSinks(mcastRoute));
return Response.noContent().build();
}
/**
* Deletes a sink connect points for a given host for a specific route.
*
* @param group group IP address
* @param srcIp source IP address
* @param hostId sink host
* @return 204 NO CONTENT
*/
@DELETE
@Consumes(MediaType.APPLICATION_JSON)
@Path("sinks/{group}/{srcIp}/{hostId}")
public Response deleteHostSinks(@PathParam("group") String group,
@PathParam("srcIp") String srcIp,
@PathParam("hostId") String hostId) {
Optional<McastRoute> route = getMcastRoute(group, srcIp);
route.ifPresent(mcastRoute -> get(MulticastRouteService.class)
.removeSink(mcastRoute, HostId.hostId(hostId)));
return Response.noContent().build();
}
private Optional<McastRoute> getMcastRoute(String group, String srcIp) {
IpAddress ipAddress = null;
if (!srcIp.equals(ASM)) {
ipAddress = IpAddress.valueOf(srcIp);
}
return getStaticRoute(get(MulticastRouteService.class)
.getRoute(IpAddress.valueOf(group), ipAddress));
}
}