From 9f26a5983e007f8e888af3dd8d1382c83fce446b Mon Sep 17 00:00:00 2001 From: Kanagaraj Manickam k00365106 Date: Thu, 28 Feb 2019 12:29:27 +0530 Subject: CMD: Enhace command profile with additional macros Issue-ID: CLI-129 Change-Id: I612ecfe2c25f73714a8759ce87fdc373c8d5a7f0 Signed-off-by: Kanagaraj Manickam k00365106 --- .../onap/cli/fw/cmd/cmd/OpenCommandShellCmd.java | 184 ++++++++++++++++++++- .../org/onap/cli/fw/cmd/cmd/ProcessRunner.java | 135 --------------- .../cli/fw/cmd/conf/OnapCommandCmdConstants.java | 6 +- .../fw/cmd/schema/OnapCommandSchemaCmdLoader.java | 17 +- .../cmd/default_input_parameters_cmd.yaml | 19 --- 5 files changed, 198 insertions(+), 163 deletions(-) delete mode 100644 profiles/command/src/main/java/org/onap/cli/fw/cmd/cmd/ProcessRunner.java (limited to 'profiles/command/src/main') diff --git a/profiles/command/src/main/java/org/onap/cli/fw/cmd/cmd/OpenCommandShellCmd.java b/profiles/command/src/main/java/org/onap/cli/fw/cmd/cmd/OpenCommandShellCmd.java index 8969c2b6..69987d9b 100644 --- a/profiles/command/src/main/java/org/onap/cli/fw/cmd/cmd/OpenCommandShellCmd.java +++ b/profiles/command/src/main/java/org/onap/cli/fw/cmd/cmd/OpenCommandShellCmd.java @@ -21,14 +21,25 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Map.Entry; import org.onap.cli.fw.cmd.OnapCommand; import org.onap.cli.fw.cmd.conf.OnapCommandCmdConstants; +import org.onap.cli.fw.cmd.error.OnapCommandCmdFailure; import org.onap.cli.fw.cmd.schema.OnapCommandSchemaCmdLoader; import org.onap.cli.fw.error.OnapCommandException; import org.onap.cli.fw.error.OnapCommandExecutionFailed; +import org.onap.cli.fw.error.OnapCommandResultEmpty; +import org.onap.cli.fw.error.OnapCommandResultMapProcessingFailed; import org.onap.cli.fw.input.OnapCommandParameter; import org.onap.cli.fw.schema.OnapCommandSchema; +import org.onap.cli.fw.utils.OnapCommandUtils; +import org.onap.cli.fw.utils.ProcessRunner; + +import com.jayway.jsonpath.JsonPath; +import com.jayway.jsonpath.PathNotFoundException; + +import net.minidev.json.JSONArray; /** * Hello world. @@ -44,12 +55,18 @@ public class OpenCommandShellCmd extends OnapCommand { private List command; - private Map envs; + private Map envs = new HashMap<>(); private String wd = null; private List successStatusCodes = new ArrayList<>(); + private List passCodes = new ArrayList<>(); + + private String output = "$stdout"; + + private String error = "$stderr"; + public List getSuccessStatusCodes() { return successStatusCodes; } @@ -75,7 +92,6 @@ public class OpenCommandShellCmd extends OnapCommand { } - public List getCommand() { return command; } @@ -102,8 +118,13 @@ public class OpenCommandShellCmd extends OnapCommand { //Read the input arguments Map paramMap = this.getParametersMap(); + List commandLine = new ArrayList<>(); + for (String cmdTkn: this.getCommand()) { + commandLine.add(OnapCommandUtils.replaceLineFromInputParameters(cmdTkn, paramMap)); + } + //Process command - String []cmd = this.getCommand().toArray(new String []{}); + String []cmd = commandLine.toArray(new String []{}); String cwd = this.getWd(); List envs = new ArrayList<>(); @@ -121,9 +142,158 @@ public class OpenCommandShellCmd extends OnapCommand { throw new OnapCommandExecutionFailed(this.getName(), e); } - //Populate outputs - this.getResult().getRecordsMap().get("output").getValues().add(pr.getOutput()); - this.getResult().getRecordsMap().get("error").getValues().add(pr.getError()); - this.getResult().getRecordsMap().get("exitCode").getValues().add("" + pr.getExitCode()); + if (!this.successStatusCodes.contains(pr.getExitCode())) { + throw new OnapCommandExecutionFailed(this.getName(), pr.getError(), pr.getExitCode()); + } + + String outputValue = ""; + + if (this.output.equals("$stdout")) { + outputValue = pr.getOutput(); + } else { + outputValue = OnapCommandUtils.replaceLineFromInputParameters(this.output, paramMap); + outputValue = OnapCommandUtils.replaceLineForSpecialValues(outputValue); + } + + this.getResult().setOutput(outputValue); + + //populate results + for (Entry resultMapEntry : this.getResultMap().entrySet()) { + String value = OnapCommandUtils.replaceLineFromInputParameters(resultMapEntry.getValue(), paramMap); + value = OnapCommandUtils.replaceLineForSpecialValues(value); + this.getResult().getRecordsMap().get(resultMapEntry.getKey()).setValues( + this.replaceLineFromOutputResults(value, outputValue)); + } + + //check for pass/failure + if (!this.passCodes.contains(pr.getExitCode())) { + this.getResult().setPassed(false); + } } + + public String getOutput() { + return output; + } + + public void setOutput(String output) { + this.output = output; + } + + private ArrayList replaceLineFromOutputResults(String line, String output) + throws OnapCommandException { + + + ArrayList result = new ArrayList<>(); + if (!line.contains("$o{")) { + result.add(line); + return result; + } + + /** + * In case of empty output [] or {} + **/ + if (output.length() <= 2) { + return result; + } + + int currentIdx = 0; + + // Process jsonpath macros + List values = new ArrayList<>(); + String processedPattern = ""; + currentIdx = 0; + int maxRows = 1; // in normal case, only one row will be there + while (currentIdx < line.length()) { + int idxS = line.indexOf("$o{", currentIdx); //check for output stream + if (idxS == -1) { + idxS = line.indexOf("$e{", currentIdx); //check for error stream + if (idxS == -1) { + processedPattern += line.substring(currentIdx); + break; + } + } + int idxE = line.indexOf("}", idxS); + String jsonPath = line.substring(idxS + 3, idxE); + jsonPath = jsonPath.trim(); + Object value = new Object(); + try { + // JSONArray or String + value = JsonPath.read(output, jsonPath); + } catch (PathNotFoundException e1) { // NOSONAR + //set to blank for those entries which are missing from the output json + value = ""; + } catch (Exception e) { + throw new OnapCommandCmdFailure("Invalid json format in command output"); + } + + if (value instanceof JSONArray) { + JSONArray arr = (JSONArray) value; + if (arr.size() > maxRows) { + maxRows = arr.size(); + } + } + processedPattern += line.substring(currentIdx, idxS) + "%s"; + values.add(value); + currentIdx = idxE + 1; + } + + if (processedPattern.isEmpty()) { + result.add(line); + return result; + } else { + for (int i = 0; i < maxRows; i++) { + currentIdx = 0; + String bodyProcessedLine = ""; + int positionalIdx = 0; // %s positional idx + while (currentIdx < processedPattern.length()) { + int idxS = processedPattern.indexOf("%s", currentIdx); + if (idxS == -1) { + bodyProcessedLine += processedPattern.substring(currentIdx); + break; + } + int idxE = idxS + 2; // %s + try { + Object value = values.get(positionalIdx); + String valueS = String.valueOf(value); + if (value instanceof JSONArray) { + JSONArray arr = (JSONArray) value; + if (!arr.isEmpty()) { + valueS = arr.get(i).toString(); + } else { + throw new OnapCommandResultEmpty(); + } + } + + bodyProcessedLine += processedPattern.substring(currentIdx, idxS) + valueS; + currentIdx = idxE; + positionalIdx++; + } catch (OnapCommandResultEmpty e) { + throw e; + } catch (Exception e) { + throw new OnapCommandResultMapProcessingFailed(line, e); + } + } + result.add(bodyProcessedLine); + } + + return result; + } + } + +public String getError() { + return error; +} + +public void setError(String error) { + this.error = error; +} + +public List getPassCodes() { + return passCodes; +} + +public void setPassCodes(List passCodes) { + this.passCodes = passCodes; +} + } diff --git a/profiles/command/src/main/java/org/onap/cli/fw/cmd/cmd/ProcessRunner.java b/profiles/command/src/main/java/org/onap/cli/fw/cmd/cmd/ProcessRunner.java deleted file mode 100644 index a86a0ff5..00000000 --- a/profiles/command/src/main/java/org/onap/cli/fw/cmd/cmd/ProcessRunner.java +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Copyright 2018 Huawei Technologies Co., Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.onap.cli.fw.cmd.cmd; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Map; - -public class ProcessRunner { - - private String []cmd = null; - private static String shell = System.getProperty("os.name").toLowerCase().startsWith("windows") ? "cmd.exe /c " : "sh -c "; - private String cwd = System.getProperty("user.home"); - private String []env = null; - private int exitCode = -1; - private String output; - private String error; - private Map results; - - public ProcessRunner(String []cmd, String []env, String cwd) { - this.cmd = cmd; - - if (cwd != null && !cwd.isEmpty()) { - this.cwd = cwd; - } - - this.env = env; - } - - public ProcessRunner(String []cmd, String cwd) { - this(cmd, null, cwd); - } - - public ProcessRunner(String []cmd) { - this(cmd, null, null); - } - - public ProcessRunner(String cmd, String []env, String cwd) { - this(new String []{cmd}, env, cwd); - } - - public ProcessRunner(String cmd, String cwd) { - this(new String []{cmd}, null, cwd); - } - - public ProcessRunner(String cmd) { - this(new String []{cmd}, null, null); - } - - @SuppressWarnings("unchecked") - public void run() throws InterruptedException, IOException { - Process p = null; - if (this.cmd.length == 1) { - p = Runtime.getRuntime().exec(this.shell + this.cmd[0], this.env, null); - } else { - List list = new ArrayList(Arrays.asList(this.shell.split(" "))); - list.addAll(Arrays.asList(this.cmd)); - String []cmds = Arrays.copyOf(list.toArray(), list.size(), String[].class); - p = Runtime.getRuntime().exec(cmds, this.env, null); - } - - this.exitCode = p.waitFor(); - this.output = this.streamToString(p.getInputStream()); - this.error = this.streamToString(p.getErrorStream()); - p.destroy(); - } - - public String streamToString(InputStream stream) throws IOException { - StringBuilder sb = new StringBuilder(); - BufferedReader br = null; - try { - br = new BufferedReader(new InputStreamReader(stream)); - String line = null; - while ((line = br.readLine()) != null) { - sb.append(line + System.getProperty("line.separator")); - } - } finally { - if (br != null) { - br.close(); - } - } - return sb.toString(); - } - - public int getExitCode() { - return this.exitCode; - } - - public String getOutput() { - return this.output; - } - - public String getError() { - return this.error; - } - - public static void main(String[] args) { - try { - ProcessRunner pr = new ProcessRunner("dir", null); - pr.run(); - System.out.println(pr.getOutput()); - System.out.println(pr.getError()); - System.out.println(pr.getExitCode()); - - pr = new ProcessRunner(new String [] {"dir", "c:"}, null); - pr.run(); - System.out.println(pr.getOutput()); - System.out.println(pr.getError()); - System.out.println(pr.getExitCode()); - - } catch (InterruptedException | IOException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - } -} \ No newline at end of file diff --git a/profiles/command/src/main/java/org/onap/cli/fw/cmd/conf/OnapCommandCmdConstants.java b/profiles/command/src/main/java/org/onap/cli/fw/cmd/conf/OnapCommandCmdConstants.java index e4f119e4..6594ef71 100644 --- a/profiles/command/src/main/java/org/onap/cli/fw/cmd/conf/OnapCommandCmdConstants.java +++ b/profiles/command/src/main/java/org/onap/cli/fw/cmd/conf/OnapCommandCmdConstants.java @@ -28,7 +28,11 @@ public class OnapCommandCmdConstants { public static final String COMMAND = "command"; public static final String ENVIRONMENT = "environment"; public static final String WD = "working_directory"; - public static final String SUCCESS_EXIT_CODE = "success_code"; + public static final String RESULT_MAP = "result_map"; + public static final String OUTPUT = "output"; + public static final String ERROR = "error"; + public static final String SUCCESS_EXIT_CODE = "success_codes"; + public static final String PASS_CODE = "pass_codes"; public static final String CMD_MANDATORY_SECTIONS = "cli.schema.cmd.sections.mandatory"; public static final String CMD_SECTIONS = "cli.schema.cmd.sections"; diff --git a/profiles/command/src/main/java/org/onap/cli/fw/cmd/schema/OnapCommandSchemaCmdLoader.java b/profiles/command/src/main/java/org/onap/cli/fw/cmd/schema/OnapCommandSchemaCmdLoader.java index 55fab733..965bd2b1 100644 --- a/profiles/command/src/main/java/org/onap/cli/fw/cmd/schema/OnapCommandSchemaCmdLoader.java +++ b/profiles/command/src/main/java/org/onap/cli/fw/cmd/schema/OnapCommandSchemaCmdLoader.java @@ -71,16 +71,31 @@ public class OnapCommandSchemaCmdLoader { case OnapCommandCmdConstants.ENVIRONMENT: Map envMap = (Map) valMap.get(key1); cmd.setEnvs(envMap); - break; case OnapCommandCmdConstants.WD: cmd.setWd((String)valMap.get(key1)); break; + case OnapCommandCmdConstants.OUTPUT: + cmd.setOutput((String)valMap.get(key1)); + break; + + case OnapCommandCmdConstants.ERROR: + cmd.setError((String)valMap.get(key1)); + break; + + case OnapCommandCmdConstants.RESULT_MAP: + cmd.setResultMap((Map) valMap.get(key1)); + break; + case OnapCommandCmdConstants.SUCCESS_EXIT_CODE: cmd.setSuccessStatusCodes((ArrayList) valMap.get(key1)); break; + + case OnapCommandCmdConstants.PASS_CODE: + cmd.setPassCodes((ArrayList) valMap.get(key1)); + break; } } } diff --git a/profiles/command/src/main/resources/open-cli-schema/cmd/default_input_parameters_cmd.yaml b/profiles/command/src/main/resources/open-cli-schema/cmd/default_input_parameters_cmd.yaml index b40c9c07..b358a0c7 100644 --- a/profiles/command/src/main/resources/open-cli-schema/cmd/default_input_parameters_cmd.yaml +++ b/profiles/command/src/main/resources/open-cli-schema/cmd/default_input_parameters_cmd.yaml @@ -13,22 +13,3 @@ # limitations under the License. open_cli_schema_version: 1.0 - -results: - direction: portrait - attributes: - - name: output - description: command output - scope: long - type: string - is_default_attr: true - - name: error - description: command error - scope: short - type: string - is_default_attr: true - - name: exitCode - description: command exit code - scope: short - type: string - is_default_attr: true \ No newline at end of file -- cgit 1.2.3-korg