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.util.ArrayList;
24 import java.util.HashMap;
25 import java.util.LinkedHashMap;
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.google.gson.Gson;
50 import com.jayway.jsonpath.JsonPath;
51 import com.jayway.jsonpath.PathNotFoundException;
53 import net.minidev.json.JSONArray;
54 import net.minidev.json.JSONObject;
59 @OnapCommandSchema(type = "cmd")
60 public class OpenCommandShellCmd extends OnapCommand {
62 public OpenCommandShellCmd() {
63 super.addDefaultSchemas(OnapCommandCmdConstants.DEFAULT_PARAMETER_CMD_FILE_NAME);
66 private Map<String, String> resultMap = new HashMap<>();
68 private List<String> command;
70 private Map<String, String> envs = new HashMap<>();
72 private String wd = null;
74 private List<Integer> successStatusCodes = new ArrayList<>();
76 private List<Integer> passCodes = new ArrayList<>();
78 private String output = "$stdout";
80 private String error = "$stderr";
82 public List<Integer> getSuccessStatusCodes() {
83 return successStatusCodes;
86 public void setSuccessStatusCodes(ArrayList<Integer> successStatusCodes) {
87 this.successStatusCodes = successStatusCodes;
90 public String getWd() {
94 public void setWd(String wd) {
98 public Map<String, String> getEnvs() {
102 public void setEnvs(Map<String, String> envs) {
107 public List<String> getCommand() {
111 public void setCommand(List<String> command) {
112 this.command = command;
115 public Map<String, String> getResultMap() {
119 public void setResultMap(Map<String, String> resultMap) {
120 this.resultMap = resultMap;
123 private String getStdoutPath() {
124 String storePath = this.getExecutionContext().getStorePath();
125 storePath = storePath + File.separator + "stdout";
129 private String getStderrPath() {
130 String storePath = this.getExecutionContext().getStorePath();
131 storePath = storePath + File.separator + "stderr";
135 private String getOutputAttributeFilePath(String attrName, boolean temp) {
136 String storePath = (!temp) ? this.getExecutionContext().getStorePath() : OnapCommandConfig.getPropertyValue("cli.tmp.dir");
137 String randomId = (this.getExecutionContext() != null) ? this.getExecutionContext().getExecutionId() : ("" + System.currentTimeMillis());
138 storePath = storePath + File.separator + randomId + "_" + attrName;
143 protected List<String> initializeProfileSchema(Map<String, ?> schemaMap, boolean validate) throws OnapCommandException {
144 return OnapCommandSchemaCmdLoader.parseCmdSchema(this, schemaMap, validate);
148 protected void run() throws OnapCommandException {
149 //Read the input arguments
150 Map<String, OnapCommandParameter> paramMap = this.getParametersMap();
152 //process command section
153 Map<String, String> tmpFiles = new HashMap<>();
154 List<String> commandLine = new ArrayList<>();
155 for (String cmdTkn: this.getCommand()) {
156 tmpFiles.putAll(this.formTmpFiles(cmdTkn));
157 String commandLine1 = OnapCommandUtils.replaceLineForSpecialValues(
159 commandLine1 = OnapCommandUtils.replaceLineFromInputParameters(commandLine1, paramMap);
161 commandLine.add(commandLine1);
164 long timeout = Long.parseLong(this.getParametersMap().get(OnapCommandCmdConstants.TIMEOUT).getValue().toString());
167 String []cmd = commandLine.toArray(new String []{});
168 String cwd = this.getWd();
169 List <String> envs = new ArrayList<>();
171 //add current process environments to sub process
172 for (Map.Entry<String, String> env: System.getenv().entrySet()) { //NOSONAR
173 envs.add(env.getKey() + "=" + env.getValue());
176 //add oclip specific environment variables
177 if (this.getExecutionContext() != null) {
178 envs.add("OPEN_CLI_REQUEST_ID=" + this.getExecutionContext().getRequestId());
179 if (this.getExecutionContext().getProfile() != null) {
180 envs.add("OPEN_CLI_PROFILE=" + this.getExecutionContext().getProfile());
182 if (OnapCommandRegistrar.getRegistrar().getHost() != null) {
183 envs.add("OPEN_CLI_RPC_HOST=" + OnapCommandRegistrar.getRegistrar().getHost());
184 envs.add("OPEN_CLI_RPC_PORT=" + OnapCommandRegistrar.getRegistrar().getPort());
188 for (String env: this.getEnvs().keySet()) {
189 envs.add(env + "=" + this.getEnvs().get(env));
192 ProcessRunner pr = new ProcessRunner(
194 (envs.size() > 0) ? envs.toArray(new String []{}) : null,
196 FileOutputStream stdoutStream = null;
197 FileOutputStream stderrStream = null;
198 String outputValue = "";
201 pr.setTimeout(timeout);
203 if (this.getExecutionContext() != null) {
205 stdoutStream = new FileOutputStream(this.getStdoutPath());
206 stderrStream = new FileOutputStream(this.getStderrPath());
208 pr.setStdout(stdoutStream);
209 pr.setStderr(stderrStream);
211 OnapCommandExecutionStore.getStore().storeExectutionDebug(this.getExecutionContext(), pr.toString());
214 this.getResult().setDebugInfo(pr.toString());
217 } catch (Exception e) {
218 throw new OnapCommandExecutionFailed(this.getName(), e);
220 if (stdoutStream != null) {
222 stdoutStream.close();
223 } catch (IOException e) {
224 //never occurs // NOSONAR
227 if (stderrStream != null) {
229 stderrStream.close();
230 } catch (IOException e) {
231 //never occurs // NOSONAR
236 if (!this.successStatusCodes.contains(pr.getExitCode())) {
237 throw new OnapCommandExecutionFailed(this.getName(), pr.getError(), pr.getExitCode());
240 if (this.output.equals("$stdout")) {
241 if (this.getExecutionContext() != null) {
242 try (FileInputStream is = new FileInputStream(this.getStdoutPath())){
243 outputValue = pr.streamToString(is);
244 } catch (IOException e) {
245 //never occurs // NOSONAR
248 outputValue = pr.getOutput();
250 } else if (this.output.equals("$stderr")) {
251 if (this.getExecutionContext() != null) {
252 try (FileInputStream is = new FileInputStream(this.getStderrPath())) {
253 outputValue = pr.streamToString(is);
254 } catch (IOException e) {
255 //never occurs // NOSONAR
258 outputValue = pr.getError();
261 //remove ${tmp: and closing }
262 String tmpName = this.output.substring(7, this.output.length()-1);
263 String tmpFile = tmpFiles.get("tmp:" + tmpName);
264 if (tmpFile != null) {
265 try (FileInputStream is = new FileInputStream(tmpFile)) {
266 outputValue = pr.streamToString(is);
267 } catch (IOException e) {
268 //never occurs // NOSONAR
273 this.getResult().setDebugInfo(pr.toString() + "\n" + outputValue);
274 this.getResult().setOutput(outputValue);
277 if (!this.getResult().getType().name().equalsIgnoreCase(OnapCommandResultType.TEXT.name()))
278 for (Entry<String, String> resultMapEntry : this.getResultMap().entrySet()) {
279 String attrName = resultMapEntry.getKey();
280 OnapCommandResultAttribute attr = this.getResult().getRecordsMap().get(attrName);
282 String value = OnapCommandUtils.replaceLineFromInputParameters(resultMapEntry.getValue(), paramMap);
283 value = OnapCommandUtils.replaceLineForSpecialValues(value);
284 attr.setValues(this.replaceLineFromOutputResults(value, outputValue));
287 //check for pass/failure
288 if (!this.passCodes.isEmpty() && !this.passCodes.contains(pr.getExitCode())) {
289 this.getResult().setPassed(false);
291 this.getResult().setPassed(true);
295 public String getOutput() {
299 public void setOutput(String output) {
300 this.output = output;
303 private Map<String, String> formTmpFiles(String line){
305 Map<String, String> result = new HashMap<>();
307 if (!line.contains("$s{tmp")) {
312 while (currentIdx < line.length()) {
313 int idxS = line.indexOf("$s{tmp:", currentIdx); //check for output stream
318 int idxE = line.indexOf("}", idxS);
319 String tmpName = line.substring(idxS + 7, idxE);
320 tmpName = tmpName.trim();
321 String tmpTkns[] = tmpName.split(":");
324 if (tmpTkns.length == 2) {
325 tmpFileName = tmpTkns[0];
326 paramName = tmpTkns[1];
328 tmpFileName = tmpTkns[0];
332 String tmpFilePath = this.getOutputAttributeFilePath(tmpFileName, true);
333 if (paramName != null) {
334 //Write the value of input params into file before passing to command
336 FileUtils.touch(new File(tmpFilePath));
337 FileUtils.writeStringToFile(new File(tmpFilePath),
338 this.getParametersMap().get(paramName).getValue().toString());
339 } catch (IOException e) {
344 result.put("tmp:" + tmpFileName, tmpFilePath); //used in output parsing
345 result.put("tmp:" + tmpName, tmpFilePath); //used in line replacement
347 currentIdx = idxE + 1;
352 private ArrayList<String> replaceLineFromOutputResults(String line, String output)
353 throws OnapCommandException {
356 ArrayList<String> result = new ArrayList<>();
357 if (!line.contains("$o{")) {
363 * In case of empty output [] or {}
365 if (output.length() <= 2) {
371 // Process jsonpath macros
372 List<Object> values = new ArrayList<>();
373 String processedPattern = "";
375 int maxRows = 1; // in normal case, only one row will be there
376 while (currentIdx < line.length()) {
377 int idxS = line.indexOf("$o{", currentIdx); //check for output stream
379 idxS = line.indexOf("$e{", currentIdx); //check for error stream
381 processedPattern += line.substring(currentIdx);
385 int idxE = line.indexOf("}", idxS);
386 String jsonPath = line.substring(idxS + 3, idxE);
387 jsonPath = jsonPath.trim();
388 Object value = new Object();
390 // Instead of parsing, just assign the json as it is
391 if (!jsonPath.equals("$"))
392 value = JsonPath.read(output, jsonPath);
395 } catch (PathNotFoundException e1) { // NOSONAR
396 //set to blank for those entries which are missing from the output json
398 } catch (Exception e) {
399 throw new OnapCommandCmdFailure("Invalid json format in command output");
402 if (value instanceof JSONArray) {
403 JSONArray arr = (JSONArray) value;
404 if (arr.size() > maxRows) {
405 maxRows = arr.size();
408 processedPattern += line.substring(currentIdx, idxS) + "%s";
410 currentIdx = idxE + 1;
413 if (processedPattern.isEmpty()) {
417 for (int i = 0; i < maxRows; i++) {
419 String bodyProcessedLine = "";
420 int positionalIdx = 0; // %s positional idx
421 while (currentIdx < processedPattern.length()) {
422 int idxS = processedPattern.indexOf("%s", currentIdx);
424 bodyProcessedLine += processedPattern.substring(currentIdx);
428 int idxEnd = idxS + 2; // %s
431 Object val = values.get(positionalIdx);
432 String valStr = String.valueOf(val);
434 if (val instanceof JSONArray) {
435 JSONArray aJson = (JSONArray) val;
437 if (!aJson.isEmpty()) {
438 valStr = aJson.get(i).toString();
440 throw new OnapCommandResultEmpty();
444 bodyProcessedLine += processedPattern.substring(currentIdx, idxS) + valStr;
447 } catch (OnapCommandResultEmpty e) {
449 } catch (Exception e) {
450 throw new OnapCommandResultMapProcessingFailed(line, e);
453 result.add(bodyProcessedLine);
460 public String getError() {
464 public void setError(String error) {
468 public List<Integer> getPassCodes() {
472 public void setPassCodes(List<Integer> passCodes) {
473 this.passCodes = passCodes;