Changed the code to not log user-controlled data.
[vnfsdk/refrepo.git] / vnfmarket-be / vnf-sdk-marketplace / src / main / java / org / onap / vtp / scenario / VTPScenarioResource.java
1 /**
2  * Copyright 2018 Huawei Technologies Co., Ltd.
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
17 package org.onap.vtp.scenario;
18
19 import java.io.File;
20 import java.io.FileNotFoundException;
21 import java.io.FileReader;
22 import java.io.FileWriter;
23 import java.io.IOException;
24 import java.text.MessageFormat;
25 import java.util.ArrayList;
26 import java.util.Arrays;
27 import java.util.Iterator;
28 import java.util.List;
29 import java.util.Map;
30 import java.util.Objects;
31 import java.util.regex.Matcher;
32
33 import javax.ws.rs.Consumes;
34 import javax.ws.rs.DELETE;
35 import javax.ws.rs.GET;
36 import javax.ws.rs.POST;
37 import javax.ws.rs.Path;
38 import javax.ws.rs.PathParam;
39 import javax.ws.rs.Produces;
40 import javax.ws.rs.QueryParam;
41 import javax.ws.rs.core.MediaType;
42 import javax.ws.rs.core.Response;
43
44 import com.google.common.collect.Maps;
45 import org.apache.commons.io.FileUtils;
46 import org.apache.commons.lang3.StringUtils;
47 import org.apache.cxf.common.util.CollectionUtils;
48 import org.eclipse.jetty.http.HttpStatus;
49 import org.glassfish.jersey.media.multipart.BodyPartEntity;
50 import org.glassfish.jersey.media.multipart.FormDataBodyPart;
51 import org.glassfish.jersey.media.multipart.FormDataParam;
52 import org.onap.vnfsdk.marketplace.common.CommonConstant;
53 import org.onap.vnfsdk.marketplace.common.FileUtil;
54 import org.onap.vnfsdk.marketplace.common.ToolUtil;
55 import org.onap.vtp.VTPResource;
56 import org.onap.vtp.error.VTPError;
57 import org.onap.vtp.error.VTPError.VTPException;
58 import org.onap.vtp.manager.DistManager;
59 import org.onap.vtp.scenario.model.VTPTestCase;
60 import org.onap.vtp.scenario.model.VTPTestScenario;
61 import org.onap.vtp.scenario.model.VTPTestSuite;
62 import org.onap.vtp.scenario.model.VTPTestCase.VTPTestCaseInput;
63 import org.onap.vtp.scenario.model.VTPTestCase.VTPTestCaseList;
64 import org.onap.vtp.scenario.model.VTPTestCase.VTPTestCaseOutput;
65 import org.onap.vtp.scenario.model.VTPTestScenario.VTPTestScenarioList;
66 import org.onap.vtp.scenario.model.VTPTestSuite.VTPTestSuiteList;
67
68 import com.google.gson.JsonArray;
69 import com.google.gson.JsonElement;
70 import com.google.gson.JsonObject;
71
72 import io.swagger.annotations.Api;
73 import io.swagger.annotations.ApiOperation;
74 import io.swagger.annotations.ApiParam;
75 import io.swagger.annotations.ApiResponse;
76 import io.swagger.annotations.ApiResponses;
77
78 @Path("/vtp")
79 @Api(tags = {"VTP Scenario"})
80 public class VTPScenarioResource extends VTPResource{
81     private static final String DESCRIPTION = "description";
82     private static final String PRODUCT_ARG="--product";
83     private static final String OPEN_CLI="open-cli";
84     private static final String FORMAT="--format";
85     private static final String IO_EXCEPTION_OCCURS ="IOException occurs";
86     private static final String SERVICE="service";
87     private static final String PRODUCT = "product";
88     private DistManager distManagerVtpScenarioResource = new DistManager();
89     public VTPTestScenarioList listTestScenariosHandler() throws VTPException {
90         List<String> args = new ArrayList<>();
91
92         args.addAll(Arrays.asList(
93                 PRODUCT_ARG, OPEN_CLI, "product-list", FORMAT, "json"
94         ));
95
96
97         JsonElement results = null;
98         if (isDistMode()) {
99             String endPoint="/manager/scenarios";
100             return  distManagerVtpScenarioResource.getScenarioListFromManager(endPoint);
101         }
102         else{
103             try {
104                 results = this.makeRpcAndGetJson(args);
105             } catch (IOException e) {
106                 LOG.error(IO_EXCEPTION_OCCURS, e);
107             }
108         }
109
110         VTPTestScenarioList list = new VTPTestScenarioList();
111
112         if (results != null && results.isJsonArray() && results.getAsJsonArray().size()>0) {
113             JsonArray resultsArray = results.getAsJsonArray();
114             for (Iterator<JsonElement> it = resultsArray.iterator(); it.hasNext();) {
115                 JsonElement jsonElement = it.next();
116                 JsonObject n = jsonElement.getAsJsonObject();
117                 if (n.entrySet().iterator().hasNext()) {
118                     String name = n.get(PRODUCT).getAsString();
119
120                     if (OPEN_CLI.equalsIgnoreCase(name))
121                         continue;
122
123                     list.getScenarios().add(new VTPTestScenario().setName(name).setDescription(
124                             n.get(DESCRIPTION).getAsString()));
125                 }
126             }
127         }
128
129         return list;
130     }
131     private String loggerPatternBreaking(String loggerInput) {
132         return Objects.nonNull(loggerInput) ? loggerInput.replaceAll("[\n\r\t]", "_") : StringUtils.EMPTY;
133     }
134
135     @Path("/scenarios")
136     @GET
137     @ApiOperation(tags = "VTP Scenario", value = " List available test scenarios", response = VTPTestScenario.class, responseContainer = "List")
138     @Produces(MediaType.APPLICATION_JSON)
139     @ApiResponses(value = {
140             @ApiResponse(code = HttpStatus.INTERNAL_SERVER_ERROR_500,
141                     message = "Failed to perform the operation",
142                     response = VTPError.class) })
143     public Response listTestScenarios() throws VTPException {
144         return Response.ok(this.listTestScenariosHandler().getScenarios().toString(), MediaType.APPLICATION_JSON).build();
145     }
146
147     public VTPTestSuiteList listTestSutiesHandler(String scenario) throws VTPException {
148         List<String> args = new ArrayList<>();
149
150         args.addAll(Arrays.asList(
151                 PRODUCT_ARG, OPEN_CLI, "service-list", PRODUCT_ARG, scenario, FORMAT, "json"
152         ));
153
154         JsonElement results = null;
155         if (isDistMode()) {
156             String url="/manager/scenarios/"+scenario+"/testsuites";
157             return distManagerVtpScenarioResource.getSuiteListFromManager(url);
158         }else {
159             try {
160                 results = this.makeRpcAndGetJson(args);
161             } catch (IOException e) {
162                 LOG.error(IO_EXCEPTION_OCCURS,e);
163             }
164         }
165
166         VTPTestSuiteList list = new VTPTestSuiteList();
167
168         if (results != null && results.isJsonArray() && results.getAsJsonArray().size()>0) {
169             JsonArray resultsArray = results.getAsJsonArray();
170             for (Iterator<JsonElement> it = resultsArray.iterator(); it.hasNext();) {
171                 JsonElement jsonElement = it.next();
172                 JsonObject n = jsonElement.getAsJsonObject();
173                 if (n.entrySet().iterator().hasNext()) {
174                     list.getSuites().add(new VTPTestSuite().setName(n.get(SERVICE).getAsString()).setDescription(
175                             n.get(DESCRIPTION).getAsString()));
176                 }
177             }
178         }
179
180         return list;
181     }
182
183     @Path("/scenarios/{scenario}/testsuites")
184     @GET
185     @ApiOperation(tags = "VTP Scenario",  value = " List available test suties in given scenario", response = VTPTestSuite.class, responseContainer = "List")
186     @Produces(MediaType.APPLICATION_JSON)
187     @ApiResponses(value = {
188             @ApiResponse(code = HttpStatus.INTERNAL_SERVER_ERROR_500,
189                     message = "Failed to perform the operation",
190                     response = VTPError.class) })
191     public Response listTestSuties(
192             @ApiParam("Test scenario name") @PathParam("scenario") String scenario) throws VTPException {
193
194         return Response.ok(this.listTestSutiesHandler(scenario).getSuites().toString(), MediaType.APPLICATION_JSON).build();
195     }
196
197     public VTPTestCaseList listTestcasesHandler(String testSuiteName, String scenario) throws VTPException {
198         List<String> args = new ArrayList<>();
199
200         args.addAll(Arrays.asList(
201                 PRODUCT_ARG, OPEN_CLI, "schema-list", PRODUCT_ARG, scenario, FORMAT, "json"
202         ));
203         if (testSuiteName != null) {
204             args.add("--service");
205             args.add(testSuiteName);
206         }
207
208         JsonElement results = null;
209         if (isDistMode()) {
210             String url = "/manager/scenarios/" + scenario + "/testcases";
211             return distManagerVtpScenarioResource.getTestCaseListFromManager(url);
212         } else {
213             try {
214                 results = this.makeRpcAndGetJson(args);
215             } catch (IOException e) {
216                 LOG.error(IO_EXCEPTION_OCCURS, e);
217             }
218         }
219
220         VTPTestCaseList list = new VTPTestCaseList();
221
222         if (results != null && results.isJsonArray() && results.getAsJsonArray().size()>0) {
223             JsonArray resultsArray = results.getAsJsonArray();
224             for (Iterator<JsonElement> it = resultsArray.iterator(); it.hasNext();) {
225                 JsonElement jsonElement = it.next();
226                 JsonObject n = jsonElement.getAsJsonObject();
227                 if (n.entrySet().iterator().hasNext())
228                     list.getTestCases().add(
229                             new VTPTestCase().setTestCaseName(
230                                     n.get("command").getAsString()).setTestSuiteName(
231                                     n.get(SERVICE).getAsString()));
232             }
233         }
234
235         return list;
236     }
237
238     @Path("/scenarios/{scenario}/testcases")
239     @GET
240     @ApiOperation(tags = "VTP Scenario", value = " List available test cases", response = VTPTestCase.class, responseContainer = "List")
241     @Produces(MediaType.APPLICATION_JSON)
242     @ApiResponses(value = {
243             @ApiResponse(code = HttpStatus.INTERNAL_SERVER_ERROR_500,
244                     message = "Failed to perform the operation",
245                     response = VTPError.class) })
246     public Response listTestcases(
247             @ApiParam("Test scenario name") @PathParam("scenario") String scenario,
248             @ApiParam("Test suite name") @QueryParam("testSuiteName") String testSuiteName
249     ) throws VTPException {
250
251         return Response.ok(this.listTestcasesHandler(testSuiteName, scenario).getTestCases().toString(), MediaType.APPLICATION_JSON).build();
252     }
253
254     public VTPTestCase getTestcaseHandler(String scenario, String testSuiteName, String testCaseName) throws VTPException {
255         List<String> args = new ArrayList<>();
256         args.addAll(Arrays.asList(
257                 PRODUCT_ARG, OPEN_CLI, "schema-show", PRODUCT_ARG, scenario, "--service", testSuiteName, "--command", testCaseName , FORMAT, "json"
258         ));
259         JsonElement results = null;
260         try {
261             results = this.makeRpcAndGetJson(args);
262         } catch (IOException e) {
263             LOG.error(IO_EXCEPTION_OCCURS,e);
264         }
265
266         JsonObject schema = results.getAsJsonObject().getAsJsonObject("schema");
267
268         VTPTestCase tc = new VTPTestCase();
269         tc.setTestCaseName(schema.get("name").getAsString());
270         tc.setDescription(schema.get(DESCRIPTION).getAsString());
271         tc.setTestSuiteName(schema.get(SERVICE).getAsString());
272         tc.setAuthor(schema.get("author").getAsString());
273         JsonElement inputsJson = schema.get("inputs");
274         if (inputsJson != null && inputsJson.isJsonArray()) {
275             formatResponseData(tc, inputsJson);
276         }
277
278         JsonElement outputsJson = schema.get("outputs");
279         if (outputsJson != null && outputsJson.isJsonArray() && outputsJson.getAsJsonArray().size()>0) {
280             for (final JsonElement jsonElement: outputsJson.getAsJsonArray()) {
281                 JsonObject outputJson = jsonElement.getAsJsonObject();
282                 VTPTestCaseOutput output = new VTPTestCaseOutput();
283                 output.setName(outputJson.get("name").getAsString());
284                 output.setDescription(outputJson.get(DESCRIPTION).getAsString());
285                 output.setType(outputJson.get("type").getAsString());
286
287                 tc.getOutputs().add(output);
288             }
289         }
290
291         return tc;
292     }
293
294         private void formatResponseData(VTPTestCase tc, JsonElement inputsJson) {
295                 for (final JsonElement jsonElement: inputsJson.getAsJsonArray()) {
296                     JsonObject inputJson  = jsonElement.getAsJsonObject();
297                     VTPTestCaseInput input = new VTPTestCaseInput();
298
299                     input.setName(inputJson.get("name").getAsString());
300                     input.setDescription(inputJson.get(DESCRIPTION).getAsString());
301                     input.setType(inputJson.get("type").getAsString());
302
303                     if (inputJson.get("is_optional") != null)
304                         input.setIsOptional(inputJson.get("is_optional").getAsBoolean());
305
306                     if (inputJson.get("default_value") != null)
307                         input.setDefaultValue(inputJson.get("default_value").getAsString());
308
309                     if (inputJson.get("metadata") != null)
310                         input.setMetadata(inputJson.get("metadata"));
311
312                     tc.getInputs().add(input);
313                 }
314         }
315
316     @Path("/scenarios/{scenario}/testsuites/{testSuiteName}/testcases/{testCaseName}")
317     @GET
318     @ApiOperation(tags = "VTP Scenario",  value = "Retrieve test cases details like inputs outputs and test suite name", response = VTPTestCase.class)
319     @Produces(MediaType.APPLICATION_JSON)
320     @ApiResponses(value = {
321             @ApiResponse(code = HttpStatus.INTERNAL_SERVER_ERROR_500,
322                     message = "Failed to perform the operation", response = VTPError.class),
323             @ApiResponse(code = HttpStatus.NOT_FOUND_404,
324                     message = "Test case does not exist", response = VTPError.class)})
325     public Response getTestcase(
326             @ApiParam("Test scenario name") @PathParam("scenario") String scenario,
327             @ApiParam(value = "Test case name") @PathParam("testSuiteName") String testSuiteName,
328             @ApiParam(value = "Test case name") @PathParam("testCaseName") String testCaseName)
329             throws VTPException {
330
331         return Response.ok(this.getTestcaseHandler(scenario, testSuiteName, testCaseName).toString(), MediaType.APPLICATION_JSON).build();
332     }
333
334     @Path("/scenarios")
335     @POST
336     @ApiOperation(tags = "VTP Scenario", value = "Create Scenario")
337     @Consumes(MediaType.MULTIPART_FORM_DATA)
338     @Produces(MediaType.APPLICATION_JSON)
339     @ApiResponses(value = {
340             @ApiResponse(code = HttpStatus.INTERNAL_SERVER_ERROR_500, message = "Failed to perform the operation", response = VTPError.class)})
341     public Response storageScenarios(@ApiParam(value = "file form data body parts", required = true)
342                                      @FormDataParam("files") List<FormDataBodyPart> bodyParts) throws VTPException {
343         bodyParts.forEach(bodyPart -> {
344             BodyPartEntity entity = (BodyPartEntity) bodyPart.getEntity();
345             String fileName = bodyPart.getContentDisposition().getFileName();
346             if (!ToolUtil.isYamlFile(new File(fileName))) {
347                 LOG.error("The fileName {} is not yaml !!!", fileName);
348                 return;
349             }
350             String scenario = fileName.substring(0, fileName.indexOf("-registry"));
351             File scenarioDir = new File(VTP_YAML_STORE, scenario);
352             File yamlFile = new File(VTP_YAML_STORE, fileName);
353
354             // 1、store the scenario yaml file and create the scenario dir
355             try {
356                 FileUtils.deleteQuietly(yamlFile);
357                 FileUtils.deleteDirectory(scenarioDir);
358                 FileUtils.forceMkdir(scenarioDir);
359                 FileUtils.copyInputStreamToFile(entity.getInputStream(), yamlFile);
360             } catch (IOException e) {
361                 LOG.error("Save yaml {} failed", fileName, e);
362             }
363
364             // 2、create the testsuits dir and copy the testcase to current scenarios by commands
365             try {
366                 Map<String, Object> yamlInfos = Maps.newHashMap();
367                 try (FileReader fileReader = new FileReader(yamlFile)) {
368                     yamlInfos = snakeYaml().load(fileReader);
369                 }
370                 for (Object service : (List) yamlInfos.get("services")) {
371                     processCurrentScenarioCommands(scenario, scenarioDir, service);
372                 }
373             } catch (Exception e) {
374                 LOG.error("Parse testcase yaml failed !!!", e);
375             }
376         });
377         return Response.ok("Save yaml success", MediaType.APPLICATION_JSON).build();
378     }
379
380         private void processCurrentScenarioCommands(String scenario, File scenarioDir, Object service)
381                         throws IOException, FileNotFoundException {
382                 Map<String, Object> serviceMap = (Map<String, Object>) service;
383                 String testsuite = serviceMap.get("name").toString();
384                 File testsuiteDir = new File(scenarioDir, testsuite);
385                 FileUtils.forceMkdir(testsuiteDir);
386                 if (!serviceMap.containsKey("commands")) {
387                     return;
388                 }
389                 for (Object cmd : (List) serviceMap.get("commands")) {
390                     File source = new File(VTP_YAML_STORE, cmd.toString().replaceAll("::", Matcher.quoteReplacement(File.separator)));
391                     if (!source.isFile()) {
392                         LOG.error("Source {} is not a yaml file !!!", source.getName());
393                         continue;
394                     }
395                     File dest = new File(testsuiteDir, cmd.toString().substring(cmd.toString().lastIndexOf("::") + 2));
396                     FileUtils.copyFile(source, dest);
397
398                     // 3、modify the testcase scenario and testsuite
399                     Map<String, Object> result = Maps.newHashMap();
400                     try (FileReader fileReader = new FileReader(dest)) {
401                         result = snakeYaml().load(fileReader);
402                     }
403                     Map<String, Object> info = (Map<String, Object>) result.get("info");
404                     info.put(PRODUCT, scenario);
405                     info.put(SERVICE, testsuite);
406                     try (FileWriter fileWriter = new FileWriter(dest)) {
407                         snakeYaml().dump(result, fileWriter);
408                     }
409                 }
410         }
411
412
413     @Path("/scenarios/{scenarioName}")
414     @DELETE
415     @ApiOperation(tags = "VTP Scenario", value = "Delete yaml string")
416     @Produces(MediaType.APPLICATION_JSON)
417     @ApiResponses(value = {
418             @ApiResponse(code = HttpStatus.INTERNAL_SERVER_ERROR_500, message = "Failed to perform the operation", response = VTPError.class)})
419     public Response deleteScenario(@ApiParam("Test scenario yaml") @PathParam("scenarioName") String scenarioName) throws VTPException {
420         String scenario = scenarioName.substring(0, scenarioName.indexOf("-registry"));
421         File scenarioDir = new File(VTP_YAML_STORE, scenario);
422         List<File> yamls =  FileUtil.searchFiles(scenarioDir, CommonConstant.YAML_SUFFIX);
423         if (!CollectionUtils.isEmpty(yamls)) {
424         if (LOG.isInfoEnabled()) {
425             LOG.error("The scenario yaml {} has sub testcase yamls, delete failed", loggerPatternBreaking(scenarioName));
426         }
427             LOG.error("The scenario yaml {} has sub testcase yamls, delete failed", scenarioName);
428             throw new VTPException(
429                     new VTPError().setMessage(MessageFormat.format("The scenario yaml {0} has sub testcase yamls, delete failed !!!", scenarioName))
430                             .setHttpStatus(HttpStatus.INTERNAL_SERVER_ERROR_500));
431         }
432
433         try {
434             FileUtils.deleteQuietly(new File(VTP_YAML_STORE, scenarioName));
435             FileUtils.deleteDirectory(scenarioDir);
436         } catch (IOException e) {
437             LOG.error("Delete scenario yaml {} failed", scenarioName, e);
438             throw new VTPException(
439                     new VTPError().setMessage("Delete yaml failed !!!").setHttpStatus(HttpStatus.INTERNAL_SERVER_ERROR_500));
440         }
441         return Response.ok("Delete yaml success", MediaType.APPLICATION_JSON).build();
442     }
443
444     @Path("/testcases")
445     @POST
446     @ApiOperation(tags = "VTP Scenario", value = "Create test case")
447     @Consumes(MediaType.MULTIPART_FORM_DATA)
448     @Produces(MediaType.APPLICATION_JSON)
449     @ApiResponses(value = {
450             @ApiResponse(code = HttpStatus.INTERNAL_SERVER_ERROR_500, message = "Failed to perform the operation", response = VTPError.class)})
451     public Response storageTestcases(@ApiParam(value = "file form data body parts", required = true)
452                                      @FormDataParam("files") List<FormDataBodyPart> bodyParts) throws VTPException {
453         bodyParts.forEach(bodyPart -> {
454             BodyPartEntity entity = (BodyPartEntity) bodyPart.getEntity();
455             String fileName = bodyPart.getContentDisposition().getFileName();
456             if (ToolUtil.isYamlFile(new File(fileName))) {
457                 // 1、store the testcase yaml file
458                 Map<String, Object> result = snakeYaml().load(entity.getInputStream());
459                 Map<String, Object> info = (Map<String, Object>) result.get("info");
460
461                 File yamlFile = new File(VTP_YAML_STORE, info.get(PRODUCT) + File.separator + info.get(SERVICE) + File.separator + fileName);
462                 try {
463                     FileUtils.deleteQuietly(yamlFile);
464                     FileUtils.copyInputStreamToFile(entity.getInputStream(), yamlFile);
465                 } catch (IOException e) {
466                     LOG.error("Save testcase yaml {} failed", yamlFile.getName(), e);
467                 }
468             } else {
469                 // 2、store the testcase script file
470                 File scriptFile = new File(VTP_SCRIPT_STORE, fileName);
471                 try {
472                     FileUtils.deleteQuietly(scriptFile);
473                     FileUtils.copyInputStreamToFile(entity.getInputStream(), scriptFile);
474                 } catch (IOException e) {
475                     LOG.error("Save testcase script {} failed", scriptFile.getName(), e);
476                 }
477             }
478         });
479         return Response.ok("Save success", MediaType.APPLICATION_JSON).build();
480     }
481 }