2 * Copyright 2018 Huawei Technologies Co., Ltd.
3 * Copyright 2020 Nokia.
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
9 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 package org.onap.vtp.execution;
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;
30 import java.util.UUID;
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;
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;
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;
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;
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();
93 protected static String pathToExecutions = "/opt/vtp/data/executions/";
95 public VTPTestExecutionList executeHandler(VTPTestExecutionList executions, String requestId) throws VTPException {
96 if (requestId == null) {
97 requestId = UUID.randomUUID().toString();
100 for (VTPTestExecution execution: executions.getExecutions()) {
101 String startTime = dateFormatter.format(new Date());
102 execution.setStartTime(startTime);
105 Output output = this.makeRpc(
106 execution.getScenario(),
108 execution.getProfile(),
109 execution.getTestCaseName(),
110 execution.getParameters()
112 String endTime = dateFormatter.format(new Date());
113 execution.setEndTime(endTime);
114 execution.setExecutionId(output.getAddonsMap().get(EXECUTION_ID));
116 // set execution status based on success from test.
117 if (output.getSuccess()) {
118 execution.setStatus(VTPTestExecution.Status.COMPLETED.name());
121 execution.setStatus(VTPTestExecution.Status.FAILED.name());
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), "{}"))) {
130 execution.setResults(jsonParser.parse(m.get(ERROR)));
131 } catch (Exception e) { //NOSONAR
132 LOG.error(EXCEPTION_OCCURS,e);
135 else if (m.containsKey("results")) {
137 execution.setResults(jsonParser.parse(m.get("results")));
138 } catch (Exception e) { //NOSONAR
139 LOG.error(EXCEPTION_OCCURS,e);
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
154 File f = new File(path);
156 FileUtils.forceDelete(f);
158 FileUtils.forceMkdir(f.getParentFile());
160 BodyPartEntity fileEntity = (BodyPartEntity) part.getEntity();
161 java.nio.file.Files.copy(
162 fileEntity.getInputStream(),
164 StandardCopyOption.REPLACE_EXISTING);
166 IOUtils.closeQuietly(fileEntity.getInputStream());
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 {
194 VTPTestExecutionList executions = new VTPTestExecution.VTPTestExecutionList();
195 Map<String, String> map = null;
197 map = this.storeTestCaseInputFiles(bodyParts);
198 } catch (IOException e) {
199 LOG.error("IOException occurs",e);
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());
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);
219 executions.setExecutions(
220 gson.fromJson(executionsJson, new TypeToken<List<VTPTestExecution>>(){}.getType()));
221 } catch (Exception e) { //NOSONAR
222 LOG.error(EXCEPTION_OCCURS,e);
225 executions = this.executeHandler(executions, requestId);
228 for (Map.Entry<String, String> entry: map.entrySet()) {
230 FileUtils.forceDelete(new File(entry.getValue()));
231 } catch (IOException e) {
232 LOG.error("IOException occurs",e);
237 return Response.ok(executions.getExecutions().toString(), MediaType.APPLICATION_JSON).build();
240 public VTPTestExecutionList listTestExecutionsHandler(
243 String testSuiteName,
245 String profile, //NOSONAR
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"
253 if (startTime != null && !startTime.isEmpty()) {
254 args.add("--start-time");
258 if (endTime != null && !endTime.isEmpty()) {
259 args.add("--end-time");
263 if (requestId != null && !requestId.isEmpty()) {
264 args.add("--request-id");
268 if (testSuiteName != null && !testSuiteName.isEmpty()) {
269 args.add("--service");
270 args.add(testSuiteName);
273 if (scenario != null && !scenario.isEmpty()) {
274 args.add(PRODUCT_ARG);
278 if (testCaseName != null && !testCaseName.isEmpty()) {
279 args.add("--command");
280 args.add(testCaseName);
283 JsonElement results = this.makeRpcAndGetJson(args);
285 VTPTestExecutionList list = new VTPTestExecutionList();
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());
297 if (n.get(END_TIME) != null)
298 exec.setEndTime(n.get(END_TIME).getAsString());
300 if (n.get(EXECUTION_ID) != null) {
301 exec.setExecutionId(n.get(EXECUTION_ID).getAsString());
303 new VTPExecutionResultsSupplier(pathToExecutions)
304 .getExecutionOutputsFromFile(exec.getExecutionId())
308 if (n.get(REQUEST_ID) != null)
309 exec.setRequestId(n.get(REQUEST_ID).getAsString());
311 if (n.get(PRODUCT) != null)
312 exec.setScenario(n.get(PRODUCT).getAsString());
314 if (n.get(SERVICE) != null)
315 exec.setTestSuiteName(n.get(SERVICE).getAsString());
317 if (n.get(COMMAND) != null)
318 exec.setTestCaseName(n.get(COMMAND).getAsString());
320 if (n.get(PROFILE) != null)
321 exec.setProfile(n.get(PROFILE).getAsString());
323 if (n.get(STATUS) != null)
324 exec.setStatus(n.get(STATUS).getAsString());
327 list.getExecutions().add(exec);
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 {
355 return Response.ok(this.listTestExecutionsHandler(
356 requestId, scenario, testsuiteName, testcaseName, profileName, startTime, endTime).getExecutions().toString(), MediaType.APPLICATION_JSON).build();
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"
367 JsonElement result = this.makeRpcAndGetJson(args);
369 VTPTestExecution exec = new VTPTestExecution();
371 if (result != null && result.isJsonObject()) {
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());
378 if (resultObj.get(END_TIME) != null)
379 exec.setEndTime(resultObj.get(END_TIME).getAsString());
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());
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));
399 if (resultObj.get(OUTPUT) != null) {
400 JsonParser jsonParser = new JsonParser();
401 JsonElement resultJson = null;
403 resultJson = jsonParser.parse(resultObj.get(OUTPUT).getAsString());
405 //workarround, sometimes its null.
406 if (resultJson == null || resultJson.isJsonNull()) {
407 resultJson = jsonParser.parse(resultObj.get(OUTPUT).toString());
409 } catch (Exception e) {
410 LOG.error(EXCEPTION_OCCURS, e);
411 JsonObject node = new JsonObject();
412 node.addProperty(ERROR, resultObj.get(OUTPUT).toString());
416 exec.setResults(resultJson);
424 @Path("/executions/{executionId}")
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 {
436 return Response.ok(this.getTestExecutionHandler(executionId).toString(), MediaType.APPLICATION_JSON).build();
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"
447 Result result = this.makeRpc(args);
449 return result.getOutput();
452 @Path("/executions/{executionId}/logs")
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))) {
468 return Response.ok(this.getTestExecutionLogsHandler(executionId, option), MediaType.TEXT_PLAIN).build();