2 * Copyright 2018 Huawei Technologies Co., Ltd.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package org.onap.vtp.execution;
20 import java.io.IOException;
21 import java.nio.file.StandardCopyOption;
22 import java.util.ArrayList;
23 import java.util.Arrays;
24 import java.util.Date;
25 import java.util.HashMap;
26 import java.util.Iterator;
27 import java.util.List;
29 import java.util.UUID;
31 import javax.ws.rs.Consumes;
32 import javax.ws.rs.DefaultValue;
33 import javax.ws.rs.GET;
34 import javax.ws.rs.POST;
35 import javax.ws.rs.Path;
36 import javax.ws.rs.PathParam;
37 import javax.ws.rs.Produces;
38 import javax.ws.rs.QueryParam;
39 import javax.ws.rs.core.MediaType;
40 import javax.ws.rs.core.Response;
42 import org.apache.commons.io.FileUtils;
43 import org.apache.commons.io.IOUtils;
44 import org.apache.commons.lang3.StringUtils;
45 import org.eclipse.jetty.http.HttpStatus;
46 import org.glassfish.jersey.media.multipart.BodyPartEntity;
47 import org.glassfish.jersey.media.multipart.FormDataBodyPart;
48 import org.glassfish.jersey.media.multipart.FormDataParam;
49 import org.onap.vtp.VTPResource;
50 import org.onap.vtp.error.VTPError;
51 import org.onap.vtp.error.VTPError.VTPException;
52 import org.onap.vtp.execution.model.VTPTestExecution;
53 import org.onap.vtp.execution.model.VTPTestExecution.VTPTestExecutionList;
54 import org.open.infc.grpc.Output;
55 import org.open.infc.grpc.Result;
57 import com.google.gson.Gson;
58 import com.google.gson.JsonElement;
59 import com.google.gson.JsonObject;
60 import com.google.gson.JsonArray;
61 import com.google.gson.JsonParser;
62 import com.google.gson.reflect.TypeToken;
64 import io.swagger.annotations.Api;
65 import io.swagger.annotations.ApiOperation;
66 import io.swagger.annotations.ApiParam;
67 import io.swagger.annotations.ApiResponse;
68 import io.swagger.annotations.ApiResponses;
71 @Api(tags = {"VTP Execution"})
72 public class VTPExecutionResource extends VTPResource{
73 private static final String EXECUTION_ID = "execution-id";
74 private static final String START_TIME = "start-time";
75 private static final String END_TIME = "end-time";
76 private static final String REQUEST_ID = "request-id";
77 private static final String PRODUCT = "product";
78 private static final String SERVICE = "service";
79 private static final String COMMAND = "command";
80 private static final String PROFILE = "profile";
81 private static final String STATUS = "status";
82 private static final String OUTPUT = "output";
83 private static final String INPUT = "input";
84 private static final String ERROR = "error";
85 private static final String FILE = "file://";
86 private static final String EXCEPTION_OCCURS ="Exception occurs";
87 private static final String PRODUCT_ARG="--product";
88 private static final String OPEN_CLI="open-cli";
89 private static final String FORMAT="--format";
91 private static Gson gson = new Gson();
93 public VTPTestExecutionList executeHandler(VTPTestExecutionList executions, String requestId) throws VTPException {
94 if (requestId == null) {
95 requestId = UUID.randomUUID().toString();
98 for (VTPTestExecution execution: executions.getExecutions()) {
99 String startTime = dateFormatter.format(new Date());
100 execution.setStartTime(startTime);
103 Output output = this.makeRpc(
104 execution.getScenario(),
106 execution.getProfile(),
107 execution.getTestCaseName(),
108 execution.getParameters()
110 String endTime = dateFormatter.format(new Date());
111 execution.setEndTime(endTime);
112 execution.setExecutionId(output.getAddonsMap().get(EXECUTION_ID));
114 // set execution status based on success from test.
115 if (output.getSuccess()) {
116 execution.setStatus(VTPTestExecution.Status.COMPLETED.name());
119 execution.setStatus(VTPTestExecution.Status.FAILED.name());
122 // set the results from what is available in the output independent of status.
123 // tests can fail but still produce results.
124 JsonParser jsonParser = new JsonParser();
125 Map<String,String> m = output.getAttrsMap();
126 if ((m.containsKey(ERROR)) && (!StringUtils.equals(m.get(ERROR), "{}"))) {
128 execution.setResults(jsonParser.parse(m.get(ERROR)));
129 } catch (Exception e) { //NOSONAR
130 LOG.error(EXCEPTION_OCCURS,e);
133 else if (m.containsKey("results")) {
135 execution.setResults(jsonParser.parse(m.get("results")));
136 } catch (Exception e) { //NOSONAR
137 LOG.error(EXCEPTION_OCCURS,e);
145 private Map<String, String> storeTestCaseInputFiles(List<FormDataBodyPart> bodyParts) throws IOException {
146 Map<String, String> map = new HashMap<>();
147 if (bodyParts != null) {
148 for (FormDataBodyPart part: bodyParts) {
149 String name = part.getContentDisposition().getFileName();
150 String path = VTP_EXECUTION_TEMP_STORE + "/" + name; //NOSONAR
152 File f = new File(path);
154 FileUtils.forceDelete(f);
156 FileUtils.forceMkdir(f.getParentFile());
158 BodyPartEntity fileEntity = (BodyPartEntity) part.getEntity();
159 java.nio.file.Files.copy(
160 fileEntity.getInputStream(),
162 StandardCopyOption.REPLACE_EXISTING);
164 IOUtils.closeQuietly(fileEntity.getInputStream());
176 @ApiOperation(tags = "VTP Execution", value = "Execute the test case with given inputs in 'executions' form-data "
177 + "as key-value pair of parameter's name vs parameter's value. If parameter is binary type then" +
178 "multi-part form-data 'file' should be used to feed the binary file content and it can be more than once. "
179 + "To use the given file as input parameter, prefix the value with file://<filename>." ,
180 response = VTPTestExecution.class, responseContainer = "List")
181 @Consumes({MediaType.MULTIPART_FORM_DATA, MediaType.APPLICATION_JSON})
182 @Produces(MediaType.APPLICATION_JSON)
183 @ApiResponses(value = {
184 @ApiResponse(code = HttpStatus.INTERNAL_SERVER_ERROR_500,
185 message = "Failed to perform the operation",
186 response = VTPError.class)})
187 public Response executeTestcases(
188 @ApiParam(value = "Request Id") @QueryParam("requestId") String requestId,
189 @ApiParam(value = "Testcase File arguments", required = false) @FormDataParam("file") List<FormDataBodyPart> bodyParts,
190 @FormDataParam("executions") String executionsJson) throws VTPException {
192 VTPTestExecutionList executions = new VTPTestExecution.VTPTestExecutionList();
193 Map<String, String> map = null;
195 map = this.storeTestCaseInputFiles(bodyParts);
196 } catch (IOException e) {
197 LOG.error("IOException occurs",e);
201 for (Map.Entry<String, String> entry: map.entrySet()) {
202 if (executionsJson.contains(FILE + entry.getKey())) {
203 executionsJson = executionsJson.replaceAll(FILE + entry.getKey(), entry.getValue());
208 if (executionsJson.contains(FILE)) {
209 VTPError err = new VTPError()
210 .setMessage("Some file form-data is missing as executions has input parameter tagged with file://")
211 .setHttpStatus(HttpStatus.BAD_REQUEST_400);
212 throw new VTPException(err);
217 executions.setExecutions(
218 gson.fromJson(executionsJson, new TypeToken<List<VTPTestExecution>>(){}.getType()));
219 } catch (Exception e) { //NOSONAR
220 LOG.error(EXCEPTION_OCCURS,e);
223 executions = this.executeHandler(executions, requestId);
226 for (Map.Entry<String, String> entry: map.entrySet()) {
228 FileUtils.forceDelete(new File(entry.getValue()));
229 } catch (IOException e) {
230 LOG.error("IOException occurs",e);
235 return Response.ok(executions.getExecutions().toString(), MediaType.APPLICATION_JSON).build();
238 public VTPTestExecutionList listTestExecutionsHandler(
241 String testSuiteName,
243 String profile, //NOSONAR
245 String endTime) throws VTPException, IOException {
246 List<String> args = new ArrayList<>();
247 args.addAll(Arrays.asList(
248 PRODUCT_ARG, OPEN_CLI, "execution-list", FORMAT, "json"
251 if (startTime != null && !startTime.isEmpty()) {
252 args.add("--start-time");
256 if (endTime != null && !endTime.isEmpty()) {
257 args.add("--end-time");
261 if (requestId != null && !requestId.isEmpty()) {
262 args.add("--request-id");
266 if (testSuiteName != null && !testSuiteName.isEmpty()) {
267 args.add("--service");
268 args.add(testSuiteName);
271 if (scenario != null && !scenario.isEmpty()) {
272 args.add(PRODUCT_ARG);
276 if (testCaseName != null && !testCaseName.isEmpty()) {
277 args.add("--command");
278 args.add(testCaseName);
281 JsonElement results = this.makeRpcAndGetJson(args);
283 VTPTestExecutionList list = new VTPTestExecutionList();
285 if (results != null && results.isJsonArray() && results.getAsJsonArray().size() > 0) {
286 JsonArray resultsArray = results.getAsJsonArray();
287 for (Iterator<JsonElement> it = resultsArray.iterator(); it.hasNext();) {
288 JsonElement jsonElement = it.next();
289 JsonObject n = jsonElement.getAsJsonObject();
290 if (n.entrySet().iterator().hasNext()) {
291 VTPTestExecution exec = new VTPTestExecution();
292 if (n.get(START_TIME) != null)
293 exec.setStartTime(n.get(START_TIME).getAsString());
295 if (n.get(END_TIME) != null)
296 exec.setEndTime(n.get(END_TIME).getAsString());
298 if (n.get(EXECUTION_ID) != null)
299 exec.setExecutionId(n.get(EXECUTION_ID).getAsString());
301 if (n.get(REQUEST_ID) != null)
302 exec.setRequestId(n.get(REQUEST_ID).getAsString());
304 if (n.get(PRODUCT) != null)
305 exec.setScenario(n.get(PRODUCT).getAsString());
307 if (n.get(SERVICE) != null)
308 exec.setTestSuiteName(n.get(SERVICE).getAsString());
310 if (n.get(COMMAND) != null)
311 exec.setTestCaseName(n.get(COMMAND).getAsString());
313 if (n.get(PROFILE) != null)
314 exec.setProfile(n.get(PROFILE).getAsString());
316 if (n.get(STATUS) != null)
317 exec.setStatus(n.get(STATUS).getAsString());
319 list.getExecutions().add(exec);
330 @ApiOperation(tags = "VTP Execution", value = " List test executions", response = VTPTestExecution.class, responseContainer = "List")
331 @Produces(MediaType.APPLICATION_JSON)
332 @ApiResponses(value = {
333 @ApiResponse(code = HttpStatus.INTERNAL_SERVER_ERROR_500,
334 message = "Failed to perform the operation",
335 response = VTPError.class) })
336 public Response listTestExecutions(
337 @ApiParam("Test request Id") @QueryParam("requestId") String requestId,
338 @ApiParam("Test scenario name") @QueryParam("scenario") String scenario,
339 @ApiParam("Test suite name") @QueryParam("testsuiteName") String testsuiteName,
340 @ApiParam("Test case name") @QueryParam("testcaseName") String testcaseName,
341 @ApiParam("Test profile name") @QueryParam("profileName") String profileName,
342 @ApiParam("Test execution start time") @QueryParam("startTime") String startTime,
343 @ApiParam("Test execution end time") @QueryParam("endTime") String endTime
344 ) throws VTPException, IOException {
346 return Response.ok(this.listTestExecutionsHandler(
347 requestId, scenario, testsuiteName, testcaseName, profileName, startTime, endTime).getExecutions().toString(), MediaType.APPLICATION_JSON).build();
350 public VTPTestExecution getTestExecutionHandler(
351 String executionId) throws VTPException, IOException {
352 List<String> args = new ArrayList<>();
353 args.addAll(Arrays.asList(
354 PRODUCT_ARG, OPEN_CLI, "execution-show", "--execution-id", executionId, FORMAT, "json"
358 JsonElement result = this.makeRpcAndGetJson(args);
360 VTPTestExecution exec = new VTPTestExecution();
362 if (result != null && result.isJsonObject()) {
364 JsonObject resultObj = result.getAsJsonObject();
365 if (resultObj.entrySet().iterator().hasNext()){
366 if (resultObj.get(START_TIME) != null)
367 exec.setStartTime(resultObj.get(START_TIME).getAsString());
369 if (resultObj.get(END_TIME) != null)
370 exec.setEndTime(resultObj.get(END_TIME).getAsString());
372 if (resultObj.get(EXECUTION_ID) != null)
373 exec.setExecutionId(resultObj.get(EXECUTION_ID).getAsString());
374 if (resultObj.get(REQUEST_ID) != null)
375 exec.setExecutionId(resultObj.get(REQUEST_ID).getAsString());
377 if (resultObj.get(PRODUCT) != null)
378 exec.setScenario(resultObj.get(PRODUCT).getAsString());
379 if (resultObj.get(SERVICE) != null)
380 exec.setTestSuiteName(resultObj.get(SERVICE).getAsString());
381 if (resultObj.get(COMMAND) != null)
382 exec.setTestCaseName(resultObj.get(COMMAND).getAsString());
383 if (resultObj.get(PROFILE) != null)
384 exec.setExecutionId(resultObj.get(PROFILE).getAsString());
385 if (resultObj.get(STATUS) != null)
386 exec.setStatus(resultObj.get(STATUS).getAsString());
387 if (resultObj.get(INPUT) != null ) {
388 exec.setParameters(resultObj.get(INPUT));
390 if (resultObj.get(OUTPUT) != null) {
391 JsonParser jsonParser = new JsonParser();
392 JsonElement resultJson = null;
394 resultJson = jsonParser.parse(resultObj.get(OUTPUT).getAsString());
396 //workarround, sometimes its null.
397 if (resultJson == null || resultJson.isJsonNull()) {
398 resultJson = jsonParser.parse(resultObj.get(OUTPUT).toString());
400 } catch (Exception e) {
401 LOG.error(EXCEPTION_OCCURS, e);
402 JsonObject node = new JsonObject();
403 node.addProperty(ERROR, resultObj.get(OUTPUT).toString());
407 exec.setResults(resultJson);
415 @Path("/executions/{executionId}")
417 @ApiOperation(tags = "VTP Execution", value = " Retrieve test execution complete details", response = VTPTestExecution.class)
418 @Produces(MediaType.APPLICATION_JSON)
419 @ApiResponses(value = {
420 @ApiResponse(code = HttpStatus.INTERNAL_SERVER_ERROR_500,
421 message = "Failed to perform the operation",
422 response = VTPError.class) })
423 public Response getTestExecution(
424 @ApiParam("Test execution Id") @PathParam("executionId") String executionId
425 ) throws VTPException, IOException {
427 return Response.ok(this.getTestExecutionHandler(executionId).toString(), MediaType.APPLICATION_JSON).build();
430 public String getTestExecutionLogsHandler(
431 String executionId, String action) throws VTPException {
432 List<String> args = new ArrayList<>();
433 args.addAll(Arrays.asList(
434 PRODUCT_ARG, OPEN_CLI, "execution-show-" + action, "--execution-id", executionId, FORMAT, "text"
438 Result result = this.makeRpc(args);
440 return result.getOutput();
443 @Path("/executions/{executionId}/logs")
445 @ApiOperation(tags = "VTP Execution", value = "Retrieve test execution logs details", response = String.class)
446 @Produces(MediaType.TEXT_PLAIN)
447 @ApiResponses(value = {
448 @ApiResponse(code = HttpStatus.INTERNAL_SERVER_ERROR_500,
449 message = "Failed to perform the operation",
450 response = VTPError.class) })
451 public Response getTestExecutionLogs(
452 @ApiParam("Test execution Id") @PathParam("executionId") String executionId,
453 @ApiParam("Test console reports, Options: out, err, debug") @DefaultValue("out") @QueryParam("option") String option
454 ) throws VTPException {
455 if (!("out".equalsIgnoreCase(option) || "err".equalsIgnoreCase(option) || "debug".equalsIgnoreCase(option))) {
459 return Response.ok(this.getTestExecutionLogsHandler(executionId, option), MediaType.TEXT_PLAIN).build();