blob: 1da9545485389dede8162cfb5e4ef6a6999e08bc [file] [log] [blame]
Thomas Vachuskaf9c84362015-04-15 11:20:45 -07001/*
2 * Copyright 2015 Open Networking Laboratory
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 */
16package org.onlab.stc;
17
Thomas Vachuska86439372015-06-05 09:21:32 -070018import org.onlab.stc.Coordinator.Status;
19
Thomas Vachuskaf9c84362015-04-15 11:20:45 -070020import java.io.BufferedReader;
21import java.io.File;
22import java.io.IOException;
23import java.io.InputStream;
24import java.io.InputStreamReader;
25import java.io.PrintWriter;
Thomas Vachuskab51b8bc2015-07-27 08:37:12 -070026import java.util.function.Function;
Thomas Vachuskaf9c84362015-04-15 11:20:45 -070027
Thomas Vachuska4bfccd542015-05-30 00:35:25 -070028import static java.lang.String.format;
Thomas Vachuska86439372015-06-05 09:21:32 -070029import static org.onlab.stc.Coordinator.Status.FAILED;
30import static org.onlab.stc.Coordinator.Status.SUCCEEDED;
Thomas Vachuskaf9c84362015-04-15 11:20:45 -070031import static org.onlab.stc.Coordinator.print;
32
33/**
34 * Manages execution of the specified step or a group.
35 */
36class StepProcessor implements Runnable {
37
Thomas Vachuska86439372015-06-05 09:21:32 -070038 private static final String IGNORE_CODE = "~";
Thomas Vachuskaf9c84362015-04-15 11:20:45 -070039 private static final int FAIL = -1;
40
41 static String launcher = "stc-launcher ";
42
43 private final Step step;
Thomas Vachuskaf9c84362015-04-15 11:20:45 -070044 private final File logDir;
Thomas Vachuskab51b8bc2015-07-27 08:37:12 -070045 private String command;
Thomas Vachuskaf9c84362015-04-15 11:20:45 -070046
47 private Process process;
48 private StepProcessListener delegate;
Thomas Vachuskab51b8bc2015-07-27 08:37:12 -070049 private Function<String, String> substitutor;
Thomas Vachuskaf9c84362015-04-15 11:20:45 -070050
51 /**
52 * Creates a process monitor.
53 *
Thomas Vachuskab51b8bc2015-07-27 08:37:12 -070054 * @param step step or group to be executed
55 * @param logDir directory where step process log should be stored
56 * @param delegate process lifecycle listener
57 * @param substitutor function to substitute var reference in command
Thomas Vachuskaf9c84362015-04-15 11:20:45 -070058 */
Thomas Vachuskab51b8bc2015-07-27 08:37:12 -070059 StepProcessor(Step step, File logDir, StepProcessListener delegate,
60 Function<String, String> substitutor) {
Thomas Vachuskaf9c84362015-04-15 11:20:45 -070061 this.step = step;
Thomas Vachuskaf9c84362015-04-15 11:20:45 -070062 this.logDir = logDir;
63 this.delegate = delegate;
Thomas Vachuskab51b8bc2015-07-27 08:37:12 -070064 this.substitutor = substitutor;
Thomas Vachuskaf9c84362015-04-15 11:20:45 -070065 }
66
67 @Override
68 public void run() {
Thomas Vachuskab51b8bc2015-07-27 08:37:12 -070069 command = substitutor != null ? substitutor.apply(command()) : command();
70 delegate.onStart(step, command);
Thomas Vachuska86439372015-06-05 09:21:32 -070071 int code = execute();
72 boolean ignoreCode = step.env() != null && step.env.equals(IGNORE_CODE);
73 Status status = ignoreCode || code == 0 ? SUCCEEDED : FAILED;
74 delegate.onCompletion(step, status);
Thomas Vachuskaf9c84362015-04-15 11:20:45 -070075 }
76
77 /**
78 * Executes the step process.
79 *
80 * @return exit code
81 */
82 private int execute() {
Thomas Vachuska4bfccd542015-05-30 00:35:25 -070083 try (PrintWriter pw = new PrintWriter(logFile())) {
Thomas Vachuskab51b8bc2015-07-27 08:37:12 -070084 process = Runtime.getRuntime().exec(command);
Thomas Vachuskaf9c84362015-04-15 11:20:45 -070085 processOutput(pw);
86
87 // Wait for the process to complete and get its exit code.
88 if (process.isAlive()) {
89 process.waitFor();
90 }
91 return process.exitValue();
92
93 } catch (IOException e) {
94 print("Unable to run step %s using command %s", step.name(), step.command());
95 } catch (InterruptedException e) {
96 print("Step %s interrupted", step.name());
97 }
98 return FAIL;
99 }
100
101 /**
Thomas Vachuska4bfccd542015-05-30 00:35:25 -0700102 * Returns ready-to-run command for the step.
103 *
104 * @return command to execute
105 */
106 private String command() {
107 return format("%s %s %s %s", launcher,
108 step.env() != null ? step.env() : "-",
109 step.cwd() != null ? step.cwd() : "-",
110 step.command());
111 }
112
113 /**
Thomas Vachuskaf9c84362015-04-15 11:20:45 -0700114 * Captures output of the step process.
115 *
116 * @param pw print writer to send output to
117 * @throws IOException if unable to read output or write logs
118 */
119 private void processOutput(PrintWriter pw) throws IOException {
120 InputStream out = process.getInputStream();
121 BufferedReader br = new BufferedReader(new InputStreamReader(out));
122
123 // Slurp its combined stderr/stdout
124 String line;
125 while ((line = br.readLine()) != null) {
126 pw.println(line);
127 delegate.onOutput(step, line);
128 }
129 }
130
131 /**
Thomas Vachuska4bfccd542015-05-30 00:35:25 -0700132 * Returns the log file for the step output.
Thomas Vachuskaf9c84362015-04-15 11:20:45 -0700133 *
Thomas Vachuskaf9c84362015-04-15 11:20:45 -0700134 * @return log file
135 */
Thomas Vachuska4bfccd542015-05-30 00:35:25 -0700136 private File logFile() {
Thomas Vachuskaf9c84362015-04-15 11:20:45 -0700137 return new File(logDir, step.name() + ".log");
138 }
139
140}