blob: b0951f47f268a94c38e9d387cd571b6229af7b9d [file] [log] [blame]
Madan Jampani38a88212015-09-15 11:21:27 -07001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2015-present Open Networking Foundation
Madan Jampani38a88212015-09-15 11:21:27 -07003 *
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.INTERNAL_SERVER_ERROR;
lishuai0f47f342015-09-16 11:38:48 +080021import static javax.ws.rs.core.Response.Status.NOT_FOUND;
Ray Milkey86ee5e82018-04-02 15:33:07 -070022import static org.onlab.util.Tools.readTreeFromStream;
Madan Jampani38a88212015-09-15 11:21:27 -070023
24import java.io.IOException;
25import java.io.InputStream;
26import java.util.Collections;
27import java.util.HashMap;
28import java.util.HashSet;
29import java.util.Map;
30import java.util.Set;
31import java.util.concurrent.ConcurrentMap;
32
33import javax.ws.rs.Consumes;
34import javax.ws.rs.DELETE;
35import javax.ws.rs.GET;
36import javax.ws.rs.POST;
37import javax.ws.rs.PUT;
38import javax.ws.rs.Path;
39import javax.ws.rs.PathParam;
40import javax.ws.rs.Produces;
41import javax.ws.rs.core.MediaType;
42import javax.ws.rs.core.Response;
43
44import org.onlab.packet.IpAddress;
45import org.onlab.packet.IpAddress.Version;
46import org.onlab.packet.IpPrefix;
47import org.onlab.util.ItemNotFoundException;
48import org.onosproject.rest.AbstractWebResource;
49import org.onosproject.vtnrsc.AllocationPool;
50import org.onosproject.vtnrsc.DefaultAllocationPool;
51import org.onosproject.vtnrsc.DefaultHostRoute;
52import org.onosproject.vtnrsc.DefaultSubnet;
53import org.onosproject.vtnrsc.HostRoute;
54import org.onosproject.vtnrsc.Subnet;
55import org.onosproject.vtnrsc.SubnetId;
56import org.onosproject.vtnrsc.TenantId;
57import org.onosproject.vtnrsc.TenantNetworkId;
58import org.onosproject.vtnrsc.Subnet.Mode;
59import org.onosproject.vtnrsc.subnet.SubnetService;
onosjcc36e04a82015-10-27 16:46:37 +080060import org.onosproject.vtnweb.web.SubnetCodec;
Madan Jampani38a88212015-09-15 11:21:27 -070061import org.slf4j.Logger;
62import org.slf4j.LoggerFactory;
63
64import com.fasterxml.jackson.databind.JsonNode;
65import com.fasterxml.jackson.databind.ObjectMapper;
66import com.fasterxml.jackson.databind.node.ObjectNode;
67import com.google.common.collect.Maps;
68import com.google.common.collect.Sets;
69
70@Path("subnets")
71public class SubnetWebResource extends AbstractWebResource {
72 private final Logger log = LoggerFactory.getLogger(SubnetWebResource.class);
bobzhouf8da9652015-10-14 11:23:18 +080073 public static final String SUBNET_NOT_CREATED = "Subnet failed to create!";
74 public static final String SUBNET_NOT_FOUND = "Subnet is not found";
Madan Jampani38a88212015-09-15 11:21:27 -070075 public static final String JSON_NOT_NULL = "JsonNode can not be null";
76
77 @GET
78 @Produces(MediaType.APPLICATION_JSON)
Wu wenbind0b119f2016-05-11 18:03:41 +080079 @Consumes(MediaType.APPLICATION_JSON)
Madan Jampani38a88212015-09-15 11:21:27 -070080 public Response listSubnets() {
81 Iterable<Subnet> subnets = get(SubnetService.class).getSubnets();
82 ObjectNode result = new ObjectMapper().createObjectNode();
83 result.set("subnets", new SubnetCodec().encode(subnets, this));
84 return ok(result.toString()).build();
85 }
86
87 @GET
88 @Path("{subnetUUID}")
89 @Produces(MediaType.APPLICATION_JSON)
Wu wenbind0b119f2016-05-11 18:03:41 +080090 @Consumes(MediaType.APPLICATION_JSON)
Madan Jampani38a88212015-09-15 11:21:27 -070091 public Response getSubnet(@PathParam("subnetUUID") String id) {
92
93 if (!get(SubnetService.class).exists(SubnetId.subnetId(id))) {
lishuai0f47f342015-09-16 11:38:48 +080094 return Response.status(NOT_FOUND)
95 .entity(SUBNET_NOT_FOUND).build();
Madan Jampani38a88212015-09-15 11:21:27 -070096 }
97 Subnet sub = nullIsNotFound(get(SubnetService.class)
98 .getSubnet(SubnetId.subnetId(id)),
99 SUBNET_NOT_FOUND);
100
101 ObjectNode result = new ObjectMapper().createObjectNode();
102 result.set("subnet", new SubnetCodec().encode(sub, this));
103 return ok(result.toString()).build();
104 }
105
106 @POST
107 @Produces(MediaType.APPLICATION_JSON)
108 @Consumes(MediaType.APPLICATION_JSON)
109 public Response createSubnet(final InputStream input) {
110
111 try {
112 ObjectMapper mapper = new ObjectMapper();
Ray Milkey86ee5e82018-04-02 15:33:07 -0700113 JsonNode subnode = readTreeFromStream(mapper, input);
Madan Jampani38a88212015-09-15 11:21:27 -0700114 Iterable<Subnet> subnets = createOrUpdateByInputStream(subnode);
115 Boolean result = nullIsNotFound((get(SubnetService.class)
116 .createSubnets(subnets)),
bobzhouf8da9652015-10-14 11:23:18 +0800117 SUBNET_NOT_CREATED);
Madan Jampani38a88212015-09-15 11:21:27 -0700118
119 if (!result) {
lishuai0f47f342015-09-16 11:38:48 +0800120 return Response.status(INTERNAL_SERVER_ERROR)
bobzhouf8da9652015-10-14 11:23:18 +0800121 .entity(SUBNET_NOT_CREATED).build();
Madan Jampani38a88212015-09-15 11:21:27 -0700122 }
123 return Response.status(202).entity(result.toString()).build();
124 } catch (Exception e) {
125 return Response.status(INTERNAL_SERVER_ERROR).entity(e.toString())
126 .build();
127 }
128 }
129
130 @PUT
131 @Path("{subnetUUID}")
132 @Produces(MediaType.APPLICATION_JSON)
133 @Consumes(MediaType.APPLICATION_JSON)
134 public Response updateSubnet(@PathParam("id") String id,
135 final InputStream input) {
136 try {
137 ObjectMapper mapper = new ObjectMapper();
Ray Milkey86ee5e82018-04-02 15:33:07 -0700138 JsonNode subnode = readTreeFromStream(mapper, input);
Madan Jampani38a88212015-09-15 11:21:27 -0700139 Iterable<Subnet> subnets = createOrUpdateByInputStream(subnode);
140 Boolean result = nullIsNotFound(get(SubnetService.class)
141 .updateSubnets(subnets), SUBNET_NOT_FOUND);
142 if (!result) {
lishuai0f47f342015-09-16 11:38:48 +0800143 return Response.status(INTERNAL_SERVER_ERROR)
144 .entity(SUBNET_NOT_FOUND).build();
Madan Jampani38a88212015-09-15 11:21:27 -0700145 }
146 return Response.status(203).entity(result.toString()).build();
147 } catch (Exception e) {
148 return Response.status(INTERNAL_SERVER_ERROR).entity(e.toString())
149 .build();
150 }
151 }
152
Madan Jampani38a88212015-09-15 11:21:27 -0700153 @DELETE
Wu wenbinb0bd6132016-05-10 19:20:23 +0800154 @Path("{subnetUUID}")
155 @Consumes(MediaType.APPLICATION_JSON)
156 @Produces(MediaType.APPLICATION_JSON)
Madan Jampani38a88212015-09-15 11:21:27 -0700157 public Response deleteSingleSubnet(@PathParam("subnetUUID") String id)
158 throws IOException {
159 try {
160 SubnetId subId = SubnetId.subnetId(id);
161 Set<SubnetId> subIds = new HashSet<>();
162 subIds.add(subId);
163 get(SubnetService.class).removeSubnets(subIds);
Jian Lic2a542b2016-05-10 11:48:19 -0700164 return Response.noContent().entity("SUCCESS").build();
Madan Jampani38a88212015-09-15 11:21:27 -0700165 } catch (Exception e) {
166 return Response.status(INTERNAL_SERVER_ERROR).entity(e.toString())
167 .build();
168 }
169 }
170
171 private Iterable<Subnet> createOrUpdateByInputStream(JsonNode subnode) {
172 checkNotNull(subnode, JSON_NOT_NULL);
173 Iterable<Subnet> subnets = null;
174 JsonNode subnetNodes = subnode.get("subnets");
175 if (subnetNodes == null) {
176 subnetNodes = subnode.get("subnet");
177 }
178 log.debug("subnetNodes is {}", subnetNodes.toString());
179 if (subnetNodes.isArray()) {
180 subnets = changeJsonToSubs(subnetNodes);
181 } else {
182 subnets = changeJsonToSub(subnetNodes);
183 }
184 return subnets;
185 }
186
187 /**
188 * Returns a collection of subnets from subnetNodes.
189 *
190 * @param subnetNodes the subnet json node
191 * @return subnets a collection of subnets
192 */
193 public Iterable<Subnet> changeJsonToSubs(JsonNode subnetNodes) {
194 checkNotNull(subnetNodes, JSON_NOT_NULL);
195 Map<SubnetId, Subnet> subMap = new HashMap<>();
196 for (JsonNode subnetNode : subnetNodes) {
197 if (!subnetNode.hasNonNull("id")) {
198 return null;
199 }
200 SubnetId id = SubnetId.subnetId(subnetNode.get("id").asText());
201 String subnetName = subnetNode.get("name").asText();
202 TenantId tenantId = TenantId
203 .tenantId(subnetNode.get("tenant_id").asText());
204 TenantNetworkId networkId = TenantNetworkId
205 .networkId(subnetNode.get("network_id").asText());
lishuai0f47f342015-09-16 11:38:48 +0800206 String version = subnetNode.get("ip_version").asText();
207 Version ipVersion;
208 switch (version) {
209 case "4":
210 ipVersion = Version.INET;
211 break;
212 case "6":
213 ipVersion = Version.INET;
214 break;
215 default:
216 throw new IllegalArgumentException("ipVersion should be 4 or 6.");
217 }
Madan Jampani38a88212015-09-15 11:21:27 -0700218 IpPrefix cidr = IpPrefix.valueOf(subnetNode.get("cidr").asText());
219 IpAddress gatewayIp = IpAddress
220 .valueOf(subnetNode.get("gateway_ip").asText());
221 Boolean dhcpEnabled = subnetNode.get("enable_dhcp").asBoolean();
222 Boolean shared = subnetNode.get("shared").asBoolean();
223 JsonNode hostRoutes = subnetNode.get("host_routes");
224 Iterable<HostRoute> hostRoutesIt = jsonNodeToHostRoutes(hostRoutes);
225 JsonNode allocationPools = subnetNode.get("allocation_pools");
226 Iterable<AllocationPool> allocationPoolsIt = jsonNodeToAllocationPools(allocationPools);
227 Mode ipV6AddressMode = Mode
228 .valueOf(subnetNode.get("ipv6_address_mode").asText());
229 Mode ipV6RaMode = Mode
230 .valueOf(subnetNode.get("ipv6_ra_mode").asText());
231 Subnet subnet = new DefaultSubnet(id, subnetName, networkId,
232 tenantId, ipVersion, cidr,
233 gatewayIp, dhcpEnabled, shared,
234 Sets.newHashSet(hostRoutesIt), ipV6AddressMode,
235 ipV6RaMode, Sets.newHashSet(allocationPoolsIt));
236 subMap.put(id, subnet);
237 }
238 return Collections.unmodifiableCollection(subMap.values());
239 }
240
241 /**
242 * Returns a collection of subnets from subnetNodes.
243 *
244 * @param subnetNodes the subnet json node
245 * @return subnets a collection of subnets
246 */
247 public Iterable<Subnet> changeJsonToSub(JsonNode subnetNodes) {
248 checkNotNull(subnetNodes, JSON_NOT_NULL);
249 checkArgument(subnetNodes.get("enable_dhcp").isBoolean(), "enable_dhcp should be boolean");
250 checkArgument(subnetNodes.get("shared").isBoolean(), "shared should be boolean");
251 Map<SubnetId, Subnet> subMap = new HashMap<>();
252 if (!subnetNodes.hasNonNull("id")) {
253 return null;
254 }
255 SubnetId id = SubnetId.subnetId(subnetNodes.get("id").asText());
256 String subnetName = subnetNodes.get("name").asText();
257 TenantId tenantId = TenantId
258 .tenantId(subnetNodes.get("tenant_id").asText());
259 TenantNetworkId networkId = TenantNetworkId
260 .networkId(subnetNodes.get("network_id").asText());
261 String version = subnetNodes.get("ip_version").asText();
262 Version ipVersion;
263 switch (version) {
264 case "4":
265 ipVersion = Version.INET;
266 break;
267 case "6":
268 ipVersion = Version.INET;
269 break;
270 default:
271 throw new IllegalArgumentException("ipVersion should be 4 or 6.");
272 }
273
274 IpPrefix cidr = IpPrefix.valueOf(subnetNodes.get("cidr").asText());
275 IpAddress gatewayIp = IpAddress
276 .valueOf(subnetNodes.get("gateway_ip").asText());
277 Boolean dhcpEnabled = subnetNodes.get("enable_dhcp").asBoolean();
278 Boolean shared = subnetNodes.get("shared").asBoolean();
279 JsonNode hostRoutes = subnetNodes.get("host_routes");
280 Iterable<HostRoute> hostRoutesIt = jsonNodeToHostRoutes(hostRoutes);
281 JsonNode allocationPools = subnetNodes.get("allocation_pools");
282 Iterable<AllocationPool> allocationPoolsIt = jsonNodeToAllocationPools(allocationPools);
283
284 Mode ipV6AddressMode = getMode(subnetNodes.get("ipv6_address_mode")
285 .asText());
286 Mode ipV6RaMode = getMode(subnetNodes.get("ipv6_ra_mode").asText());
287
288 Subnet subnet = new DefaultSubnet(id, subnetName, networkId, tenantId,
289 ipVersion, cidr, gatewayIp,
290 dhcpEnabled, shared, Sets.newHashSet(hostRoutesIt),
291 ipV6AddressMode, ipV6RaMode,
292 Sets.newHashSet(allocationPoolsIt));
293 subMap.put(id, subnet);
294 return Collections.unmodifiableCollection(subMap.values());
295 }
296
297 /**
298 * Gets ipv6_address_mode or ipv6_ra_mode type.
299 *
300 * @param mode the String value in JsonNode
301 * @return ipV6Mode Mode of the ipV6Mode
302 */
303 private Mode getMode(String mode) {
304 Mode ipV6Mode;
305 if (mode == null) {
306 return null;
307 }
308 switch (mode) {
309 case "dhcpv6-stateful":
310 ipV6Mode = Mode.DHCPV6_STATEFUL;
311 break;
312 case "dhcpv6-stateless":
313 ipV6Mode = Mode.DHCPV6_STATELESS;
314 break;
315 case "slaac":
316 ipV6Mode = Mode.SLAAC;
317 break;
318 default:
319 ipV6Mode = null;
320 }
321 return ipV6Mode;
322 }
323
324 /**
325 * Changes JsonNode alocPools to a collection of the alocPools.
326 *
327 * @param allocationPools the allocationPools JsonNode
328 * @return a collection of allocationPools
329 */
330 public Iterable<AllocationPool> jsonNodeToAllocationPools(JsonNode allocationPools) {
331 checkNotNull(allocationPools, JSON_NOT_NULL);
332 ConcurrentMap<Integer, AllocationPool> alocplMaps = Maps
333 .newConcurrentMap();
334 Integer i = 0;
335 for (JsonNode node : allocationPools) {
336 IpAddress startIp = IpAddress.valueOf(node.get("start").asText());
337 IpAddress endIp = IpAddress.valueOf(node.get("end").asText());
338 AllocationPool alocPls = new DefaultAllocationPool(startIp, endIp);
339 alocplMaps.putIfAbsent(i, alocPls);
340 i++;
341 }
342 return Collections.unmodifiableCollection(alocplMaps.values());
343 }
344
345 /**
346 * Changes hostRoutes JsonNode to a collection of the hostRoutes.
347 *
348 * @param hostRoutes the hostRoutes json node
349 * @return a collection of hostRoutes
350 */
351 public Iterable<HostRoute> jsonNodeToHostRoutes(JsonNode hostRoutes) {
352 checkNotNull(hostRoutes, JSON_NOT_NULL);
353 ConcurrentMap<Integer, HostRoute> hostRouteMaps = Maps
354 .newConcurrentMap();
355 Integer i = 0;
356 for (JsonNode node : hostRoutes) {
357 IpAddress nexthop = IpAddress.valueOf(node.get("nexthop").asText());
358 IpPrefix destination = IpPrefix.valueOf(node.get("destination")
359 .asText());
360 HostRoute hostRoute = new DefaultHostRoute(nexthop, destination);
361 hostRouteMaps.putIfAbsent(i, hostRoute);
362 i++;
363 }
364 return Collections.unmodifiableCollection(hostRouteMaps.values());
365 }
366
367 /**
368 * Returns the specified item if that items is null; otherwise throws not
369 * found exception.
370 *
371 * @param item item to check
372 * @param <T> item type
373 * @param message not found message
374 * @return item if not null
375 * @throws org.onlab.util.ItemNotFoundException if item is null
376 */
377 protected <T> T nullIsNotFound(T item, String message) {
378 if (item == null) {
379 throw new ItemNotFoundException(message);
380 }
381 return item;
382 }
383
384}