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