91f45f02fcdab3c7e47c533e8cb5239102eae720
[cli.git] / framework / src / main / java / org / onap / cli / fw / store / OnapCommandExecutionStore.java
1 /*
2  * Copyright 2019 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.store;
18
19 import java.io.File;
20 import java.io.IOException;
21 import java.nio.file.Files;
22 import java.nio.file.Path;
23 import java.nio.file.Paths;
24 import java.text.SimpleDateFormat;
25 import java.util.ArrayList;
26 import java.util.Arrays;
27 import java.util.List;
28 import java.util.Locale;
29 import java.util.Map;
30 import java.util.TimeZone;
31
32 import org.apache.commons.io.FileUtils;
33 import org.onap.cli.fw.conf.OnapCommandConfig;
34 import org.onap.cli.fw.conf.OnapCommandConstants;
35 import org.onap.cli.fw.error.OnapCommandExecutionFailed;
36 import org.onap.cli.fw.error.OnapCommandExecutionNotFound;
37 import org.onap.cli.fw.utils.ProcessRunner;
38 import org.slf4j.Logger;
39 import org.slf4j.LoggerFactory;
40
41 public class OnapCommandExecutionStore {
42     private static Logger log = LoggerFactory.getLogger(OnapCommandExecutionStore.class);
43
44     private static boolean storeReady = false; // NOSONAR
45     private static final String REQUEST_ID = "requestId";
46     private static final String EXECUTION_ID = "executionId";
47     private static final String INPUT = "input";
48     private static final String STDOUT = "stdout";
49     private static final String STDERR = "stderr";
50     private static final String DEBUG = "debug";
51     private static final String IN_PROGRESS = "in-progress";
52     private static final String OUTPUT = "output";
53     private static final String ERROR = "error";
54     private static final String COMPLETED = "completed";
55     private static final String FAILED = "failed";
56     private static final String EXECUTIONID = "execution-id";
57     private static final String REQUESTID = "request-id";
58     private static final String OS_NAME = "os.name";
59     private static final String WINDOWS = "windows";
60
61     private SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS", Locale.US);
62
63     private static final String SEPARATOR = "__";
64
65     private enum SearchMode {
66         FIND,
67         FILE //for developer mode
68
69
70     }
71
72     private static SearchMode searchMode = SearchMode.FILE;
73     static {
74         String mode = OnapCommandConfig.getPropertyValue(OnapCommandConstants.OPEN_CLI_EXECUTION_SEARCH_MODE);
75         if (mode.equalsIgnoreCase(SearchMode.FIND.name()))
76             searchMode = SearchMode.FIND;
77     }
78
79     public static class ExecutionStoreContext {
80         private String requestId;
81         private String executionId;
82         private String profile;
83         private String storePath;
84         public String getExecutionId() {
85             return executionId;
86         }
87         public ExecutionStoreContext setExecutionId(String executionId) {
88             this.executionId = executionId;
89             return this;
90         }
91         public String getStorePath() {
92             return storePath;
93         }
94         public ExecutionStoreContext setStorePath(String storePath) {
95             this.storePath = storePath;
96             return this;
97         }
98         public String getRequestId() {
99             return requestId;
100         }
101         public ExecutionStoreContext setRequestId(String requestId) {
102             this.requestId = requestId;
103              return this;
104         }
105         public String getProfile() {
106             return profile;
107         }
108         public void setProfile(String profile) {
109             this.profile = profile;
110         }
111     }
112
113     public static class Execution {
114         private String id;
115         private String requestId;
116         private String status;
117         private String startTime;
118         private String endTime;
119         private String input;
120         private String output;
121         private String profile;
122         private String command;
123         private String product;
124         private String service;
125
126         public String getInput() {
127             return input;
128         }
129         public void setInput(String input) {
130             this.input = input;
131         }
132         public String getOutput() {
133             return output;
134         }
135         public void setOutput(String output) {
136             this.output = output;
137         }
138         public String getProfile() {
139             return profile;
140         }
141         public void setProfile(String profile) {
142             this.profile = profile;
143         }
144         public String getCommand() {
145             return command;
146         }
147         public void setCommand(String command) {
148             this.command = command;
149         }
150         public String getProduct() {
151             return product;
152         }
153         public void setProduct(String product) {
154             this.product = product;
155         }
156         public String getService() {
157             return service;
158         }
159         public void setService(String service) {
160             this.service = service;
161         }
162         public String getId() {
163             return id;
164         }
165         public void setId(String id) {
166             this.id = id;
167         }
168         public String getEndTime() {
169             return endTime;
170         }
171         public void setEndTime(String endTime) {
172             this.endTime = endTime;
173         }
174         public void setStartTime(String timeOfExecution) {
175             this.startTime = timeOfExecution;
176         }
177         public String getStartTime() {
178             return startTime;
179         }
180         public String getStatus() {
181             return status;
182         }
183         public void setStatus(String status) {
184             this.status = status;
185         }
186         public String getRequestId() {
187             return requestId;
188         }
189         public void setRequestId(String requestId) {
190             this.requestId = requestId;
191         }
192     }
193
194     static {
195         try {
196             FileUtils.forceMkdir(new File(getBasePath()));
197             storeReady = true;
198         } catch (IOException e) {
199             log.error("Failed to create the data store results");
200         }
201     }
202
203     private static OnapCommandExecutionStore store = null;
204
205     private OnapCommandExecutionStore() {
206         this.dateFormatter.setTimeZone(TimeZone.getTimeZone("UTC"));
207     }
208
209     public static OnapCommandExecutionStore getStore() {
210         if (store == null) {
211             store = new OnapCommandExecutionStore();
212         }
213
214         return store;
215     }
216
217     private static String getBasePath() {
218         return OnapCommandConfig.getPropertyValue(OnapCommandConstants.OPEN_CLI_DATA_DIR) +
219                 File.separator + "executions";
220     }
221
222     public ExecutionStoreContext storeExectutionStart(
223             String requestId, String product, String service, String cmd, String profile, String input) {
224
225         ExecutionStoreContext context = new ExecutionStoreContext();
226         context.setRequestId(requestId);
227
228         String executionId = requestId + "-" + System.currentTimeMillis();
229         context.setExecutionId(executionId);
230
231         String storePath = getBasePath() + File.separator + executionId + SEPARATOR + product +
232                 SEPARATOR + service +
233                 SEPARATOR + cmd +
234                 SEPARATOR + (profile != null ? profile : "" );
235
236         try {
237             File dir = new File(storePath);
238             FileUtils.forceMkdir(dir);
239             context.setStorePath(dir.getAbsolutePath());
240
241             if (product != null)
242                 FileUtils.writeStringToFile(new File(context.getStorePath() + File.separator + OnapCommandConstants.INFO_PRODUCT), product);
243             if (service != null)
244                 FileUtils.writeStringToFile(new File(context.getStorePath() + File.separator + OnapCommandConstants.INFO_SERVICE), service);
245             if (cmd != null)
246                 FileUtils.writeStringToFile(new File(context.getStorePath() + File.separator + OnapCommandConstants.RPC_CMD), cmd);
247
248             FileUtils.writeStringToFile(new File(context.getStorePath() + File.separator + REQUEST_ID), requestId);
249
250             FileUtils.writeStringToFile(new File(context.getStorePath() + File.separator + EXECUTION_ID), executionId);
251
252             if (input != null)
253                 FileUtils.writeStringToFile(new File(context.getStorePath() + File.separator + INPUT), input);
254             if (profile != null) {
255                 context.setProfile(profile);
256                 FileUtils.writeStringToFile(new File(context.getStorePath() + File.separator + OnapCommandConstants.RPC_PROFILE), profile);
257             }
258
259             FileUtils.touch(new File(context.getStorePath() + File.separator + STDOUT));
260             FileUtils.touch(new File(context.getStorePath() + File.separator + STDERR));
261             FileUtils.touch(new File(context.getStorePath() + File.separator + DEBUG));
262
263             FileUtils.touch(new File(context.getStorePath() + File.separator + IN_PROGRESS));
264         } catch (IOException e) {
265             log.error("Failed to store the execution start details {}", storePath);
266         }
267
268         return context;
269     }
270
271     public void storeExectutionEnd(
272             ExecutionStoreContext context,
273             String output, String error, String debug, boolean passed) {
274
275         try {
276             if (output != null)
277                 FileUtils.writeStringToFile(new File(context.getStorePath() + File.separator + OUTPUT), output);
278             if (error != null)
279                 FileUtils.writeStringToFile(new File(context.getStorePath() + File.separator + ERROR), error);
280             if (debug != null)
281                 FileUtils.writeStringToFile(new File(context.getStorePath() + File.separator + DEBUG), debug);
282             if (passed)
283                 FileUtils.touch(new File(context.getStorePath() + File.separator + COMPLETED));
284             else
285                 FileUtils.touch(new File(context.getStorePath() + File.separator + FAILED));
286             Path path= Paths.get(context.getStorePath() + File.separator + IN_PROGRESS);
287             deleteFile(context, path);
288         } catch (IOException e) {
289             log.error("Failed to store the execution end details {}", context.storePath);
290         }
291     }
292
293     private void deleteFile(ExecutionStoreContext context, Path path){
294         try {
295             Files.delete(path);
296         } catch (IOException e) {
297             String contextPath = context.getStorePath() + File.separator + IN_PROGRESS;
298             log.error("Failed to delete {}", contextPath);
299         }
300     }
301
302     public void storeExectutionProgress(
303             ExecutionStoreContext context,
304             String output, String error, String debug) {
305
306         try {
307             if (output != null)
308                 FileUtils.writeStringToFile(new File(context.getStorePath() + File.separator + OUTPUT), output);
309             if (error != null)
310                 FileUtils.writeStringToFile(new File(context.getStorePath() + File.separator + ERROR), error);
311             if (debug != null)
312                 FileUtils.writeStringToFile(new File(context.getStorePath() + File.separator + DEBUG), debug);
313         } catch (IOException e) {
314             log.error("Failed to store the execution end details {}", context.storePath);
315         }
316     }
317
318     public void storeExectutionDebug(
319             ExecutionStoreContext context,
320             String debug) {
321
322         try {
323             if (debug != null) {
324                 FileUtils.writeStringToFile(new File(context.getStorePath() + File.separator + DEBUG), debug);
325             }
326         } catch (IOException e) {
327             log.error("Failed to store the execution debug details {}", context.storePath);
328         }
329     }
330
331     public void storeExectutionOutput(
332             ExecutionStoreContext context,
333             String output) {
334
335         try {
336             if (output != null) {
337                 FileUtils.writeStringToFile(new File(context.getStorePath() + File.separator + OUTPUT), output);
338             }
339         } catch (IOException e) {
340             log.error("Failed to store the execution output details {}", context.storePath);
341         }
342     }
343
344     public List <String> listExecutionsWindows(Map<String, String> search, List <String> dirs){
345         for (File f: new File(getBasePath()).listFiles()) {
346             if(search.containsKey(EXECUTIONID)) {
347                 if (f.getName().startsWith(search.get(EXECUTIONID)))
348                     dirs.add(f.getAbsolutePath());
349
350                 continue;
351             }
352
353             if(search.containsKey(REQUESTID)) {
354                 if (f.getName().startsWith(search.get(REQUESTID)))
355                     dirs.add(f.getAbsolutePath());
356
357             }
358
359             else
360                 dirs.add(f.getAbsolutePath());
361         }
362         return dirs;
363     }
364
365     public List <String> searchAndListExecutions(Map<String, String> search, List <String> dirs) throws OnapCommandExecutionFailed, IOException, InterruptedException {
366         StringBuilder searchString = new StringBuilder("find " + new File(getBasePath()).getAbsolutePath() + " -type d ");
367
368         String startTime = search.get("startTime");
369         if (startTime != null) {
370             searchString.append(" -newermt " + startTime);
371         }
372
373         String endTime = search.get("endTime");
374         if (endTime != null) {
375             searchString.append(" ! -newermt " + endTime);
376         }
377
378         searchString.append(" -name \"");
379
380         if(search.containsKey(EXECUTIONID)) {
381             searchString.append(search.get(EXECUTIONID));
382         } else if(search.containsKey(REQUESTID)) {
383             searchString.append(search.get(REQUESTID) + "*");
384         } else {
385             searchString.append("*");
386         }
387
388         for (String term: Arrays.asList("product", "service", "command", "profile")) {
389             searchString.append("__");
390             if (search.get(term) != null && !search.get(term).isEmpty()) {
391                 searchString.append(search.get(term));
392             } else {
393                 searchString.append("*");
394             }
395         }
396         if (!searchString.toString().endsWith("*"))
397             searchString.append("*");
398
399         searchString.append("\"");
400
401         ProcessRunner pr = new ProcessRunner(new String [] {searchString.toString()}, null, ".");
402         pr.setTimeout(10000);
403         pr.overrideToUnix();
404         pr.run();
405         if (pr.getExitCode() != 0) {
406             throw new OnapCommandExecutionFailed("System failed to search the executions with error " + pr.getError());
407         }
408
409         if (!pr.getOutput().trim().isEmpty())
410             dirs = Arrays.asList(pr.getOutput().split("\\r?\\n"));
411
412         return dirs;
413     }
414
415     public List<OnapCommandExecutionStore.Execution> listExecutions(Map<String, String> search) throws OnapCommandExecutionFailed {
416         List <OnapCommandExecutionStore.Execution> list = new ArrayList<>();
417
418         try {
419             List <String> dirs = new ArrayList<>();
420             if (System.getProperty(OS_NAME).toLowerCase().startsWith(WINDOWS) || searchMode.equals(SearchMode.FILE)) {
421                 dirs = listExecutionsWindows(search, dirs);
422             } else {
423                 //find results -type d -newermt '2019-02-11 10:00:00' ! -newermt '2019-02-11 15:10:00' -name "*__*__profile-list*"
424                 //find 'results' -type d -newermt '2019-02-11T10:00:00.000' ! -newermt '2019-02-11T15:10:00.000' -name "*__*__profile*"
425                 dirs = searchAndListExecutions(search, dirs);
426             }
427
428             for (String dir: dirs) {
429                 list.add(this.makeExecution(dir));
430             }
431         } catch (Exception e) {// NOSONAR
432             throw new OnapCommandExecutionFailed(e, "Failed to search the executions");
433         }
434
435         return list;
436     }
437
438     private Execution makeExecution(String executionStorePath) throws IOException {
439         OnapCommandExecutionStore.Execution exectuion = new OnapCommandExecutionStore.Execution();
440         if (new File(executionStorePath + File.separator + REQUEST_ID).exists())
441             exectuion.setRequestId(FileUtils.readFileToString(new File(executionStorePath + File.separator + REQUEST_ID)));
442         if (new File(executionStorePath + File.separator + EXECUTION_ID).exists())
443             exectuion.setId(FileUtils.readFileToString(new File(executionStorePath + File.separator + EXECUTION_ID)));
444         exectuion.setProduct(FileUtils.readFileToString(new File(executionStorePath + File.separator + OnapCommandConstants.INFO_PRODUCT)));
445         exectuion.setService(FileUtils.readFileToString(new File(executionStorePath + File.separator + OnapCommandConstants.INFO_SERVICE)));
446         exectuion.setCommand(FileUtils.readFileToString(new File(executionStorePath + File.separator + OnapCommandConstants.RPC_CMD)));
447         if (new File(executionStorePath + File.separator + OnapCommandConstants.RPC_PROFILE).exists())
448             exectuion.setProfile(FileUtils.readFileToString(new File(executionStorePath + File.separator + OnapCommandConstants.RPC_PROFILE)));
449
450         exectuion.setInput(FileUtils.readFileToString(new File(executionStorePath + File.separator + INPUT)));
451         exectuion.setStartTime(dateFormatter.format(new File(executionStorePath + File.separator + INPUT).lastModified()));
452
453         if (new File(executionStorePath + File.separator + IN_PROGRESS).exists()) {
454             exectuion.setStatus(IN_PROGRESS);
455         } else if (new File(executionStorePath + File.separator + COMPLETED).exists()) {
456             exectuion.setStatus(COMPLETED);
457             if (new File(executionStorePath + File.separator + OUTPUT).exists()) {
458                 exectuion.setOutput(FileUtils.readFileToString(new File(executionStorePath + File.separator + OUTPUT)));
459                 exectuion.setEndTime(dateFormatter.format(new File(executionStorePath + File.separator + OUTPUT).lastModified()));
460             }
461         } else if (new File(executionStorePath + File.separator + FAILED).exists()) {
462             exectuion.setStatus(FAILED);
463             if (new File(executionStorePath + File.separator + ERROR).exists()) {
464                 exectuion.setOutput(FileUtils.readFileToString(new File(executionStorePath + File.separator + ERROR)));
465                 exectuion.setEndTime(dateFormatter.format(new File(executionStorePath + File.separator + ERROR).lastModified()));
466             }
467         }
468
469         return exectuion;
470     }
471
472     private File getExecutionDir(String executionId) throws OnapCommandExecutionNotFound {
473         File []f =  new File(getBasePath()).listFiles((dir, name) -> {
474             return name.startsWith(executionId);
475         });
476
477         if (f.length == 0) {
478             throw new OnapCommandExecutionNotFound(executionId);
479         }
480
481         return f[0];
482     }
483
484     public String showExecutionOut(String executionId) throws OnapCommandExecutionNotFound {
485         try {
486             return FileUtils.readFileToString(new File (this.getExecutionDir(executionId).getAbsolutePath() + File.separator + STDOUT));
487         } catch (IOException e) {
488             return "";
489         }
490     }
491
492     public String showExecutionErr(String executionId) throws OnapCommandExecutionNotFound {
493         try {
494             return FileUtils.readFileToString(new File (this.getExecutionDir(executionId).getAbsolutePath() + File.separator + STDERR));
495         } catch (IOException e) {
496             return "";
497         }
498     }
499
500     public String showExecutionDebug(String executionId) throws OnapCommandExecutionNotFound {
501         try {
502             return FileUtils.readFileToString(new File (this.getExecutionDir(executionId).getAbsolutePath() + File.separator + DEBUG));
503         } catch (IOException e) {
504             return "";
505         }
506     }
507     public Execution getExecution(String executionId) throws OnapCommandExecutionNotFound, OnapCommandExecutionFailed {
508         try {
509             return this.makeExecution(this.getExecutionDir(executionId).getAbsolutePath());
510         } catch (IOException e) {
511             throw new OnapCommandExecutionFailed(e, "Failed to retrieve the execution");
512         }
513     }
514 }