blob: 26e33170ad7f02dedb3a6ff3851aef29c15c2c6c [file] [log] [blame]
Andrea Campanella545edb42018-03-20 16:37:29 -07001/*
2 * Copyright 2017-present Open Networking Foundation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package org.onosproject.mcast.web;
18
Andrea Campanella644a8a62018-03-21 19:08:21 -070019import com.fasterxml.jackson.databind.node.ArrayNode;
20import com.fasterxml.jackson.databind.node.ObjectNode;
Andrea Campanella545edb42018-03-20 16:37:29 -070021import com.google.common.annotations.Beta;
Andrea Campanella644a8a62018-03-21 19:08:21 -070022import com.google.common.collect.ImmutableSet;
23import org.onlab.packet.IpAddress;
24import org.onosproject.mcast.api.McastRoute;
25import org.onosproject.mcast.api.MulticastRouteService;
26import org.onosproject.net.ConnectPoint;
27import org.onosproject.net.HostId;
Andrea Campanella545edb42018-03-20 16:37:29 -070028import org.onosproject.rest.AbstractWebResource;
29
Andrea Campanella644a8a62018-03-21 19:08:21 -070030import javax.ws.rs.Consumes;
31import javax.ws.rs.DELETE;
Andrea Campanella545edb42018-03-20 16:37:29 -070032import javax.ws.rs.GET;
Andrea Campanella644a8a62018-03-21 19:08:21 -070033import javax.ws.rs.POST;
Andrea Campanella545edb42018-03-20 16:37:29 -070034import javax.ws.rs.Path;
Andrea Campanella644a8a62018-03-21 19:08:21 -070035import javax.ws.rs.PathParam;
Andrea Campanella545edb42018-03-20 16:37:29 -070036import javax.ws.rs.Produces;
37import javax.ws.rs.core.MediaType;
38import javax.ws.rs.core.Response;
Andrea Campanella644a8a62018-03-21 19:08:21 -070039import java.io.IOException;
40import java.io.InputStream;
41import java.net.URI;
42import java.util.HashSet;
43import java.util.List;
44import java.util.Optional;
45import java.util.Set;
46
47import static org.onlab.util.Tools.nullIsIllegal;
Andrea Campanella545edb42018-03-20 16:37:29 -070048
49/**
50 * Manage the multicast routing information.
51 */
52@Beta
53@Path("mcast")
54public class McastRouteWebResource extends AbstractWebResource {
55
Andrea Campanella644a8a62018-03-21 19:08:21 -070056 //TODO return error messages
57
58 private static final String SOURCES = "sources";
59 private static final String SINKS = "sinks";
60 private static final String ROUTES = "routes";
61 private static final String ROUTES_KEY_ERROR = "No routes";
62 private static final String ASM = "*";
63
64 private Optional<McastRoute> getStaticRoute(Set<McastRoute> mcastRoutes) {
65 return mcastRoutes.stream()
66 .filter(mcastRoute -> mcastRoute.type() == McastRoute.Type.STATIC)
67 .findAny();
68 }
69
Andrea Campanella545edb42018-03-20 16:37:29 -070070 /**
71 * Get all multicast routes.
72 * Returns array of all known multicast routes.
73 *
74 * @return 200 OK with array of all known multicast routes
75 */
76 @GET
77 @Produces(MediaType.APPLICATION_JSON)
78 public Response getRoutes() {
Andrea Campanella644a8a62018-03-21 19:08:21 -070079 Set<McastRoute> routes = get(MulticastRouteService.class).getRoutes();
80 ObjectNode root = encodeArray(McastRoute.class, ROUTES, routes);
81 return ok(root).build();
82 }
83
84 /**
85 * Gets a multicast route.
86 *
87 * @param group group IP address
88 * @param srcIp source IP address
89 * @return 200 OK with a multicast routes
90 */
91 @GET
92 @Produces(MediaType.APPLICATION_JSON)
93 @Path("{group}/{srcIp}")
94 public Response getRoute(@PathParam("group") String group,
95 @PathParam("srcIp") String srcIp) {
96 Optional<McastRoute> route = getMcastRoute(group, srcIp);
97 if (route.isPresent()) {
98 ObjectNode root = encode(route.get(), McastRoute.class);
99 return ok(root).build();
100 }
Andrea Campanella545edb42018-03-20 16:37:29 -0700101 return Response.noContent().build();
102 }
103
Andrea Campanella644a8a62018-03-21 19:08:21 -0700104 /**
105 * Get all sources connect points for a multicast route.
106 *
107 * @param group group IP address
108 * @param srcIp source IP address
109 * @return 200 OK with array of all sources for multicast route
110 */
111 @GET
112 @Produces(MediaType.APPLICATION_JSON)
113 @Path("sources/{group}/{srcIp}")
114 public Response getSources(@PathParam("group") String group,
115 @PathParam("srcIp") String srcIp) {
116 Optional<McastRoute> route = getMcastRoute(group, srcIp);
117 if (route.isPresent()) {
118 get(MulticastRouteService.class).sources(route.get());
119 ArrayNode node = this.mapper().createArrayNode();
120 get(MulticastRouteService.class).sources(route.get()).forEach(source -> {
121 node.add(source.toString());
122 });
123 ObjectNode root = this.mapper().createObjectNode().putPOJO(SOURCES, node);
124 return ok(root).build();
125 }
126 return Response.noContent().build();
127 }
128
129 /**
130 * Get all sinks connect points for a multicast route.
131 *
132 * @param group group IP address
133 * @param srcIp source IP address
134 * @return 200 OK with array of all sinks for multicast route
135 */
136 @GET
137 @Produces(MediaType.APPLICATION_JSON)
138 @Path("sinks/{group}/{srcIp}")
139 public Response getSinks(@PathParam("group") String group,
140 @PathParam("srcIp") String srcIp) {
141 Optional<McastRoute> route = getMcastRoute(group, srcIp);
142 if (route.isPresent()) {
143 get(MulticastRouteService.class).sources(route.get());
144 ObjectNode sinks = this.mapper().createObjectNode();
145 get(MulticastRouteService.class).routeData(route.get()).sinks().forEach((k, v) -> {
146 ArrayNode node = this.mapper().createArrayNode();
147 v.forEach(sink -> {
148 node.add(sink.toString());
149 });
150 sinks.putPOJO(k.toString(), node);
151 });
152 ObjectNode root = this.mapper().createObjectNode().putPOJO(SINKS, sinks);
153 return ok(root).build();
154 }
155 return Response.noContent().build();
156 }
157
158 /**
159 * Get all sink connect points for a given sink host in a multicast route.
160 *
161 * @param group group IP address
162 * @param srcIp source IP address
163 * @param hostId host Id
164 * @return 200 OK with array of all sinks for multicast route
165 */
166 @GET
167 @Produces(MediaType.APPLICATION_JSON)
168 @Path("sinks/{group}/{srcIp}/{hostId}")
169 public Response getHostSinks(@PathParam("group") String group,
170 @PathParam("srcIp") String srcIp,
171 @PathParam("hostId") String hostId) {
172 Optional<McastRoute> route = getMcastRoute(group, srcIp);
173 if (route.isPresent()) {
174 get(MulticastRouteService.class).sources(route.get());
175 ArrayNode node = this.mapper().createArrayNode();
176 get(MulticastRouteService.class).sinks(route.get(), HostId.hostId(hostId))
177 .forEach(source -> {
178 node.add(source.toString());
179 });
180 ObjectNode root = this.mapper().createObjectNode().putPOJO(SINKS, node);
181 return ok(root).build();
182 }
183 return Response.noContent().build();
184 }
185
186 /**
187 * Creates a set of new multicast routes.
188 *
189 * @param stream multicast routes JSON
190 * @return status of the request - CREATED if the JSON is correct,
191 * BAD_REQUEST if the JSON is invalid
192 * @onos.rsModel McastRouteBulk
193 */
194 @POST
195 @Consumes(MediaType.APPLICATION_JSON)
196 @Path("bulk/")
197 public Response createRoutes(InputStream stream) {
198 MulticastRouteService service = get(MulticastRouteService.class);
199 try {
200 ObjectNode jsonTree = (ObjectNode) mapper().readTree(stream);
201 ArrayNode routesArray = nullIsIllegal((ArrayNode) jsonTree.get(ROUTES),
202 ROUTES_KEY_ERROR);
203 routesArray.forEach(routeJson -> {
204 McastRoute route = codec(McastRoute.class).decode((ObjectNode) routeJson, this);
205 service.add(route);
206
207 Set<ConnectPoint> sources = new HashSet<>();
208 routeJson.path(SOURCES).elements().forEachRemaining(src -> {
209 sources.add(ConnectPoint.deviceConnectPoint(src.asText()));
210 });
211 Set<HostId> sinks = new HashSet<>();
212 routeJson.path(SINKS).elements().forEachRemaining(sink -> {
213 sinks.add(HostId.hostId(sink.asText()));
214 });
215
216 if (!sources.isEmpty()) {
217 service.addSources(route, sources);
218 }
219 if (!sinks.isEmpty()) {
220 sinks.forEach(sink -> {
221 service.addSink(route, sink);
222 });
223 }
224 });
225 } catch (IOException ex) {
226 throw new IllegalArgumentException(ex);
227 }
228
229 return Response
230 .created(URI.create(""))
231 .build();
232 }
233
234 /**
235 * Create new multicast route.
236 *
237 * @param stream multicast route JSON
238 * @return status of the request - CREATED if the JSON is correct,
239 * BAD_REQUEST if the JSON is invalid
240 * @onos.rsModel McastRoute
241 */
242 @POST
243 @Consumes(MediaType.APPLICATION_JSON)
244 public Response createRoute(InputStream stream) {
245 MulticastRouteService service = get(MulticastRouteService.class);
246 try {
247 ObjectNode jsonTree = (ObjectNode) mapper().readTree(stream);
248 McastRoute route = codec(McastRoute.class).decode(jsonTree, this);
249 service.add(route);
250
251 Set<ConnectPoint> sources = new HashSet<>();
252 jsonTree.path(SOURCES).elements().forEachRemaining(src -> {
253 sources.add(ConnectPoint.deviceConnectPoint(src.asText()));
254 });
255 Set<HostId> sinks = new HashSet<>();
256 jsonTree.path(SINKS).elements().forEachRemaining(sink -> {
257 sinks.add(HostId.hostId(sink.asText()));
258 });
259
260 if (!sources.isEmpty()) {
261 service.addSources(route, sources);
262 }
263 if (!sinks.isEmpty()) {
264 sinks.forEach(sink -> {
265 service.addSink(route, sink);
266 });
267 }
268
269 } catch (IOException ex) {
270 throw new IllegalArgumentException(ex);
271 }
272
273 return Response
274 .created(URI.create(""))
275 .build();
276 }
277
278 /**
279 * Adds sources for a given existing multicast route.
280 *
281 * @param group group IP address
282 * @param srcIp source IP address
283 * @param stream host sinks JSON
284 * @return status of the request - CREATED if the JSON is correct,
285 * BAD_REQUEST if the JSON is invalid
286 * @onos.rsModel McastSourcesAdd
287 */
288 @POST
289 @Consumes(MediaType.APPLICATION_JSON)
290 @Produces(MediaType.APPLICATION_JSON)
291 @Path("sources/{group}/{srcIp}")
292 public Response addSources(@PathParam("group") String group,
293 @PathParam("srcIp") String srcIp,
294 InputStream stream) {
295 MulticastRouteService service = get(MulticastRouteService.class);
296 Optional<McastRoute> route = getMcastRoute(group, srcIp);
297 if (route.isPresent()) {
298 ArrayNode jsonTree;
299 try {
300 jsonTree = (ArrayNode) mapper().readTree(stream).get(SOURCES);
301 Set<ConnectPoint> sources = new HashSet<>();
302 jsonTree.elements().forEachRemaining(src -> {
303 sources.add(ConnectPoint.deviceConnectPoint(src.asText()));
304 });
305 if (!sources.isEmpty()) {
306 service.addSources(route.get(), sources);
307 }
308 } catch (IOException e) {
309 throw new IllegalArgumentException(e);
310 }
311 return Response.ok().build();
312 }
313 return Response.noContent().build();
314
315 }
316
317 /**
318 * Adds sinks for a given existing multicast route.
319 *
320 * @param group group IP address
321 * @param srcIp source IP address
322 * @param stream host sinks JSON
323 * @return status of the request - CREATED if the JSON is correct,
324 * BAD_REQUEST if the JSON is invalid
325 * @onos.rsModel McastSinksAdd
326 */
327 @POST
328 @Consumes(MediaType.APPLICATION_JSON)
329 @Produces(MediaType.APPLICATION_JSON)
330 @Path("sinks/{group}/{srcIp}")
331 public Response addSinks(@PathParam("group") String group,
332 @PathParam("srcIp") String srcIp,
333 InputStream stream) {
334 MulticastRouteService service = get(MulticastRouteService.class);
335 Optional<McastRoute> route = getMcastRoute(group, srcIp);
336 if (route.isPresent()) {
337 ArrayNode jsonTree;
338 try {
339 jsonTree = (ArrayNode) mapper().readTree(stream).get(SINKS);
340 Set<HostId> sinks = new HashSet<>();
341 jsonTree.elements().forEachRemaining(sink -> {
342 sinks.add(HostId.hostId(sink.asText()));
343 });
344 if (!sinks.isEmpty()) {
345 sinks.forEach(sink -> {
346 service.addSink(route.get(), sink);
347 });
348 }
349 } catch (IOException e) {
350 throw new IllegalArgumentException(e);
351 }
352 return Response.ok().build();
353 }
354 return Response.noContent().build();
355 }
356
357
358 /**
359 * Adds a new sink for an existing host in a given multicast route.
360 *
361 * @param group group IP address
362 * @param srcIp source IP address
363 * @param hostId the host Id
364 * @param stream sink connect points JSON
365 * @return status of the request - CREATED if the JSON is correct,
366 * BAD_REQUEST if the JSON is invalid
367 * @onos.rsModel McastHostSinksAdd
368 */
369 @POST
370 @Consumes(MediaType.APPLICATION_JSON)
371 @Produces(MediaType.APPLICATION_JSON)
372 @Path("sinks/{group}/{srcIp}/{hostId}")
373 public Response addHostSinks(@PathParam("group") String group,
374 @PathParam("srcIp") String srcIp,
375 @PathParam("hostId") String hostId,
376 InputStream stream) {
377 MulticastRouteService service = get(MulticastRouteService.class);
378 Optional<McastRoute> route = getMcastRoute(group, srcIp);
379 if (route.isPresent()) {
380 ArrayNode jsonTree;
381 try {
382 jsonTree = (ArrayNode) mapper().readTree(stream).get(SINKS);
383 Set<ConnectPoint> sinks = new HashSet<>();
384 jsonTree.elements().forEachRemaining(src -> {
385 sinks.add(ConnectPoint.deviceConnectPoint(src.asText()));
386 });
387 if (!sinks.isEmpty()) {
388 service.addSinks(route.get(), HostId.hostId(hostId), sinks);
389 }
390 } catch (IOException e) {
391 throw new IllegalArgumentException(e);
392 }
393 return Response.ok().build();
394 }
395 return Response.noContent().build();
396 }
397
398 /**
399 * Removes all the multicast routes.
400 *
401 * @return 204 NO CONTENT
402 */
403 @DELETE
404 public Response deleteRoutes() {
405 MulticastRouteService service = get(MulticastRouteService.class);
406 service.getRoutes().forEach(service::remove);
407 return Response.noContent().build();
408 }
409
410 /**
411 * Removes all the given multicast routes.
412 *
413 * @param stream the set of multicast routes
414 * @return 204 NO CONTENT
415 */
416 @DELETE
417 @Consumes(MediaType.APPLICATION_JSON)
418 @Path("bulk/")
419 public Response deleteRoutes(InputStream stream) {
420 MulticastRouteService service = get(MulticastRouteService.class);
421 try {
422 ObjectNode jsonTree = (ObjectNode) mapper().readTree(stream);
423 ArrayNode routesArray = nullIsIllegal((ArrayNode) jsonTree.get(ROUTES),
424 ROUTES_KEY_ERROR);
425 List<McastRoute> routes = codec(McastRoute.class).decode(routesArray, this);
426 routes.forEach(service::remove);
427 } catch (IOException ex) {
428 throw new IllegalArgumentException(ex);
429 }
430 return Response.noContent().build();
431 }
432
433 /**
434 * Deletes a specific route.
435 *
436 * @param group group IP address
437 * @param srcIp source IP address
438 * @return 204 NO CONTENT
439 */
440 @DELETE
441 @Path("{group}/{srcIp}")
442 public Response deleteRoute(@PathParam("group") String group,
443 @PathParam("srcIp") String srcIp) {
444 Optional<McastRoute> route = getMcastRoute(group, srcIp);
445 route.ifPresent(mcastRoute -> {
446 get(MulticastRouteService.class).remove(mcastRoute);
447 });
448 return Response.noContent().build();
449 }
450
451 /**
452 * Deletes all the source connect points for a specific route.
453 * If the sources are empty the entire route is removed.
454 *
455 * @param group group IP address
456 * @param srcIp source IP address
457 * @return 204 NO CONTENT
458 */
459 @DELETE
460 @Consumes(MediaType.APPLICATION_JSON)
461 @Path("sources/{group}/{srcIp}")
462 public Response deleteSources(@PathParam("group") String group,
463 @PathParam("srcIp") String srcIp) {
464 Optional<McastRoute> route = getMcastRoute(group, srcIp);
465 route.ifPresent(mcastRoute -> get(MulticastRouteService.class).removeSources(mcastRoute));
466 return Response.noContent().build();
467 }
468
469 /**
470 * Deletes a source connect point for a specific route.
471 * If the sources are empty the entire route is removed.
472 *
473 * @param group group IP address
474 * @param srcIp source IP address
475 * @param srcCp source connect point
476 * @return 204 NO CONTENT
477 */
478 @DELETE
479 @Consumes(MediaType.APPLICATION_JSON)
480 @Path("sources/{group}/{srcIp}/{srcCp}")
481 public Response deleteSource(@PathParam("group") String group,
482 @PathParam("srcIp") String srcIp,
483 @PathParam("srcCp") String srcCp) {
484 Optional<McastRoute> route = getMcastRoute(group, srcIp);
485 route.ifPresent(mcastRoute -> get(MulticastRouteService.class)
486 .removeSources(mcastRoute, ImmutableSet.of(ConnectPoint.deviceConnectPoint(srcCp))));
487 return Response.noContent().build();
488 }
489
490 /**
491 * Deletes all the sinks for a specific route.
492 *
493 * @param group group IP address
494 * @param srcIp source IP address
495 * @return 204 NO CONTENT
496 */
497 @DELETE
498 @Consumes(MediaType.APPLICATION_JSON)
499 @Path("sinks/{group}/{srcIp}")
500 public Response deleteHostsSinks(@PathParam("group") String group,
501 @PathParam("srcIp") String srcIp) {
502 Optional<McastRoute> route = getMcastRoute(group, srcIp);
503 route.ifPresent(mcastRoute -> get(MulticastRouteService.class)
504 .removeSinks(mcastRoute));
505 return Response.noContent().build();
506 }
507
508 /**
509 * Deletes a sink connect points for a given host for a specific route.
510 *
511 * @param group group IP address
512 * @param srcIp source IP address
513 * @param hostId sink host
514 * @return 204 NO CONTENT
515 */
516 @DELETE
517 @Consumes(MediaType.APPLICATION_JSON)
518 @Path("sinks/{group}/{srcIp}/{hostId}")
519 public Response deleteHostSinks(@PathParam("group") String group,
520 @PathParam("srcIp") String srcIp,
521 @PathParam("hostId") String hostId) {
522 Optional<McastRoute> route = getMcastRoute(group, srcIp);
523 route.ifPresent(mcastRoute -> get(MulticastRouteService.class)
524 .removeSink(mcastRoute, HostId.hostId(hostId)));
525 return Response.noContent().build();
526 }
527
528 private Optional<McastRoute> getMcastRoute(String group, String srcIp) {
529 IpAddress ipAddress = null;
530 if (!srcIp.equals(ASM)) {
531 ipAddress = IpAddress.valueOf(srcIp);
532 }
533 return getStaticRoute(get(MulticastRouteService.class)
534 .getRoute(IpAddress.valueOf(group), ipAddress));
535 }
536
Andrea Campanella545edb42018-03-20 16:37:29 -0700537}