removed block of commented-out lines of code
[cli.git] / profiles / command / src / main / java / org / onap / cli / fw / cmd / cmd / OpenCommandShellCmd.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.cli.fw.cmd.cmd;
18
19 import java.io.File;
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;
27 import java.util.Map;
28 import java.util.Map.Entry;
29
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;
48
49 import com.jayway.jsonpath.JsonPath;
50 import com.jayway.jsonpath.PathNotFoundException;
51
52 import net.minidev.json.JSONArray;
53
54 /**
55  * Hello world.
56  */
57 @OnapCommandSchema(type = "cmd")
58 public class OpenCommandShellCmd extends OnapCommand {
59
60     public OpenCommandShellCmd() {
61         super.addDefaultSchemas(OnapCommandCmdConstants.DEFAULT_PARAMETER_CMD_FILE_NAME);
62     }
63
64     private Map<String, String> resultMap = new HashMap<>();
65
66     private List<String> command;
67
68     private Map<String, String> envs = new HashMap<>();
69
70     private String wd = null;
71
72     private List<Integer> successStatusCodes = new ArrayList<>();
73
74     private List<Integer> passCodes = new ArrayList<>();
75
76     private String output = "$stdout";
77
78     private String error = "$stderr";
79
80     public List<Integer> getSuccessStatusCodes() {
81         return successStatusCodes;
82     }
83
84     public void setSuccessStatusCodes(List<Integer> successStatusCodes) {
85         this.successStatusCodes = successStatusCodes;
86     }
87
88     public String getWd() {
89         return wd;
90     }
91
92     public void setWd(String wd) {
93         this.wd = wd;
94     }
95
96     public Map<String, String> getEnvs() {
97         return envs;
98     }
99
100     public void setEnvs(Map<String, String> envs) {
101         this.envs = envs;
102     }
103
104
105     public List<String> getCommand() {
106         return command;
107     }
108
109     public void setCommand(List<String> command) {
110         this.command = command;
111     }
112
113     public Map<String, String> getResultMap() {
114         return resultMap;
115     }
116
117     public void setResultMap(Map<String, String> resultMap) {
118         this.resultMap = resultMap;
119     }
120
121     private String getStdoutPath() {
122         String storePath = this.getExecutionContext().getStorePath();
123         storePath = storePath + File.separator + "stdout";
124         return storePath;
125     }
126
127     private String getStderrPath() {
128         String storePath = this.getExecutionContext().getStorePath();
129         storePath = storePath + File.separator + "stderr";
130         return storePath;
131     }
132
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;
137         return storePath;
138     }
139
140     @Override
141     protected List<String> initializeProfileSchema(Map<String, ?> schemaMap, boolean validate) throws OnapCommandException {
142         return OnapCommandSchemaCmdLoader.parseCmdSchema(this, schemaMap, validate);
143     }
144
145     @Override
146     protected void run() throws OnapCommandException {
147         //Read the input arguments
148         Map<String, OnapCommandParameter> paramMap = this.getParametersMap();
149
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(
156                     cmdTkn, tmpFiles);
157             commandLine1 = OnapCommandUtils.replaceLineFromInputParameters(commandLine1, paramMap);
158
159             commandLine.add(commandLine1);
160         }
161
162         long timeout = Long.parseLong(this.getParametersMap().get(OnapCommandCmdConstants.TIMEOUT).getValue().toString());
163
164         //Process command
165         String []cmd = commandLine.toArray(new String []{});
166         String cwd = this.getWd();
167         List <String> envList = new ArrayList<>();
168
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());
172         }
173
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());
179             }
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());
183             }
184         }
185
186         for (String env: this.getEnvs().keySet()) {
187             envList.add(env + "=" + this.getEnvs().get(env));
188         }
189
190         ProcessRunner pr = new ProcessRunner(
191                 cmd,
192                 (!envList.isEmpty()) ? envList.toArray(new String []{}) : null,
193                 cwd);
194         FileOutputStream stdoutStream = null;
195         FileOutputStream stderrStream = null;
196         String outputValue = "";
197
198         try { //NOSONAR
199             pr.setTimeout(timeout);
200
201             if (this.getExecutionContext() != null) {
202
203                 stdoutStream = new FileOutputStream(this.getStdoutPath());
204                 stderrStream = new FileOutputStream(this.getStderrPath());
205
206                 pr.setStdout(stdoutStream);
207                 pr.setStderr(stderrStream);
208
209                 OnapCommandExecutionStore.getStore().storeExectutionDebug(this.getExecutionContext(), pr.toString());
210             }
211
212             this.getResult().setDebugInfo(pr.toString());
213
214             pr.run();
215         } catch (Exception e) {// NOSONAR
216             throw new OnapCommandExecutionFailed(this.getName(), e);
217         } finally {
218             if (stdoutStream != null) {
219                 try {
220                     stdoutStream.close();
221                 } catch (IOException e) {
222                     //never occurs  // NOSONAR
223                 }
224             }
225             if (stderrStream != null) {
226                 try {
227                     stderrStream.close();
228                 } catch (IOException e) {
229                     //never occurs  // NOSONAR
230                 }
231             }
232         }
233
234         if (!this.successStatusCodes.contains(pr.getExitCode())) {
235             throw new OnapCommandExecutionFailed(this.getName(), pr.getError(), pr.getExitCode());
236         }
237
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
244                 }
245             } else
246                 outputValue = pr.getOutput();
247
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
254                 }
255             } else
256                 outputValue = pr.getError();
257
258         } else {
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
267                 }
268             }
269         }
270
271         this.getResult().setDebugInfo(pr.toString() + "\n" + outputValue);
272         this.getResult().setOutput(outputValue);
273
274         //populate results
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);
279
280                 String value = OnapCommandUtils.replaceLineFromInputParameters(resultMapEntry.getValue(), paramMap);
281                 value = OnapCommandUtils.replaceLineForSpecialValues(value);
282                 attr.setValues(this.replaceLineFromOutputResults(value, outputValue));
283             }
284
285         //check for pass/failure
286         this.getResult().setPassed(!(!this.passCodes.isEmpty() && !this.passCodes.contains(pr.getExitCode())));
287    }
288
289     public String getOutput() {
290         return output;
291     }
292
293     public void setOutput(String output) {
294         this.output = output;
295     }
296
297     private Map<String, String> formTmpFiles(String line){
298
299         Map<String, String> result = new HashMap<>();
300
301         if (!line.contains("$s{tmp")) {
302             return result;
303         }
304
305         int currentIdx = 0;
306         while (currentIdx < line.length()) {
307             int idxS = line.indexOf("$s{tmp:", currentIdx); //check for output stream
308             if (idxS == -1) {
309                 break;
310             }
311
312             int idxE = line.indexOf('}', idxS);
313             String tmpName = line.substring(idxS + 7, idxE);
314             tmpName = tmpName.trim();
315             String[] tmpTkns = tmpName.split(":");
316             String tmpFileName;
317             String paramName;
318             if (tmpTkns.length == 2) {
319                 tmpFileName = tmpTkns[0];
320                 paramName = tmpTkns[1];
321             } else {
322                 tmpFileName = tmpTkns[0];
323                 paramName = null;
324             }
325
326             String tmpFilePath = this.getOutputAttributeFilePath(tmpFileName, true);
327             if (paramName != null) {
328                 //Write the value of input params into file before passing to command
329                 try {
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) {
334                     // NO SONAR
335                 }
336             }
337
338             result.put("tmp:" + tmpFileName, tmpFilePath); //used in output parsing
339             result.put("tmp:" + tmpName, tmpFilePath); //used in line replacement
340
341             currentIdx = idxE + 1;
342         }
343         return result;
344     }
345
346    private ArrayList<String> replaceLineFromOutputResults(String line, String output)
347             throws OnapCommandException {
348
349
350         ArrayList<String> result = new ArrayList<>();
351         if (!line.contains("$o{")) {
352             result.add(line);
353             return result;
354         }
355
356         /**
357          * In case of empty output [] or {}
358          **/
359         if (output.length() <= 2) {
360             return result;
361         }
362
363         int currentIdx = 0;
364
365         // Process  jsonpath macros
366         List<Object> values = new ArrayList<>();
367         StringBuilder processedPattern = new StringBuilder();
368         currentIdx = 0;
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
372             if (idxS == -1) {
373                 idxS = line.indexOf("$e{", currentIdx); //check for error stream
374                 if (idxS == -1) {
375                     processedPattern.append(line.substring(currentIdx));
376                     break;
377                 }
378             }
379             int idxE = line.indexOf('}', idxS);
380             String jsonPath = line.substring(idxS + 3, idxE);
381             jsonPath = jsonPath.trim();
382             Object value = new Object();
383             try {
384                 // Instead of parsing, just assign the json as it is
385                 if (!jsonPath.equals("$"))
386                     value = JsonPath.read(output, jsonPath);
387                 else
388                     value = output;
389             } catch (PathNotFoundException e1) { // NOSONAR
390                 //set to blank for those entries which are missing from the output json
391                 value = "";
392             } catch (Exception e) {
393                 throw new OnapCommandCmdFailure("Invalid json format in command output");
394             }
395
396             if (value instanceof JSONArray) {
397                 JSONArray arr = (JSONArray) value;
398                 if (arr.size() > maxRows) {
399                     maxRows = arr.size();
400                 }
401             }
402             processedPattern.append(line.substring(currentIdx, idxS) + "%s");
403             values.add(value);
404             currentIdx = idxE + 1;
405         }
406
407         if (processedPattern.toString().isEmpty()) {
408             result.add(line);
409             return result;
410         } else {
411             for (int i = 0; i < maxRows; i++) {
412                 currentIdx = 0;
413                 StringBuilder bodyProcessedLine = new StringBuilder();
414                 int positionalIdx = 0; // %s positional idx
415                 while (currentIdx < processedPattern.length()) {
416                     int idxS = processedPattern.indexOf("%s", currentIdx);
417                     if (idxS == -1) {
418                         bodyProcessedLine.append(processedPattern.substring(currentIdx));
419                         break;
420                     }
421
422                     int idxEnd = idxS + 2; // %s
423
424                     try {
425                         Object val = values.get(positionalIdx);
426                         String valStr = String.valueOf(val);
427
428                         if (val instanceof JSONArray) {
429                             JSONArray aJson = (JSONArray) val;
430
431                             if (!aJson.isEmpty()) {
432                                 valStr = aJson.get(i).toString();
433                             } else {
434                                 throw new OnapCommandResultEmpty();
435                             }
436                         }
437
438                         bodyProcessedLine.append(processedPattern.substring(currentIdx, idxS) + valStr);
439                         currentIdx = idxEnd;
440                         positionalIdx++;
441                     } catch (OnapCommandResultEmpty e) {
442                         throw e;
443                     } catch (Exception e) {
444                         throw new OnapCommandResultMapProcessingFailed(line, e);
445                     }
446                 }
447                 result.add(bodyProcessedLine.toString());
448             }
449
450             return result;
451         }
452     }
453
454 public String getError() {
455     return error;
456 }
457
458 public void setError(String error) {
459     this.error = error;
460 }
461
462 public List<Integer> getPassCodes() {
463     return passCodes;
464 }
465
466 public void setPassCodes(List<Integer> passCodes) {
467     this.passCodes = passCodes;
468 }
469
470 }