blob: 4867c908cd2c12a77924b7fbdf6d79d93da8dca1 [file] [log] [blame]
Andreas Pantelopouloscd339592018-02-23 14:18:00 -08001/*
2 * Copyright 2015-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 */
16package org.onosproject.segmentrouting.web;
17
Andreas Pantelopoulosff691b72018-03-12 16:30:20 -070018import com.fasterxml.jackson.databind.JsonNode;
Andreas Pantelopouloscd339592018-02-23 14:18:00 -080019import com.fasterxml.jackson.databind.ObjectMapper;
Andreas Pantelopoulosff691b72018-03-12 16:30:20 -070020import com.fasterxml.jackson.databind.node.ArrayNode;
Andreas Pantelopouloscd339592018-02-23 14:18:00 -080021import com.fasterxml.jackson.databind.node.ObjectNode;
Laszlo Pappc5e85bb2018-04-04 16:17:52 +010022import org.apache.commons.lang3.tuple.Pair;
Andreas Pantelopoulosff691b72018-03-12 16:30:20 -070023import org.onlab.util.ItemNotFoundException;
Andreas Pantelopouloscd339592018-02-23 14:18:00 -080024import org.onosproject.rest.AbstractWebResource;
25import org.onosproject.segmentrouting.SegmentRoutingService;
26import org.onosproject.segmentrouting.pwaas.DefaultL2TunnelDescription;
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -070027import org.onosproject.segmentrouting.pwaas.L2TunnelDescription;
Andreas Pantelopouloscd339592018-02-23 14:18:00 -080028import org.onosproject.segmentrouting.pwaas.L2TunnelPolicy;
29import org.onosproject.segmentrouting.pwaas.L2Tunnel;
30import org.onosproject.segmentrouting.pwaas.L2TunnelHandler;
31import org.slf4j.Logger;
32import org.slf4j.LoggerFactory;
33
34import javax.ws.rs.Consumes;
35import javax.ws.rs.DELETE;
36import javax.ws.rs.GET;
37import javax.ws.rs.POST;
38import javax.ws.rs.Path;
39import javax.ws.rs.Produces;
40import javax.ws.rs.core.MediaType;
41import javax.ws.rs.core.Response;
42import java.io.IOException;
43import java.io.InputStream;
Andreas Pantelopoulosff691b72018-03-12 16:30:20 -070044import java.util.ArrayList;
Andreas Pantelopouloscd339592018-02-23 14:18:00 -080045import java.util.List;
46import java.util.stream.Collectors;
47
Andreas Pantelopoulosff691b72018-03-12 16:30:20 -070048import static org.onlab.util.Tools.nullIsIllegal;
Ray Milkey86ee5e82018-04-02 15:33:07 -070049import static org.onlab.util.Tools.readTreeFromStream;
Andreas Pantelopoulosff691b72018-03-12 16:30:20 -070050
Andreas Pantelopouloscd339592018-02-23 14:18:00 -080051/**
52 * Query, create and remove pseudowires.
53 */
54@Path("pseudowire")
55public class PseudowireWebResource extends AbstractWebResource {
56
57 private static final PseudowireCodec PSEUDOWIRE_CODEC = new PseudowireCodec();
Andreas Pantelopoulosff691b72018-03-12 16:30:20 -070058 public static final String PWS = "pseudowires";
59 private static final String PWS_KEY_ERROR = "Pseudowires key must be present.";
Andreas Pantelopouloscd339592018-02-23 14:18:00 -080060
61 private static Logger log = LoggerFactory
62 .getLogger(PseudowireWebResource.class);
63
64 /**
65 * Get all pseudowires.
66 * Returns an array of pseudowires.
67 *
68 * @return status of OK
69 */
70 @GET
71 @Produces(MediaType.APPLICATION_JSON)
72 public Response getPseudowire() {
73 SegmentRoutingService srService = get(SegmentRoutingService.class);
74
75 log.debug("Fetching pseudowires form rest api!");
76
77 List<L2TunnelPolicy> policies = srService.getL2Policies();
78 List<L2Tunnel> tunnels = srService.getL2Tunnels();
79 List<DefaultL2TunnelDescription> pseudowires = tunnels.stream()
80 .map(l2Tunnel -> {
81 L2TunnelPolicy policy = null;
82 for (L2TunnelPolicy l2Policy : policies) {
83 if (l2Policy.tunnelId() == l2Tunnel.tunnelId()) {
84 policy = l2Policy;
85 break;
86 }
87 }
88
89 // return a copy
90 return new DefaultL2TunnelDescription(l2Tunnel, policy);
91 })
92 .collect(Collectors.toList());
93
94 ObjectNode result = new ObjectMapper().createObjectNode();
95 result.set("pseudowires", new PseudowireCodec().encode(pseudowires, this));
96
97 return ok(result.toString()).build();
98 }
99
100 /**
101 * Create a new pseudowire.
102 *
103 * @param input JSON stream for pseudowire to create
104 * @return Response with appropriate status
105 * @throws IOException Throws IO exception.
106 * @onos.rsModel PseudowireCreate
107 */
108 @POST
109 @Consumes(MediaType.APPLICATION_JSON)
110 public Response createPseudowire(InputStream input) throws IOException {
111
112 ObjectMapper mapper = new ObjectMapper();
Ray Milkey86ee5e82018-04-02 15:33:07 -0700113 ObjectNode pseudowireJson = readTreeFromStream(mapper, input);
Andreas Pantelopouloscd339592018-02-23 14:18:00 -0800114 SegmentRoutingService srService = get(SegmentRoutingService.class);
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -0700115 List<Pair<DefaultL2TunnelDescription, String>> failed = new ArrayList<>();
116 List<Pair<JsonNode, String>> undecoded = new ArrayList<>();
Andreas Pantelopouloscd339592018-02-23 14:18:00 -0800117
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -0700118 DefaultL2TunnelDescription pseudowire;
119 try {
120 pseudowire = PSEUDOWIRE_CODEC.decode(pseudowireJson, this);
121
122 // pseudowire decoded, try to instantiate it, if we fail add it to failed list
123 long tunId = pseudowire.l2Tunnel().tunnelId();
124 log.debug("Creating pseudowire {} from rest api!", tunId);
125
126 L2TunnelHandler.Result res = srService.addPseudowire(pseudowire);
127 if (res != L2TunnelHandler.Result.SUCCESS) {
128 log.error("Could not create pseudowire {} : {}", pseudowire.l2Tunnel().tunnelId(),
129 res.getSpecificError());
130 failed.add(Pair.of(pseudowire, res.getSpecificError()));
131 }
132 } catch (IllegalArgumentException e) {
133 log.debug("Pseudowire could not be decoded : {}", e.getMessage());
134 undecoded.add(Pair.of(pseudowireJson, e.getMessage()));
Andreas Pantelopouloscd339592018-02-23 14:18:00 -0800135 }
136
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -0700137 if ((failed.size() == 0) && (undecoded.size() == 0)) {
138 // pseudowire instantiated correctly
139 return Response.ok().build();
140 } else {
141 // failed to decode or instantiate pseudowire, return the reason
142 PseudowireCodec pwCodec = new PseudowireCodec();
143 ObjectNode result = pwCodec.encodeFailedPseudowires(failed, undecoded, this);
144 return Response.serverError().entity(result).build();
Andreas Pantelopoulosff691b72018-03-12 16:30:20 -0700145 }
146 }
147
148 /**
149 * Create a bulk of pseudowires.
150 *
151 * @param input JSON stream for pseudowires to create
152 * @return Response with appropriate status
153 * @throws IOException Throws IO exception.
154 * @onos.rsModel PseudowireCreateBulk
155 */
156 @POST
157 @Path("/bulk")
158 @Consumes(MediaType.APPLICATION_JSON)
159 public Response createPseudowiresBulk(InputStream input) throws IOException {
160
161 ObjectMapper mapper = new ObjectMapper();
Ray Milkey86ee5e82018-04-02 15:33:07 -0700162 ObjectNode pseudowireJson = readTreeFromStream(mapper, input);
Andreas Pantelopoulosff691b72018-03-12 16:30:20 -0700163 SegmentRoutingService srService = get(SegmentRoutingService.class);
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -0700164 Pair<List<Pair<JsonNode, String>>, List<L2TunnelDescription>> pseudowires;
Andreas Pantelopoulosff691b72018-03-12 16:30:20 -0700165
166 try {
167 ArrayNode pseudowiresArray = nullIsIllegal((ArrayNode) pseudowireJson.get(PWS), PWS_KEY_ERROR);
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -0700168 // get two lists, first one contains pseudowires that we were unable to decode
169 // that have faulty arguments, second one contains pseudowires that we decoded
170 // succesfully
171 pseudowires = PSEUDOWIRE_CODEC.decodePws(pseudowiresArray, this);
Andreas Pantelopoulosff691b72018-03-12 16:30:20 -0700172 } catch (ItemNotFoundException e) {
173 return Response.serverError().status(Response.Status.BAD_REQUEST).build();
174 }
175
176 log.debug("Creating pseudowires {} from rest api!", pseudowires);
Andreas Pantelopoulosffe69742018-03-20 13:58:49 -0700177 List<Pair<DefaultL2TunnelDescription, String>> failed = new ArrayList<>();
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -0700178 for (L2TunnelDescription pw : pseudowires.getRight()) {
Andreas Pantelopoulosffe69742018-03-20 13:58:49 -0700179 L2TunnelHandler.Result res = srService.addPseudowire(pw);
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -0700180 if (res != L2TunnelHandler.Result.SUCCESS) {
181 log.error("Could not create pseudowire {} : {}", pw.l2Tunnel().tunnelId(), res.getSpecificError());
182 failed.add(Pair.of((DefaultL2TunnelDescription) pw, res.getSpecificError()));
Andreas Pantelopoulosffe69742018-03-20 13:58:49 -0700183 }
184 }
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -0700185 List<Pair<JsonNode, String>> undecodedPws = pseudowires.getLeft();
Andreas Pantelopoulosff691b72018-03-12 16:30:20 -0700186
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -0700187 if ((failed.size() == 0) && (undecodedPws.size() == 0)) {
188 // all pseudowires were decoded and instantiated succesfully
Andreas Pantelopoulosffe69742018-03-20 13:58:49 -0700189 return Response.ok().build();
190 } else {
191 PseudowireCodec pwCodec = new PseudowireCodec();
192 // some failed, need to report them to user
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -0700193 ObjectNode result = pwCodec.encodeFailedPseudowires(failed, undecodedPws, this);
Andreas Pantelopoulosffe69742018-03-20 13:58:49 -0700194 return Response.serverError().entity(result).build();
Andreas Pantelopouloscd339592018-02-23 14:18:00 -0800195 }
196 }
197
198 /**
199 * Delete a pseudowire.
200 *
201 * @param input JSON stream for pseudowire to delete
202 * @return Response with appropriate status
203 * @throws IOException Throws IO exception.
204 * @onos.rsModel PseudowireDelete
205 */
206 @DELETE
207 @Consumes(MediaType.APPLICATION_JSON)
208 public Response removePseudowire(InputStream input) throws IOException {
209
210 ObjectMapper mapper = new ObjectMapper();
Ray Milkey86ee5e82018-04-02 15:33:07 -0700211 ObjectNode pseudowireJson = readTreeFromStream(mapper, input);
Andreas Pantelopouloscd339592018-02-23 14:18:00 -0800212 SegmentRoutingService srService = get(SegmentRoutingService.class);
213
214 Integer pseudowireId = PSEUDOWIRE_CODEC.decodeId(pseudowireJson);
215 if (pseudowireId == null) {
216 return Response.serverError().status(Response.Status.BAD_REQUEST).build();
217 }
218
Andreas Pantelopoulosff691b72018-03-12 16:30:20 -0700219 log.debug("Deleting pseudowire {} from rest api!", pseudowireId);
Andreas Pantelopouloscd339592018-02-23 14:18:00 -0800220
221 L2TunnelHandler.Result res = srService.removePseudowire(pseudowireId);
222 switch (res) {
Andreas Pantelopoulosffe69742018-03-20 13:58:49 -0700223 case WRONG_PARAMETERS:
224 case INTERNAL_ERROR:
225 log.error("Pseudowire {} could not be removed : {}",
226 pseudowireId, res.getSpecificError());
Andreas Pantelopouloscd339592018-02-23 14:18:00 -0800227 return Response.noContent().build();
228 case SUCCESS:
Andreas Pantelopoulosff691b72018-03-12 16:30:20 -0700229 log.debug("Pseudowire {} was removed succesfully!", pseudowireId);
Andreas Pantelopouloscd339592018-02-23 14:18:00 -0800230 return Response.noContent().build();
231 default:
232 return Response.noContent().build();
233 }
234 }
Andreas Pantelopoulosff691b72018-03-12 16:30:20 -0700235
236 /**
237 * Delete a bulk of pseudowires.
238 *
239 * @param input JSON stream for pseudowires to delete
240 * @return Response with appropriate status
241 * @throws IOException Throws IO exception.
242 * @onos.rsModel PseudowireDeleteBulk
243 */
244 @DELETE
245 @Path("/bulk")
246 @Consumes(MediaType.APPLICATION_JSON)
247 public Response removePseudowiresBulk(InputStream input) throws IOException {
248
249 ObjectMapper mapper = new ObjectMapper();
Ray Milkey86ee5e82018-04-02 15:33:07 -0700250 ObjectNode pseudowireJson = readTreeFromStream(mapper, input);
Andreas Pantelopoulosff691b72018-03-12 16:30:20 -0700251 SegmentRoutingService srService = get(SegmentRoutingService.class);
252
253 List<Integer> ids = new ArrayList<>();
254
255 // try to parse all ids, if key is not present, or if an id is not an int
256 // throw an exception and stop process
257 try {
258 for (JsonNode node : pseudowireJson.withArray(PWS)) {
259 Integer idToDelete = PseudowireCodec.decodeId((ObjectNode) node);
260 if (idToDelete == null) {
261 log.error("Error when parsing pseudowire for deletion in REST API.");
262 throw new IllegalArgumentException("Id of pseudowire should be an integer!");
263 }
264 ids.add(idToDelete);
265 }
266 } catch (IllegalArgumentException e) {
267 log.error("Pseudowire ID should be an integer.");
268 return Response.serverError().status(Response.Status.BAD_REQUEST).build();
269 } catch (UnsupportedOperationException e) {
270 log.error("Pseudowires for deletion should be an array of pseudowire ids.");
271 return Response.serverError().status(Response.Status.BAD_REQUEST).build();
272 }
273
274 for (Integer pseudowireId : ids) {
275 L2TunnelHandler.Result res = srService.removePseudowire(pseudowireId);
276 switch (res) {
Andreas Pantelopoulosffe69742018-03-20 13:58:49 -0700277 case WRONG_PARAMETERS:
278 case INTERNAL_ERROR:
279 log.error("Pseudowire {} could not be removed, internal error : {}",
280 pseudowireId, res.getSpecificError());
Ray Milkeyb994f8d2018-03-14 09:55:13 -0700281 break;
Andreas Pantelopoulosff691b72018-03-12 16:30:20 -0700282 case SUCCESS:
283 log.debug("Pseudowire {} was removed succesfully!", pseudowireId);
Ray Milkeyb994f8d2018-03-14 09:55:13 -0700284 break;
Andreas Pantelopoulosff691b72018-03-12 16:30:20 -0700285 default:
286 }
287 }
288
289 return Response.noContent().build();
290 }
Andreas Pantelopouloscd339592018-02-23 14:18:00 -0800291}