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.cli.fw.cmd.cmd;
20 import java.io.FileInputStream;
21 import java.io.FileOutputStream;
22 import java.io.IOException;
23 import java.nio.charset.Charset;
24 import java.util.ArrayList;
25 import java.util.HashMap;
26 import java.util.List;
28 import java.util.Map.Entry;
30 import org.apache.commons.io.FileUtils;
31 import org.onap.cli.fw.cmd.OnapCommand;
32 import org.onap.cli.fw.cmd.conf.OnapCommandCmdConstants;
33 import org.onap.cli.fw.cmd.error.OnapCommandCmdFailure;
34 import org.onap.cli.fw.cmd.schema.OnapCommandSchemaCmdLoader;
35 import org.onap.cli.fw.conf.OnapCommandConfig;
36 import org.onap.cli.fw.error.OnapCommandException;
37 import org.onap.cli.fw.error.OnapCommandExecutionFailed;
38 import org.onap.cli.fw.error.OnapCommandResultEmpty;
39 import org.onap.cli.fw.error.OnapCommandResultMapProcessingFailed;
40 import org.onap.cli.fw.input.OnapCommandParameter;
41 import org.onap.cli.fw.output.OnapCommandResultAttribute;
42 import org.onap.cli.fw.output.OnapCommandResultType;
43 import org.onap.cli.fw.registrar.OnapCommandRegistrar;
44 import org.onap.cli.fw.schema.OnapCommandSchema;
45 import org.onap.cli.fw.store.OnapCommandExecutionStore;
46 import org.onap.cli.fw.utils.OnapCommandUtils;
47 import org.onap.cli.fw.utils.ProcessRunner;
49 import com.jayway.jsonpath.JsonPath;
50 import com.jayway.jsonpath.PathNotFoundException;
52 import net.minidev.json.JSONArray;
57 @OnapCommandSchema(type = "cmd")
58 public class OpenCommandShellCmd extends OnapCommand {
60 public OpenCommandShellCmd() {
61 super.addDefaultSchemas(OnapCommandCmdConstants.DEFAULT_PARAMETER_CMD_FILE_NAME);
64 private Map<String, String> resultMap = new HashMap<>();
66 private List<String> command;
68 private Map<String, String> envs = new HashMap<>();
70 private String wd = null;
72 private List<Integer> successStatusCodes = new ArrayList<>();
74 private List<Integer> passCodes = new ArrayList<>();
76 private String output = "$stdout";
78 private String error = "$stderr";
80 public List<Integer> getSuccessStatusCodes() {
81 return successStatusCodes;
84 public void setSuccessStatusCodes(List<Integer> successStatusCodes) {
85 this.successStatusCodes = successStatusCodes;
88 public String getWd() {
92 public void setWd(String wd) {
96 public Map<String, String> getEnvs() {
100 public void setEnvs(Map<String, String> envs) {
105 public List<String> getCommand() {
109 public void setCommand(List<String> command) {
110 this.command = command;
113 public Map<String, String> getResultMap() {
117 public void setResultMap(Map<String, String> resultMap) {
118 this.resultMap = resultMap;
121 private String getStdoutPath() {
122 String storePath = this.getExecutionContext().getStorePath();
123 storePath = storePath + File.separator + "stdout";
127 private String getStderrPath() {
128 String storePath = this.getExecutionContext().getStorePath();
129 storePath = storePath + File.separator + "stderr";
133 private String getOutputAttributeFilePath(String attrName, boolean temp) {
134 String storePath = (!temp) ? this.getExecutionContext().getStorePath() : OnapCommandConfig.getPropertyValue("cli.tmp.dir");
135 String randomId = (this.getExecutionContext() != null) ? this.getExecutionContext().getExecutionId() : ("" + System.currentTimeMillis());
136 storePath = storePath + File.separator + randomId + "_" + attrName;
141 protected List<String> initializeProfileSchema(Map<String, ?> schemaMap, boolean validate) throws OnapCommandException {
142 return OnapCommandSchemaCmdLoader.parseCmdSchema(this, schemaMap, validate);
146 protected void run() throws OnapCommandException {
147 //Read the input arguments
148 Map<String, OnapCommandParameter> paramMap = this.getParametersMap();
150 //process command section
151 Map<String, String> tmpFiles = new HashMap<>();
152 List<String> commandLine = new ArrayList<>();
153 for (String cmdTkn: this.getCommand()) {
154 tmpFiles.putAll(this.formTmpFiles(cmdTkn));
155 String commandLine1 = OnapCommandUtils.replaceLineForSpecialValues(
157 commandLine1 = OnapCommandUtils.replaceLineFromInputParameters(commandLine1, paramMap);
159 commandLine.add(commandLine1);
162 long timeout = Long.parseLong(this.getParametersMap().get(OnapCommandCmdConstants.TIMEOUT).getValue().toString());
165 String []cmd = commandLine.toArray(new String []{});
166 String cwd = this.getWd();
167 List <String> envList = new ArrayList<>();
169 //add current process environments to sub process
170 for (Map.Entry<String, String> env: System.getenv().entrySet()) { //NOSONAR
171 envList.add(env.getKey() + "=" + env.getValue());
174 //add oclip specific environment variables
175 if (this.getExecutionContext() != null) {
176 envList.add("OPEN_CLI_REQUEST_ID=" + this.getExecutionContext().getRequestId());
177 if (this.getExecutionContext().getProfile() != null) {
178 envList.add("OPEN_CLI_PROFILE=" + this.getExecutionContext().getProfile());
180 if (OnapCommandRegistrar.getRegistrar().getHost() != null) {
181 envList.add("OPEN_CLI_RPC_HOST=" + OnapCommandRegistrar.getRegistrar().getHost());
182 envList.add("OPEN_CLI_RPC_PORT=" + OnapCommandRegistrar.getRegistrar().getPort());
186 for (String env: this.getEnvs().keySet()) {
187 envList.add(env + "=" + this.getEnvs().get(env));
190 ProcessRunner pr = new ProcessRunner(
192 (!envList.isEmpty()) ? envList.toArray(new String []{}) : null,
194 FileOutputStream stdoutStream = null;
195 FileOutputStream stderrStream = null;
196 String outputValue = "";
199 pr.setTimeout(timeout);
201 if (this.getExecutionContext() != null) {
203 stdoutStream = new FileOutputStream(this.getStdoutPath());
204 stderrStream = new FileOutputStream(this.getStderrPath());
206 pr.setStdout(stdoutStream);
207 pr.setStderr(stderrStream);
209 OnapCommandExecutionStore.getStore().storeExectutionDebug(this.getExecutionContext(), pr.toString());
212 this.getResult().setDebugInfo(pr.toString());
215 } catch (Exception e) {// NOSONAR
216 throw new OnapCommandExecutionFailed(this.getName(), e);
218 if (stdoutStream != null) {
220 stdoutStream.close();
221 } catch (IOException e) {
222 //never occurs // NOSONAR
225 if (stderrStream != null) {
227 stderrStream.close();
228 } catch (IOException e) {
229 //never occurs // NOSONAR
234 if (!this.successStatusCodes.contains(pr.getExitCode())) {
235 throw new OnapCommandExecutionFailed(this.getName(), pr.getError(), pr.getExitCode());
238 if (this.output.equals("$stdout")) {
239 if (this.getExecutionContext() != null) {
240 try (FileInputStream is = new FileInputStream(this.getStdoutPath())){
241 outputValue = pr.streamToString(is);
242 } catch (IOException e) {
243 //never occurs // NOSONAR
246 outputValue = pr.getOutput();
248 } else if (this.output.equals("$stderr")) {
249 if (this.getExecutionContext() != null) {
250 try (FileInputStream is = new FileInputStream(this.getStderrPath())) {
251 outputValue = pr.streamToString(is);
252 } catch (IOException e) {
253 //never occurs // NOSONAR
256 outputValue = pr.getError();
259 //remove $(tmp: and closing )
260 String tmpName = this.output.substring(7, this.output.length()-1);
261 String tmpFile = tmpFiles.get("tmp:" + tmpName);
262 if (tmpFile != null) {
263 try (FileInputStream is = new FileInputStream(tmpFile)) {
264 outputValue = pr.streamToString(is);
265 } catch (IOException e) {
266 //never occurs // NOSONAR
271 this.getResult().setDebugInfo(pr.toString() + "\n" + outputValue);
272 this.getResult().setOutput(outputValue);
275 if (!this.getResult().getType().name().equalsIgnoreCase(OnapCommandResultType.TEXT.name()))
276 for (Entry<String, String> resultMapEntry : this.getResultMap().entrySet()) {
277 String attrName = resultMapEntry.getKey();
278 OnapCommandResultAttribute attr = this.getResult().getRecordsMap().get(attrName);
280 String value = OnapCommandUtils.replaceLineFromInputParameters(resultMapEntry.getValue(), paramMap);
281 value = OnapCommandUtils.replaceLineForSpecialValues(value);
282 attr.setValues(this.replaceLineFromOutputResults(value, outputValue));
285 //check for pass/failure
286 this.getResult().setPassed(!(!this.passCodes.isEmpty() && !this.passCodes.contains(pr.getExitCode())));
289 public String getOutput() {
293 public void setOutput(String output) {
294 this.output = output;
297 private Map<String, String> formTmpFiles(String line){
299 Map<String, String> result = new HashMap<>();
301 if (!line.contains("$s{tmp")) {
306 while (currentIdx < line.length()) {
307 int idxS = line.indexOf("$s{tmp:", currentIdx); //check for output stream
312 int idxE = line.indexOf('}', idxS);
313 String tmpName = line.substring(idxS + 7, idxE);
314 tmpName = tmpName.trim();
315 String[] tmpTkns = tmpName.split(":");
318 if (tmpTkns.length == 2) {
319 tmpFileName = tmpTkns[0];
320 paramName = tmpTkns[1];
322 tmpFileName = tmpTkns[0];
326 String tmpFilePath = this.getOutputAttributeFilePath(tmpFileName, true);
327 if (paramName != null) {
328 //Write the value of input params into file before passing to command
330 FileUtils.touch(new File(tmpFilePath));
331 FileUtils.writeStringToFile(new File(tmpFilePath),
332 this.getParametersMap().get(paramName).getValue().toString(), (Charset) null);
333 } catch (IOException e) {
338 result.put("tmp:" + tmpFileName, tmpFilePath); //used in output parsing
339 result.put("tmp:" + tmpName, tmpFilePath); //used in line replacement
341 currentIdx = idxE + 1;
346 private ArrayList<String> replaceLineFromOutputResults(String line, String output)
347 throws OnapCommandException {
350 ArrayList<String> result = new ArrayList<>();
351 if (!line.contains("$o{")) {
357 * In case of empty output [] or {}
359 if (output.length() <= 2) {
365 // Process jsonpath macros
366 List<Object> values = new ArrayList<>();
367 StringBuilder processedPattern = new StringBuilder();
369 int maxRows = 1; // in normal case, only one row will be there
370 while (currentIdx < line.length()) {
371 int idxS = line.indexOf("$o{", currentIdx); //check for output stream
373 idxS = line.indexOf("$e{", currentIdx); //check for error stream
375 processedPattern.append(line.substring(currentIdx));
379 int idxE = line.indexOf('}', idxS);
380 String jsonPath = line.substring(idxS + 3, idxE);
381 jsonPath = jsonPath.trim();
382 Object value = new Object();
384 // Instead of parsing, just assign the json as it is
385 if (!jsonPath.equals("$"))
386 value = JsonPath.read(output, jsonPath);
389 } catch (PathNotFoundException e1) { // NOSONAR
390 //set to blank for those entries which are missing from the output json
392 } catch (Exception e) {
393 throw new OnapCommandCmdFailure("Invalid json format in command output");
396 if (value instanceof JSONArray) {
397 JSONArray arr = (JSONArray) value;
398 if (arr.size() > maxRows) {
399 maxRows = arr.size();
402 processedPattern.append(line.substring(currentIdx, idxS) + "%s");
404 currentIdx = idxE + 1;
407 if (processedPattern.toString().isEmpty()) {
411 for (int i = 0; i < maxRows; i++) {
413 StringBuilder bodyProcessedLine = new StringBuilder();
414 int positionalIdx = 0; // %s positional idx
415 while (currentIdx < processedPattern.length()) {
416 int idxS = processedPattern.indexOf("%s", currentIdx);
418 bodyProcessedLine.append(processedPattern.substring(currentIdx));
422 int idxEnd = idxS + 2; // %s
425 Object val = values.get(positionalIdx);
426 String valStr = String.valueOf(val);
428 if (val instanceof JSONArray) {
429 JSONArray aJson = (JSONArray) val;
431 if (!aJson.isEmpty()) {
432 valStr = aJson.get(i).toString();
434 throw new OnapCommandResultEmpty();
438 bodyProcessedLine.append(processedPattern.substring(currentIdx, idxS) + valStr);
441 } catch (OnapCommandResultEmpty e) {
443 } catch (Exception e) {
444 throw new OnapCommandResultMapProcessingFailed(line, e);
447 result.add(bodyProcessedLine.toString());
454 public String getError() {
458 public void setError(String error) {
462 public List<Integer> getPassCodes() {
466 public void setPassCodes(List<Integer> passCodes) {
467 this.passCodes = passCodes;