blob: 84fd73c2a291f92078b121ce918a74946d16f865 [file] [log] [blame]
jiangrui330b0c92015-11-28 14:09:50 +08001/*
Brian O'Connor5ab426f2016-04-09 01:19:45 -07002 * Copyright 2015-present Open Networking Laboratory
jiangrui330b0c92015-11-28 14:09:50 +08003 *
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 */
16package org.onosproject.vtnweb.resources;
17
18import static com.google.common.base.Preconditions.checkArgument;
19import static com.google.common.base.Preconditions.checkNotNull;
20import static javax.ws.rs.core.Response.Status.BAD_REQUEST;
21import static javax.ws.rs.core.Response.Status.CONFLICT;
22import static javax.ws.rs.core.Response.Status.CREATED;
23import static javax.ws.rs.core.Response.Status.NOT_FOUND;
24import static javax.ws.rs.core.Response.Status.NO_CONTENT;
25
26import java.io.IOException;
27import java.io.InputStream;
28import java.util.ArrayList;
29import java.util.Collection;
30import java.util.Collections;
31import java.util.HashMap;
32import java.util.List;
33import java.util.Map;
34import java.util.Set;
35import java.util.concurrent.ConcurrentMap;
36
37import javax.ws.rs.Consumes;
38import javax.ws.rs.DELETE;
39import javax.ws.rs.GET;
40import javax.ws.rs.POST;
41import javax.ws.rs.PUT;
42import javax.ws.rs.Path;
43import javax.ws.rs.PathParam;
44import javax.ws.rs.Produces;
45import javax.ws.rs.QueryParam;
46import javax.ws.rs.core.MediaType;
47import javax.ws.rs.core.Response;
48
49import org.onlab.packet.IpAddress;
50import org.onlab.util.ItemNotFoundException;
51import org.onosproject.rest.AbstractWebResource;
52import org.onosproject.vtnrsc.DefaultRouter;
53import org.onosproject.vtnrsc.FixedIp;
54import org.onosproject.vtnrsc.Router;
55import org.onosproject.vtnrsc.Router.Status;
56import org.onosproject.vtnrsc.RouterGateway;
57import org.onosproject.vtnrsc.RouterId;
58import org.onosproject.vtnrsc.RouterInterface;
59import org.onosproject.vtnrsc.SubnetId;
60import org.onosproject.vtnrsc.TenantId;
61import org.onosproject.vtnrsc.TenantNetworkId;
62import org.onosproject.vtnrsc.VirtualPortId;
63import org.onosproject.vtnrsc.router.RouterService;
64import org.onosproject.vtnrsc.routerinterface.RouterInterfaceService;
65import org.onosproject.vtnweb.web.RouterCodec;
66import org.slf4j.Logger;
67import org.slf4j.LoggerFactory;
68
69import com.fasterxml.jackson.databind.JsonNode;
70import com.fasterxml.jackson.databind.ObjectMapper;
71import com.fasterxml.jackson.databind.node.ObjectNode;
72import com.google.common.collect.Maps;
73import com.google.common.collect.Sets;
74
75@Path("routers")
76public class RouterWebResource extends AbstractWebResource {
77 private final Logger log = LoggerFactory.getLogger(RouterWebResource.class);
78 public static final String CREATE_FAIL = "Router is failed to create!";
79 public static final String UPDATE_FAIL = "Router is failed to update!";
80 public static final String GET_FAIL = "Router is failed to get!";
81 public static final String NOT_EXIST = "Router does not exist!";
82 public static final String DELETE_SUCCESS = "Router delete success!";
83 public static final String JSON_NOT_NULL = "JsonNode can not be null";
84 public static final String INTFACR_ADD_SUCCESS = "Interface add success";
85 public static final String INTFACR_DEL_SUCCESS = "Interface delete success";
86
87 @GET
88 @Produces(MediaType.APPLICATION_JSON)
89 public Response listRouters() {
90 Collection<Router> routers = get(RouterService.class).getRouters();
91 ObjectNode result = new ObjectMapper().createObjectNode();
92 result.set("routers", new RouterCodec().encode(routers, this));
93 return ok(result.toString()).build();
94 }
95
96 @GET
97 @Path("{routerUUID}")
98 @Produces(MediaType.APPLICATION_JSON)
99 public Response getRouter(@PathParam("routerUUID") String id,
100 @QueryParam("fields") List<String> fields) {
101
102 if (!get(RouterService.class).exists(RouterId.valueOf(id))) {
103 return Response.status(NOT_FOUND)
104 .entity("The Router does not exists").build();
105 }
106 Router sub = nullIsNotFound(get(RouterService.class)
lishuaid6f0c9e2015-12-16 11:40:01 +0800107 .getRouter(RouterId.valueOf(id)), NOT_EXIST);
jiangrui330b0c92015-11-28 14:09:50 +0800108
109 ObjectNode result = new ObjectMapper().createObjectNode();
110 if (fields.size() > 0) {
111 result.set("router",
112 new RouterCodec().extracFields(sub, this, fields));
113 } else {
114 result.set("router", new RouterCodec().encode(sub, this));
115 }
116 return ok(result.toString()).build();
117 }
118
119 @POST
120 @Produces(MediaType.APPLICATION_JSON)
121 @Consumes(MediaType.APPLICATION_JSON)
122 public Response createRouter(final InputStream input) {
123 try {
124 ObjectMapper mapper = new ObjectMapper();
125 JsonNode subnode = mapper.readTree(input);
126 Collection<Router> routers = createOrUpdateByInputStream(subnode);
127
128 Boolean result = nullIsNotFound((get(RouterService.class)
lishuaid6f0c9e2015-12-16 11:40:01 +0800129 .createRouters(routers)), CREATE_FAIL);
jiangrui330b0c92015-11-28 14:09:50 +0800130 if (!result) {
131 return Response.status(CONFLICT).entity(CREATE_FAIL).build();
132 }
133 return Response.status(CREATED).entity(result.toString()).build();
134
135 } catch (Exception e) {
136 return Response.status(BAD_REQUEST).entity(e.getMessage()).build();
137 }
138 }
139
140 @PUT
141 @Path("{routerUUID}")
142 @Produces(MediaType.APPLICATION_JSON)
143 @Consumes(MediaType.APPLICATION_JSON)
144 public Response updateRouter(@PathParam("routerUUID") String id,
145 final InputStream input) {
146 try {
147 ObjectMapper mapper = new ObjectMapper();
148 JsonNode subnode = mapper.readTree(input);
lishuaid6f0c9e2015-12-16 11:40:01 +0800149 Collection<Router> routers = changeUpdateJsonToSub(subnode, id);
jiangrui330b0c92015-11-28 14:09:50 +0800150 Boolean result = nullIsNotFound(get(RouterService.class)
151 .updateRouters(routers), UPDATE_FAIL);
152 if (!result) {
153 return Response.status(CONFLICT).entity(UPDATE_FAIL).build();
154 }
155 return ok(result.toString()).build();
156 } catch (Exception e) {
157 return Response.status(BAD_REQUEST).entity(e.getMessage()).build();
158 }
159 }
160
161 @Path("{routerUUID}")
162 @DELETE
163 public Response deleteSingleRouter(@PathParam("routerUUID") String id)
164 throws IOException {
165 try {
166 RouterId routerId = RouterId.valueOf(id);
167 Set<RouterId> routerIds = Sets.newHashSet(routerId);
168 get(RouterService.class).removeRouters(routerIds);
169 return Response.status(NO_CONTENT).entity(DELETE_SUCCESS).build();
170 } catch (Exception e) {
171 return Response.status(BAD_REQUEST).entity(e.getMessage()).build();
172 }
173 }
174
175 @PUT
176 @Path("{routerUUID}/add_router_interface")
177 @Produces(MediaType.APPLICATION_JSON)
178 @Consumes(MediaType.APPLICATION_JSON)
179 public Response addRouterInterface(@PathParam("routerUUID") String id,
180 final InputStream input) {
181 if (!get(RouterService.class).exists(RouterId.valueOf(id))) {
182 return Response.status(NOT_FOUND).entity(NOT_EXIST).build();
183 }
184 try {
185 ObjectMapper mapper = new ObjectMapper();
186 JsonNode subnode = mapper.readTree(input);
187 if (!subnode.hasNonNull("id")) {
188 throw new IllegalArgumentException("id should not be null");
189 } else if (subnode.get("id").asText().isEmpty()) {
190 throw new IllegalArgumentException("id should not be empty");
191 }
192 RouterId routerId = RouterId.valueOf(id);
193 if (!subnode.hasNonNull("subnet_id")) {
194 throw new IllegalArgumentException("subnet_id should not be null");
195 } else if (subnode.get("subnet_id").asText().isEmpty()) {
196 throw new IllegalArgumentException("subnet_id should not be empty");
197 }
lishuaid6f0c9e2015-12-16 11:40:01 +0800198 SubnetId subnetId = SubnetId
199 .subnetId(subnode.get("subnet_id").asText());
jiangrui330b0c92015-11-28 14:09:50 +0800200 if (!subnode.hasNonNull("tenant_id")) {
201 throw new IllegalArgumentException("tenant_id should not be null");
202 } else if (subnode.get("tenant_id").asText().isEmpty()) {
203 throw new IllegalArgumentException("tenant_id should not be empty");
204 }
lishuaid6f0c9e2015-12-16 11:40:01 +0800205 TenantId tenentId = TenantId
206 .tenantId(subnode.get("tenant_id").asText());
jiangrui330b0c92015-11-28 14:09:50 +0800207 if (!subnode.hasNonNull("port_id")) {
208 throw new IllegalArgumentException("port_id should not be null");
209 } else if (subnode.get("port_id").asText().isEmpty()) {
210 throw new IllegalArgumentException("port_id should not be empty");
211 }
lishuaid6f0c9e2015-12-16 11:40:01 +0800212 VirtualPortId portId = VirtualPortId
213 .portId(subnode.get("port_id").asText());
jiangrui330b0c92015-11-28 14:09:50 +0800214 RouterInterface routerInterface = RouterInterface
215 .routerInterface(subnetId, portId, routerId, tenentId);
216 get(RouterInterfaceService.class)
217 .addRouterInterface(routerInterface);
218 return ok(INTFACR_ADD_SUCCESS).build();
219 } catch (Exception e) {
220 return Response.status(BAD_REQUEST).entity(e.getMessage()).build();
221 }
222 }
223
224 @PUT
225 @Path("{routerUUID}/remove_router_interface")
226 @Produces(MediaType.APPLICATION_JSON)
227 @Consumes(MediaType.APPLICATION_JSON)
228 public Response removeRouterInterface(@PathParam("routerUUID") String id,
229 final InputStream input) {
230 if (!get(RouterService.class).exists(RouterId.valueOf(id))) {
231 return Response.status(NOT_FOUND).entity(NOT_EXIST).build();
232 }
233 try {
234 ObjectMapper mapper = new ObjectMapper();
235 JsonNode subnode = mapper.readTree(input);
236 if (!subnode.hasNonNull("id")) {
237 throw new IllegalArgumentException("id should not be null");
238 } else if (subnode.get("id").asText().isEmpty()) {
239 throw new IllegalArgumentException("id should not be empty");
240 }
241 RouterId routerId = RouterId.valueOf(id);
242 if (!subnode.hasNonNull("subnet_id")) {
243 throw new IllegalArgumentException("subnet_id should not be null");
244 } else if (subnode.get("subnet_id").asText().isEmpty()) {
245 throw new IllegalArgumentException("subnet_id should not be empty");
246 }
lishuaid6f0c9e2015-12-16 11:40:01 +0800247 SubnetId subnetId = SubnetId
248 .subnetId(subnode.get("subnet_id").asText());
jiangrui330b0c92015-11-28 14:09:50 +0800249 if (!subnode.hasNonNull("port_id")) {
250 throw new IllegalArgumentException("port_id should not be null");
251 } else if (subnode.get("port_id").asText().isEmpty()) {
252 throw new IllegalArgumentException("port_id should not be empty");
253 }
lishuaid6f0c9e2015-12-16 11:40:01 +0800254 VirtualPortId portId = VirtualPortId
255 .portId(subnode.get("port_id").asText());
jiangrui330b0c92015-11-28 14:09:50 +0800256 if (!subnode.hasNonNull("tenant_id")) {
257 throw new IllegalArgumentException("tenant_id should not be null");
258 } else if (subnode.get("tenant_id").asText().isEmpty()) {
259 throw new IllegalArgumentException("tenant_id should not be empty");
260 }
lishuaid6f0c9e2015-12-16 11:40:01 +0800261 TenantId tenentId = TenantId
262 .tenantId(subnode.get("tenant_id").asText());
jiangrui330b0c92015-11-28 14:09:50 +0800263 RouterInterface routerInterface = RouterInterface
264 .routerInterface(subnetId, portId, routerId, tenentId);
265 get(RouterInterfaceService.class)
266 .removeRouterInterface(routerInterface);
267 return ok(INTFACR_DEL_SUCCESS).build();
268 } catch (Exception e) {
269 return Response.status(BAD_REQUEST).entity(e.getMessage()).build();
270 }
271 }
272
273 private Collection<Router> createOrUpdateByInputStream(JsonNode subnode)
274 throws Exception {
275 checkNotNull(subnode, JSON_NOT_NULL);
276 JsonNode routerNode = subnode.get("routers");
277 if (routerNode == null) {
278 routerNode = subnode.get("router");
279 }
280 log.debug("routerNode is {}", routerNode.toString());
281
282 if (routerNode.isArray()) {
283 throw new Exception("only singleton requests allowed");
284 } else {
285 return changeJsonToSub(routerNode);
286 }
287 }
288
289 /**
290 * Returns a collection of floatingIps from floatingIpNodes.
291 *
292 * @param routerNode the router json node
293 * @return routers a collection of router
Bharat saraswald270b182015-12-01 01:53:06 +0530294 * @throws Exception when any argument is illegal
jiangrui330b0c92015-11-28 14:09:50 +0800295 */
296 public Collection<Router> changeJsonToSub(JsonNode routerNode)
297 throws Exception {
298 checkNotNull(routerNode, JSON_NOT_NULL);
299 Map<RouterId, Router> subMap = new HashMap<RouterId, Router>();
300 if (!routerNode.hasNonNull("id")) {
301 new IllegalArgumentException("id should not be null");
302 } else if (routerNode.get("id").asText().isEmpty()) {
303 throw new IllegalArgumentException("id should not be empty");
304 }
305 RouterId id = RouterId.valueOf(routerNode.get("id").asText());
306
307 if (!routerNode.hasNonNull("tenant_id")) {
308 throw new IllegalArgumentException("tenant_id should not be null");
309 } else if (routerNode.get("tenant_id").asText().isEmpty()) {
310 throw new IllegalArgumentException("tenant_id should not be empty");
311 }
lishuaid6f0c9e2015-12-16 11:40:01 +0800312 TenantId tenantId = TenantId
313 .tenantId(routerNode.get("tenant_id").asText());
jiangrui330b0c92015-11-28 14:09:50 +0800314
315 VirtualPortId gwPortId = null;
316 if (routerNode.hasNonNull("gw_port_id")) {
lishuaid6f0c9e2015-12-16 11:40:01 +0800317 gwPortId = VirtualPortId
318 .portId(routerNode.get("gw_port_id").asText());
jiangrui330b0c92015-11-28 14:09:50 +0800319 }
320
321 if (!routerNode.hasNonNull("status")) {
322 throw new IllegalArgumentException("status should not be null");
323 } else if (routerNode.get("status").asText().isEmpty()) {
324 throw new IllegalArgumentException("status should not be empty");
325 }
326 Status status = Status.valueOf(routerNode.get("status").asText());
327
328 String routerName = null;
329 if (routerNode.hasNonNull("name")) {
330 routerName = routerNode.get("name").asText();
331 }
332
333 boolean adminStateUp = true;
334 checkArgument(routerNode.get("admin_state_up").isBoolean(),
335 "admin_state_up should be boolean");
336 if (routerNode.hasNonNull("admin_state_up")) {
337 adminStateUp = routerNode.get("admin_state_up").asBoolean();
338 }
339 boolean distributed = false;
340 if (routerNode.hasNonNull("distributed")) {
341 distributed = routerNode.get("distributed").asBoolean();
342 }
343 RouterGateway gateway = null;
344 if (routerNode.hasNonNull("external_gateway_info")) {
lishuaid6f0c9e2015-12-16 11:40:01 +0800345 gateway = jsonNodeToGateway(routerNode
346 .get("external_gateway_info"));
347 }
348 List<String> routes = new ArrayList<String>();
349 DefaultRouter routerObj = new DefaultRouter(id, routerName,
350 adminStateUp, status,
351 distributed, gateway,
352 gwPortId, tenantId, routes);
353 subMap.put(id, routerObj);
354 return Collections.unmodifiableCollection(subMap.values());
355 }
356
357 /**
358 * Returns a collection of floatingIps from floatingIpNodes.
359 *
360 * @param subnode the router json node
361 * @param routerId the router identify
362 * @return routers a collection of router
363 * @throws Exception when any argument is illegal
364 */
365 public Collection<Router> changeUpdateJsonToSub(JsonNode subnode,
366 String routerId)
367 throws Exception {
368 checkNotNull(subnode, JSON_NOT_NULL);
369 checkNotNull(routerId, "routerId should not be null");
370 Map<RouterId, Router> subMap = new HashMap<RouterId, Router>();
371 JsonNode routerNode = subnode.get("router");
372 RouterId id = RouterId.valueOf(routerId);
373 Router sub = nullIsNotFound(get(RouterService.class).getRouter(id),
374 NOT_EXIST);
375 TenantId tenantId = sub.tenantId();
376
377 VirtualPortId gwPortId = null;
378 if (routerNode.hasNonNull("gw_port_id")) {
379 gwPortId = VirtualPortId
380 .portId(routerNode.get("gw_port_id").asText());
381 }
382 Status status = sub.status();
383
384 String routerName = routerNode.get("name").asText();
385
386 checkArgument(routerNode.get("admin_state_up").isBoolean(),
387 "admin_state_up should be boolean");
388 boolean adminStateUp = routerNode.get("admin_state_up").asBoolean();
389
390 boolean distributed = sub.distributed();
391 if (routerNode.hasNonNull("distributed")) {
392 distributed = routerNode.get("distributed").asBoolean();
393 }
394 RouterGateway gateway = sub.externalGatewayInfo();
395 if (routerNode.hasNonNull("external_gateway_info")) {
396 gateway = jsonNodeToGateway(routerNode
397 .get("external_gateway_info"));
jiangrui330b0c92015-11-28 14:09:50 +0800398 }
399 List<String> routes = new ArrayList<String>();
400 DefaultRouter routerObj = new DefaultRouter(id, routerName,
401 adminStateUp, status,
402 distributed, gateway,
403 gwPortId, tenantId, routes);
404 subMap.put(id, routerObj);
405 return Collections.unmodifiableCollection(subMap.values());
406 }
407
408 /**
409 * Changes JsonNode Gateway to the Gateway.
410 *
411 * @param gateway the gateway JsonNode
412 * @return gateway
413 */
414 private RouterGateway jsonNodeToGateway(JsonNode gateway) {
415 checkNotNull(gateway, JSON_NOT_NULL);
416 if (!gateway.hasNonNull("network_id")) {
417 throw new IllegalArgumentException("network_id should not be null");
418 } else if (gateway.get("network_id").asText().isEmpty()) {
419 throw new IllegalArgumentException("network_id should not be empty");
420 }
lishuaid6f0c9e2015-12-16 11:40:01 +0800421 TenantNetworkId networkId = TenantNetworkId
422 .networkId(gateway.get("network_id").asText());
jiangrui330b0c92015-11-28 14:09:50 +0800423
424 if (!gateway.hasNonNull("enable_snat")) {
425 throw new IllegalArgumentException("enable_snat should not be null");
426 } else if (gateway.get("enable_snat").asText().isEmpty()) {
427 throw new IllegalArgumentException("enable_snat should not be empty");
428 }
429 checkArgument(gateway.get("enable_snat").isBoolean(),
430 "enable_snat should be boolean");
431 boolean enableSnat = gateway.get("enable_snat").asBoolean();
432
433 if (!gateway.hasNonNull("external_fixed_ips")) {
lishuaid6f0c9e2015-12-16 11:40:01 +0800434 throw new IllegalArgumentException("external_fixed_ips should not be null");
jiangrui330b0c92015-11-28 14:09:50 +0800435 } else if (gateway.get("external_fixed_ips").isNull()) {
lishuaid6f0c9e2015-12-16 11:40:01 +0800436 throw new IllegalArgumentException("external_fixed_ips should not be empty");
jiangrui330b0c92015-11-28 14:09:50 +0800437 }
438 Collection<FixedIp> fixedIpList = jsonNodeToFixedIp(gateway
439 .get("external_fixed_ips"));
lishuaid6f0c9e2015-12-16 11:40:01 +0800440 RouterGateway gatewayObj = RouterGateway
441 .routerGateway(networkId, enableSnat, fixedIpList);
jiangrui330b0c92015-11-28 14:09:50 +0800442 return gatewayObj;
443 }
444
445 /**
446 * Changes JsonNode fixedIp to a collection of the fixedIp.
447 *
448 * @param fixedIp the allocationPools JsonNode
449 * @return a collection of fixedIp
450 */
451 private Collection<FixedIp> jsonNodeToFixedIp(JsonNode fixedIp) {
452 checkNotNull(fixedIp, JSON_NOT_NULL);
453 ConcurrentMap<Integer, FixedIp> fixedIpMaps = Maps.newConcurrentMap();
454 Integer i = 0;
455 for (JsonNode node : fixedIp) {
456 if (!node.hasNonNull("subnet_id")) {
457 throw new IllegalArgumentException("subnet_id should not be null");
458 } else if (node.get("subnet_id").asText().isEmpty()) {
459 throw new IllegalArgumentException("subnet_id should not be empty");
460 }
lishuaid6f0c9e2015-12-16 11:40:01 +0800461 SubnetId subnetId = SubnetId
462 .subnetId(node.get("subnet_id").asText());
jiangrui330b0c92015-11-28 14:09:50 +0800463 if (!node.hasNonNull("ip_address")) {
464 throw new IllegalArgumentException("ip_address should not be null");
465 } else if (node.get("ip_address").asText().isEmpty()) {
466 throw new IllegalArgumentException("ip_address should not be empty");
467 }
lishuaid6f0c9e2015-12-16 11:40:01 +0800468 IpAddress ipAddress = IpAddress
469 .valueOf(node.get("ip_address").asText());
jiangrui330b0c92015-11-28 14:09:50 +0800470 FixedIp fixedIpObj = FixedIp.fixedIp(subnetId, ipAddress);
471
472 fixedIpMaps.putIfAbsent(i, fixedIpObj);
473 i++;
474 }
475 return Collections.unmodifiableCollection(fixedIpMaps.values());
476 }
477
478 /**
479 * Returns the specified item if that items is null; otherwise throws not
480 * found exception.
481 *
482 * @param item item to check
483 * @param <T> item type
484 * @param message not found message
485 * @return item if not null
486 * @throws org.onlab.util.ItemNotFoundException if item is null
487 */
488 protected <T> T nullIsNotFound(T item, String message) {
489 if (item == null) {
490 throw new ItemNotFoundException(message);
491 }
492 return item;
493 }
494}