blob: 3ef2ae58294dcb20dbe459f13cb1f08cc6c47746 [file] [log] [blame]
Thomas Vachuskaf9c84362015-04-15 11:20:45 -07001/*
Brian O'Connor5ab426f2016-04-09 01:19:45 -07002 * Copyright 2015-present Open Networking Laboratory
Thomas Vachuskaf9c84362015-04-15 11:20:45 -07003 *
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;
26
Thomas Vachuska4bfccd542015-05-30 00:35:25 -070027import static java.lang.String.format;
Thomas Vachuska86439372015-06-05 09:21:32 -070028import static org.onlab.stc.Coordinator.Status.FAILED;
29import static org.onlab.stc.Coordinator.Status.SUCCEEDED;
Thomas Vachuskaf9c84362015-04-15 11:20:45 -070030import static org.onlab.stc.Coordinator.print;
31
32/**
33 * Manages execution of the specified step or a group.
34 */
35class StepProcessor implements Runnable {
36
Thomas Vachuska86439372015-06-05 09:21:32 -070037 private static final String IGNORE_CODE = "~";
Thomas Vachuska14eaba82015-08-31 16:13:07 -070038 private static final String NEGATE_CODE = "!";
39
Thomas Vachuskaf9c84362015-04-15 11:20:45 -070040 private static final int FAIL = -1;
Thomas Vachuska664f29e2015-12-08 12:58:16 -080041 private static final int SECONDS = 1_000;
Thomas Vachuskaf9c84362015-04-15 11:20:45 -070042
43 static String launcher = "stc-launcher ";
44
45 private final Step step;
Thomas Vachuskaf9c84362015-04-15 11:20:45 -070046 private final File logDir;
Thomas Vachuskab51b8bc2015-07-27 08:37:12 -070047 private String command;
Thomas Vachuskaf9c84362015-04-15 11:20:45 -070048
49 private Process process;
50 private StepProcessListener delegate;
51
52 /**
53 * Creates a process monitor.
54 *
Thomas Vachuskae2de8ee2015-08-25 16:14:28 -070055 * @param step step or group to be executed
56 * @param logDir directory where step process log should be stored
57 * @param delegate process lifecycle listener
58 * @param command actual command to execute
Thomas Vachuskaf9c84362015-04-15 11:20:45 -070059 */
Thomas Vachuskab51b8bc2015-07-27 08:37:12 -070060 StepProcessor(Step step, File logDir, StepProcessListener delegate,
Thomas Vachuskae2de8ee2015-08-25 16:14:28 -070061 String command) {
Thomas Vachuskaf9c84362015-04-15 11:20:45 -070062 this.step = step;
Thomas Vachuskaf9c84362015-04-15 11:20:45 -070063 this.logDir = logDir;
64 this.delegate = delegate;
Thomas Vachuskae2de8ee2015-08-25 16:14:28 -070065 this.command = command;
Thomas Vachuskaf9c84362015-04-15 11:20:45 -070066 }
67
68 @Override
69 public void run() {
Thomas Vachuskab51b8bc2015-07-27 08:37:12 -070070 delegate.onStart(step, command);
Thomas Vachuska664f29e2015-12-08 12:58:16 -080071 delayIfNeeded();
Thomas Vachuska86439372015-06-05 09:21:32 -070072 int code = execute();
73 boolean ignoreCode = step.env() != null && step.env.equals(IGNORE_CODE);
Thomas Vachuska14eaba82015-08-31 16:13:07 -070074 boolean negateCode = step.env() != null && step.env.equals(NEGATE_CODE);
75 Status status = ignoreCode || code == 0 && !negateCode || code != 0 && negateCode ?
76 SUCCEEDED : FAILED;
Thomas Vachuska86439372015-06-05 09:21:32 -070077 delegate.onCompletion(step, status);
Thomas Vachuskaf9c84362015-04-15 11:20:45 -070078 }
79
80 /**
Thomas Vachuska664f29e2015-12-08 12:58:16 -080081 * Pauses if the step requires it.
82 */
83 private void delayIfNeeded() {
84 if (step.delay() > 0) {
85 try {
86 Thread.sleep(step.delay() * SECONDS);
87 } catch (InterruptedException e) {
88 throw new RuntimeException("Interrupted", e);
89 }
90 }
91 }
92
93 /**
Thomas Vachuskaf9c84362015-04-15 11:20:45 -070094 * Executes the step process.
95 *
96 * @return exit code
97 */
98 private int execute() {
Thomas Vachuska4bfccd542015-05-30 00:35:25 -070099 try (PrintWriter pw = new PrintWriter(logFile())) {
Thomas Vachuskae2de8ee2015-08-25 16:14:28 -0700100 process = Runtime.getRuntime().exec(command());
Thomas Vachuskaf9c84362015-04-15 11:20:45 -0700101 processOutput(pw);
102
103 // Wait for the process to complete and get its exit code.
104 if (process.isAlive()) {
105 process.waitFor();
106 }
107 return process.exitValue();
108
109 } catch (IOException e) {
110 print("Unable to run step %s using command %s", step.name(), step.command());
111 } catch (InterruptedException e) {
112 print("Step %s interrupted", step.name());
113 }
114 return FAIL;
115 }
116
117 /**
Thomas Vachuska4bfccd542015-05-30 00:35:25 -0700118 * Returns ready-to-run command for the step.
119 *
120 * @return command to execute
121 */
122 private String command() {
123 return format("%s %s %s %s", launcher,
124 step.env() != null ? step.env() : "-",
125 step.cwd() != null ? step.cwd() : "-",
Thomas Vachuskae2de8ee2015-08-25 16:14:28 -0700126 command);
Thomas Vachuska4bfccd542015-05-30 00:35:25 -0700127 }
128
129 /**
Thomas Vachuskaf9c84362015-04-15 11:20:45 -0700130 * Captures output of the step process.
131 *
132 * @param pw print writer to send output to
133 * @throws IOException if unable to read output or write logs
134 */
135 private void processOutput(PrintWriter pw) throws IOException {
136 InputStream out = process.getInputStream();
137 BufferedReader br = new BufferedReader(new InputStreamReader(out));
138
139 // Slurp its combined stderr/stdout
140 String line;
141 while ((line = br.readLine()) != null) {
142 pw.println(line);
143 delegate.onOutput(step, line);
144 }
145 }
146
147 /**
Thomas Vachuska4bfccd542015-05-30 00:35:25 -0700148 * Returns the log file for the step output.
Thomas Vachuskaf9c84362015-04-15 11:20:45 -0700149 *
Thomas Vachuskaf9c84362015-04-15 11:20:45 -0700150 * @return log file
151 */
Thomas Vachuska4bfccd542015-05-30 00:35:25 -0700152 private File logFile() {
Thomas Vachuskaf9c84362015-04-15 11:20:45 -0700153 return new File(logDir, step.name() + ".log");
154 }
155
156}