Add list of errors in responce for GET request.
[vnfsdk/refrepo.git] / vnfmarket-be / vnf-sdk-marketplace / src / main / java / org / onap / vtp / execution / VTPExecutionResource.java
1 /**
2  * Copyright 2018 Huawei Technologies Co., Ltd.
3  * Copyright 2020 Nokia.
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17
18 package org.onap.vtp.execution;
19
20 import java.io.File;
21 import java.io.IOException;
22 import java.nio.file.StandardCopyOption;
23 import java.util.ArrayList;
24 import java.util.Arrays;
25 import java.util.Date;
26 import java.util.HashMap;
27 import java.util.Iterator;
28 import java.util.List;
29 import java.util.Map;
30 import java.util.UUID;
31
32 import javax.ws.rs.Consumes;
33 import javax.ws.rs.DefaultValue;
34 import javax.ws.rs.GET;
35 import javax.ws.rs.POST;
36 import javax.ws.rs.Path;
37 import javax.ws.rs.PathParam;
38 import javax.ws.rs.Produces;
39 import javax.ws.rs.QueryParam;
40 import javax.ws.rs.core.MediaType;
41 import javax.ws.rs.core.Response;
42
43 import org.apache.commons.io.FileUtils;
44 import org.apache.commons.io.IOUtils;
45 import org.apache.commons.lang3.StringUtils;
46 import org.eclipse.jetty.http.HttpStatus;
47 import org.glassfish.jersey.media.multipart.BodyPartEntity;
48 import org.glassfish.jersey.media.multipart.FormDataBodyPart;
49 import org.glassfish.jersey.media.multipart.FormDataParam;
50 import org.onap.vtp.VTPResource;
51 import org.onap.vtp.error.VTPError;
52 import org.onap.vtp.error.VTPError.VTPException;
53 import org.onap.vtp.execution.model.VTPTestExecution;
54 import org.onap.vtp.execution.model.VTPTestExecution.VTPTestExecutionList;
55 import org.open.infc.grpc.Output;
56 import org.open.infc.grpc.Result;
57
58 import com.google.gson.Gson;
59 import com.google.gson.JsonElement;
60 import com.google.gson.JsonObject;
61 import com.google.gson.JsonArray;
62 import com.google.gson.JsonParser;
63 import com.google.gson.reflect.TypeToken;
64
65 import io.swagger.annotations.Api;
66 import io.swagger.annotations.ApiOperation;
67 import io.swagger.annotations.ApiParam;
68 import io.swagger.annotations.ApiResponse;
69 import io.swagger.annotations.ApiResponses;
70
71 @Path("/vtp")
72 @Api(tags = {"VTP Execution"})
73 public class VTPExecutionResource  extends VTPResource{
74     private static final String EXECUTION_ID = "execution-id";
75     private static final String START_TIME = "start-time";
76     private static final String END_TIME = "end-time";
77     private static final String REQUEST_ID = "request-id";
78     private static final String PRODUCT = "product";
79     private static final String SERVICE = "service";
80     private static final String COMMAND = "command";
81     private static final String PROFILE = "profile";
82     private static final String STATUS = "status";
83     private static final String OUTPUT = "output";
84     private static final String INPUT = "input";
85     private static final String ERROR = "error";
86     private static final String FILE = "file://";
87     private static final String EXCEPTION_OCCURS ="Exception occurs";
88     private static final String PRODUCT_ARG="--product";
89     private static final String OPEN_CLI="open-cli";
90     private static final String FORMAT="--format";
91     private static final Gson gson = new Gson();
92
93     protected static String pathToExecutions = "/opt/vtp/data/executions/";
94
95     public VTPTestExecutionList executeHandler(VTPTestExecutionList executions, String requestId) throws VTPException {
96         if (requestId == null) {
97             requestId = UUID.randomUUID().toString();
98         }
99
100         for (VTPTestExecution execution: executions.getExecutions())  {
101             String startTime = dateFormatter.format(new Date());
102             execution.setStartTime(startTime);
103
104             //Run execution
105             Output output = this.makeRpc(
106                     execution.getScenario(),
107                     requestId,
108                     execution.getProfile(),
109                     execution.getTestCaseName(),
110                     execution.getParameters()
111                     );
112             String endTime = dateFormatter.format(new Date());
113             execution.setEndTime(endTime);
114             execution.setExecutionId(output.getAddonsMap().get(EXECUTION_ID));
115
116             // set execution status based on success from test.
117             if (output.getSuccess()) {
118               execution.setStatus(VTPTestExecution.Status.COMPLETED.name());
119             }
120             else {
121               execution.setStatus(VTPTestExecution.Status.FAILED.name());
122             }
123
124             // set the results from what is available in the output independent of status.
125             // tests can fail but still produce results.
126             JsonParser jsonParser = new JsonParser();
127             Map<String,String> m = output.getAttrsMap();
128             if ((m.containsKey(ERROR)) && (!StringUtils.equals(m.get(ERROR), "{}"))) {
129                 try {
130                     execution.setResults(jsonParser.parse(m.get(ERROR)));
131                 } catch (Exception e) { //NOSONAR
132                     LOG.error(EXCEPTION_OCCURS,e);
133                 }
134             }
135             else if (m.containsKey("results")) {
136                 try {
137                     execution.setResults(jsonParser.parse(m.get("results")));
138                 } catch (Exception e) { //NOSONAR
139                     LOG.error(EXCEPTION_OCCURS,e);
140                 }
141             }
142         }
143
144         return executions;
145     }
146
147     private Map<String, String> storeTestCaseInputFiles(List<FormDataBodyPart> bodyParts) throws IOException {
148         Map<String, String> map = new HashMap<>();
149         if (bodyParts != null) {
150             for (FormDataBodyPart part: bodyParts) {
151                 String name = part.getContentDisposition().getFileName();
152                 String path = VTP_EXECUTION_TEMP_STORE + "/" + name; //NOSONAR
153
154                 File f = new File(path);
155                 if (f.exists()) {
156                     FileUtils.forceDelete(f);
157                 }
158                 FileUtils.forceMkdir(f.getParentFile());
159
160                 BodyPartEntity fileEntity = (BodyPartEntity) part.getEntity();
161                 java.nio.file.Files.copy(
162                         fileEntity.getInputStream(),
163                         f.toPath(),
164                         StandardCopyOption.REPLACE_EXISTING);
165
166                 IOUtils.closeQuietly(fileEntity.getInputStream());
167
168                 map.put(name, path);
169             }
170
171         }
172
173         return map;
174     }
175
176     @Path("/executions")
177     @POST
178     @ApiOperation(tags = "VTP Execution", value = "Execute the test case with given inputs in 'executions' form-data "
179             + "as key-value pair of parameter's name vs parameter's value. If parameter is binary type then" +
180             "multi-part form-data 'file' should be used to feed the binary file content and it can be more than once. "
181             + "To use the given file as input parameter, prefix the value with file://<filename>." ,
182             response = VTPTestExecution.class, responseContainer = "List")
183     @Consumes({MediaType.MULTIPART_FORM_DATA, MediaType.APPLICATION_JSON})
184     @Produces(MediaType.APPLICATION_JSON)
185     @ApiResponses(value = {
186             @ApiResponse(code = HttpStatus.INTERNAL_SERVER_ERROR_500,
187                     message = "Failed to perform the operation",
188                     response = VTPError.class)})
189     public Response executeTestcases(
190              @ApiParam(value = "Request Id") @QueryParam("requestId") String requestId,
191              @ApiParam(value = "Testcase File arguments", required = false) @FormDataParam("file") List<FormDataBodyPart> bodyParts,
192              @FormDataParam("executions") String executionsJson) throws VTPException {
193
194         VTPTestExecutionList executions = new VTPTestExecution.VTPTestExecutionList();
195         Map<String, String> map = null;
196         try {
197             map = this.storeTestCaseInputFiles(bodyParts);
198         } catch (IOException e) {
199             LOG.error("IOException occurs",e);
200         }
201
202         if (map != null) {
203             for (Map.Entry<String, String> entry: map.entrySet()) {
204                 if (executionsJson.contains(FILE + entry.getKey())) {
205                     executionsJson = executionsJson.replaceAll(FILE + entry.getKey(), entry.getValue());
206                 }
207             }
208         }
209
210         if (executionsJson.contains(FILE)) {
211             VTPError err = new VTPError()
212                     .setMessage("Some file form-data is missing as executions has input parameter tagged with file://")
213                     .setHttpStatus(HttpStatus.BAD_REQUEST_400);
214             throw new VTPException(err);
215
216         }
217
218         try {
219             executions.setExecutions(
220                         gson.fromJson(executionsJson, new TypeToken<List<VTPTestExecution>>(){}.getType()));
221         } catch (Exception e) { //NOSONAR
222             LOG.error(EXCEPTION_OCCURS,e);
223         }
224
225         executions = this.executeHandler(executions, requestId);
226
227         if (map != null) {
228             for (Map.Entry<String, String> entry: map.entrySet()) {
229                 try {
230                     FileUtils.forceDelete(new File(entry.getValue()));
231                 } catch (IOException e) {
232                     LOG.error("IOException occurs",e);
233                 }
234             }
235         }
236
237         return Response.ok(executions.getExecutions().toString(), MediaType.APPLICATION_JSON).build();
238     }
239
240     public VTPTestExecutionList listTestExecutionsHandler(
241             String requestId,
242             String scenario,
243             String testSuiteName,
244             String testCaseName,
245             String profile, //NOSONAR
246             String startTime,
247             String endTime) throws VTPException, IOException {
248         List<String> args = new ArrayList<>();
249         args.addAll(Arrays.asList(
250                 PRODUCT_ARG, OPEN_CLI, "execution-list", FORMAT, "json"
251                 ));
252
253         if (startTime != null && !startTime.isEmpty()) {
254             args.add("--start-time");
255             args.add(startTime);
256         }
257
258         if (endTime != null && !endTime.isEmpty()) {
259             args.add("--end-time");
260             args.add(endTime);
261         }
262
263         if (requestId != null && !requestId.isEmpty()) {
264             args.add("--request-id");
265             args.add(requestId);
266         }
267
268         if (testSuiteName != null && !testSuiteName.isEmpty()) {
269             args.add("--service");
270             args.add(testSuiteName);
271         }
272
273         if (scenario != null && !scenario.isEmpty()) {
274             args.add(PRODUCT_ARG);
275             args.add(scenario);
276         }
277
278         if (testCaseName != null && !testCaseName.isEmpty()) {
279             args.add("--command");
280             args.add(testCaseName);
281         }
282
283         JsonElement results = this.makeRpcAndGetJson(args);
284
285         VTPTestExecutionList list = new VTPTestExecutionList();
286
287         if (results != null && results.isJsonArray() && results.getAsJsonArray().size() > 0) {
288             JsonArray resultsArray = results.getAsJsonArray();
289                 for (Iterator<JsonElement> it = resultsArray.iterator(); it.hasNext();) {
290                     JsonElement jsonElement = it.next();
291                     JsonObject n = jsonElement.getAsJsonObject();
292                     if (n.entrySet().iterator().hasNext()) {
293                         VTPTestExecution exec = new VTPTestExecution();
294                         if (n.get(START_TIME) != null)
295                             exec.setStartTime(n.get(START_TIME).getAsString());
296
297                         if (n.get(END_TIME) != null)
298                             exec.setEndTime(n.get(END_TIME).getAsString());
299
300                         if (n.get(EXECUTION_ID) != null) {
301                             exec.setExecutionId(n.get(EXECUTION_ID).getAsString());
302                             exec.setResults(
303                                 new VTPExecutionResultsSupplier(pathToExecutions)
304                                     .getExecutionOutputsFromFile(exec.getExecutionId())
305                             );
306                         }
307
308                         if (n.get(REQUEST_ID) != null)
309                             exec.setRequestId(n.get(REQUEST_ID).getAsString());
310
311                         if (n.get(PRODUCT) != null)
312                             exec.setScenario(n.get(PRODUCT).getAsString());
313
314                         if (n.get(SERVICE) != null)
315                             exec.setTestSuiteName(n.get(SERVICE).getAsString());
316
317                         if (n.get(COMMAND) != null)
318                             exec.setTestCaseName(n.get(COMMAND).getAsString());
319
320                         if (n.get(PROFILE) != null)
321                             exec.setProfile(n.get(PROFILE).getAsString());
322
323                         if (n.get(STATUS) != null)
324                             exec.setStatus(n.get(STATUS).getAsString());
325
326
327                         list.getExecutions().add(exec);
328                     }
329
330                 }
331         }
332
333         return list;
334     }
335
336
337     @Path("/executions")
338     @GET
339     @ApiOperation(tags = "VTP Execution", value = " List test executions", response = VTPTestExecution.class, responseContainer = "List")
340     @Produces(MediaType.APPLICATION_JSON)
341     @ApiResponses(value = {
342             @ApiResponse(code = HttpStatus.INTERNAL_SERVER_ERROR_500,
343                     message = "Failed to perform the operation",
344                     response = VTPError.class) })
345     public Response listTestExecutions(
346              @ApiParam("Test request Id") @QueryParam("requestId") String requestId,
347              @ApiParam("Test scenario name") @QueryParam("scenario") String scenario,
348              @ApiParam("Test suite name") @QueryParam("testsuiteName") String testsuiteName,
349              @ApiParam("Test case name") @QueryParam("testcaseName") String testcaseName,
350              @ApiParam("Test profile name") @QueryParam("profileName") String profileName,
351              @ApiParam("Test execution start time") @QueryParam("startTime") String startTime,
352              @ApiParam("Test execution end time") @QueryParam("endTime") String endTime
353              ) throws VTPException, IOException  {
354
355         return Response.ok(this.listTestExecutionsHandler(
356                 requestId, scenario, testsuiteName, testcaseName, profileName, startTime, endTime).getExecutions().toString(), MediaType.APPLICATION_JSON).build();
357     }
358
359     public VTPTestExecution getTestExecutionHandler(
360             String executionId) throws VTPException, IOException {
361         List<String> args = new ArrayList<>();
362         args.addAll(Arrays.asList(
363                 PRODUCT_ARG, OPEN_CLI, "execution-show", "--execution-id", executionId, FORMAT, "json"
364                 ));
365
366
367         JsonElement result = this.makeRpcAndGetJson(args);
368
369         VTPTestExecution exec = new VTPTestExecution();
370
371         if (result != null && result.isJsonObject()) {
372
373             JsonObject resultObj = result.getAsJsonObject();
374             if (resultObj.entrySet().iterator().hasNext()){
375                 if (resultObj.get(START_TIME) != null)
376                     exec.setStartTime(resultObj.get(START_TIME).getAsString());
377
378                 if (resultObj.get(END_TIME) != null)
379                     exec.setEndTime(resultObj.get(END_TIME).getAsString());
380
381                 if (resultObj.get(EXECUTION_ID) != null)
382                     exec.setExecutionId(resultObj.get(EXECUTION_ID).getAsString());
383                 if (resultObj.get(REQUEST_ID) != null)
384                     exec.setExecutionId(resultObj.get(REQUEST_ID).getAsString());
385
386                 if (resultObj.get(PRODUCT) != null)
387                     exec.setScenario(resultObj.get(PRODUCT).getAsString());
388                 if (resultObj.get(SERVICE) != null)
389                     exec.setTestSuiteName(resultObj.get(SERVICE).getAsString());
390                 if (resultObj.get(COMMAND) != null)
391                     exec.setTestCaseName(resultObj.get(COMMAND).getAsString());
392                 if (resultObj.get(PROFILE) != null)
393                     exec.setExecutionId(resultObj.get(PROFILE).getAsString());
394                 if (resultObj.get(STATUS) != null)
395                     exec.setStatus(resultObj.get(STATUS).getAsString());
396                 if (resultObj.get(INPUT) != null ) {
397                     exec.setParameters(resultObj.get(INPUT));
398                 }
399                 if (resultObj.get(OUTPUT) != null) {
400                     JsonParser jsonParser = new JsonParser();
401                     JsonElement resultJson = null;
402                     try {
403                         resultJson = jsonParser.parse(resultObj.get(OUTPUT).getAsString());
404
405                     //workarround, sometimes its null.
406                         if (resultJson == null || resultJson.isJsonNull()) {
407                             resultJson = jsonParser.parse(resultObj.get(OUTPUT).toString());
408                         }
409                     } catch (Exception e) {
410                         LOG.error(EXCEPTION_OCCURS, e);
411                         JsonObject node = new JsonObject();
412                         node.addProperty(ERROR, resultObj.get(OUTPUT).toString());
413                         resultJson = node;
414                     }
415
416                     exec.setResults(resultJson);
417                 }
418             }
419         }
420
421         return exec;
422     }
423
424     @Path("/executions/{executionId}")
425     @GET
426     @ApiOperation(tags = "VTP Execution", value = " Retrieve test execution complete details", response = VTPTestExecution.class)
427     @Produces(MediaType.APPLICATION_JSON)
428     @ApiResponses(value = {
429             @ApiResponse(code = HttpStatus.INTERNAL_SERVER_ERROR_500,
430                     message = "Failed to perform the operation",
431                     response = VTPError.class) })
432     public Response getTestExecution(
433              @ApiParam("Test execution Id") @PathParam("executionId") String executionId
434              ) throws VTPException, IOException  {
435
436         return Response.ok(this.getTestExecutionHandler(executionId).toString(), MediaType.APPLICATION_JSON).build();
437     }
438
439     public String getTestExecutionLogsHandler(
440             String executionId, String action) throws VTPException {
441         List<String> args = new ArrayList<>();
442         args.addAll(Arrays.asList(
443                 PRODUCT_ARG, OPEN_CLI, "execution-show-" + action, "--execution-id", executionId, FORMAT, "text"
444                 ));
445
446
447         Result result = this.makeRpc(args);
448
449         return result.getOutput();
450     }
451
452     @Path("/executions/{executionId}/logs")
453     @GET
454     @ApiOperation(tags = "VTP Execution", value = "Retrieve test execution logs details", response = String.class)
455     @Produces(MediaType.TEXT_PLAIN)
456     @ApiResponses(value = {
457             @ApiResponse(code = HttpStatus.INTERNAL_SERVER_ERROR_500,
458                     message = "Failed to perform the operation",
459                     response = VTPError.class) })
460     public Response getTestExecutionLogs(
461              @ApiParam("Test execution Id") @PathParam("executionId") String executionId,
462              @ApiParam("Test console reports, Options: out, err, debug") @DefaultValue("out")  @QueryParam("option") String option
463              ) throws VTPException {
464         if (!("out".equalsIgnoreCase(option) || "err".equalsIgnoreCase(option) || "debug".equalsIgnoreCase(option))) {
465                 option = "out";
466         }
467
468         return Response.ok(this.getTestExecutionLogsHandler(executionId, option), MediaType.TEXT_PLAIN).build();
469     }
470 }