Thomas Vachuska
Committed by Gerrit Code Review

Added ability for commands to post properties to be used as params of other commands.

Starting to add monitor GUI.

Change-Id: I9fcf1568d0de27dfd1c19e875f8646fd731a1dfa
...@@ -3,6 +3,8 @@ ...@@ -3,6 +3,8 @@
3 # System Test Coordinator process launcher 3 # System Test Coordinator process launcher
4 #------------------------------------------------------------------------------- 4 #-------------------------------------------------------------------------------
5 5
6 +#sleep 5 && exit 0;
7 +
6 env=$1 && shift 8 env=$1 && shift
7 cwd=$1 && shift 9 cwd=$1 && shift
8 10
......
1 +<!--
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 + -->
16 +<scenario name="example" description="Example">
17 + <step name="One" exec="echo @stc foo=bar"/>
18 + <step name="Two" requires="One" exec="echo ${foo}"/>
19 +</scenario>
...@@ -28,7 +28,7 @@ ...@@ -28,7 +28,7 @@
28 <step name="Check-Summary-For-Hosts" requires="~Ping-All-And-Verify" 28 <step name="Check-Summary-For-Hosts" requires="~Ping-All-And-Verify"
29 exec="onos-check-summary ${OC1} [0-9]* 25 140 25"/> 29 exec="onos-check-summary ${OC1} [0-9]* 25 140 25"/>
30 30
31 - <step name="Config-Topo" requires="Check-Summary-For-Hosts" 31 + <step name="Config-Topo" requires="~Check-Summary-For-Hosts"
32 exec="onos-topo-cfg ${OC1} ${ONOS_ROOT}/tools/test/topos/attmpls.json"/> 32 exec="onos-topo-cfg ${OC1} ${ONOS_ROOT}/tools/test/topos/attmpls.json"/>
33 </group> 33 </group>
34 </scenario> 34 </scenario>
...\ No newline at end of file ...\ No newline at end of file
......
...@@ -53,6 +53,19 @@ ...@@ -53,6 +53,19 @@
53 </dependency> 53 </dependency>
54 54
55 <dependency> 55 <dependency>
56 + <groupId>com.fasterxml.jackson.core</groupId>
57 + <artifactId>jackson-databind</artifactId>
58 + <version>2.4.2</version>
59 + <scope>compile</scope>
60 + </dependency>
61 + <dependency>
62 + <groupId>com.fasterxml.jackson.core</groupId>
63 + <artifactId>jackson-annotations</artifactId>
64 + <version>2.4.2</version>
65 + <scope>compile</scope>
66 + </dependency>
67 +
68 + <dependency>
56 <groupId>org.eclipse.jetty</groupId> 69 <groupId>org.eclipse.jetty</groupId>
57 <artifactId>jetty-server</artifactId> 70 <artifactId>jetty-server</artifactId>
58 <version>8.1.17.v20150415</version> 71 <version>8.1.17.v20150415</version>
......
...@@ -61,8 +61,8 @@ public class Compiler { ...@@ -61,8 +61,8 @@ public class Compiler {
61 private static final String FILE = "[@file]"; 61 private static final String FILE = "[@file]";
62 private static final String NAMESPACE = "[@namespace]"; 62 private static final String NAMESPACE = "[@namespace]";
63 63
64 - private static final String PROP_START = "${"; 64 + static final String PROP_START = "${";
65 - private static final String PROP_END = "}"; 65 + static final String PROP_END = "}";
66 private static final String HASH = "#"; 66 private static final String HASH = "#";
67 67
68 private final Scenario scenario; 68 private final Scenario scenario;
...@@ -230,7 +230,7 @@ public class Compiler { ...@@ -230,7 +230,7 @@ public class Compiler {
230 private void processStep(HierarchicalConfiguration cfg, 230 private void processStep(HierarchicalConfiguration cfg,
231 String namespace, Group parentGroup) { 231 String namespace, Group parentGroup) {
232 String name = expand(prefix(cfg.getString(NAME), namespace)); 232 String name = expand(prefix(cfg.getString(NAME), namespace));
233 - String command = expand(cfg.getString(COMMAND, parentGroup != null ? parentGroup.command() : null)); 233 + String command = expand(cfg.getString(COMMAND, parentGroup != null ? parentGroup.command() : null), true);
234 String env = expand(cfg.getString(ENV, parentGroup != null ? parentGroup.env() : null)); 234 String env = expand(cfg.getString(ENV, parentGroup != null ? parentGroup.env() : null));
235 String cwd = expand(cfg.getString(CWD, parentGroup != null ? parentGroup.cwd() : null)); 235 String cwd = expand(cfg.getString(CWD, parentGroup != null ? parentGroup.cwd() : null));
236 236
...@@ -249,7 +249,7 @@ public class Compiler { ...@@ -249,7 +249,7 @@ public class Compiler {
249 private void processGroup(HierarchicalConfiguration cfg, 249 private void processGroup(HierarchicalConfiguration cfg,
250 String namespace, Group parentGroup) { 250 String namespace, Group parentGroup) {
251 String name = expand(prefix(cfg.getString(NAME), namespace)); 251 String name = expand(prefix(cfg.getString(NAME), namespace));
252 - String command = expand(cfg.getString(COMMAND, parentGroup != null ? parentGroup.command() : null)); 252 + String command = expand(cfg.getString(COMMAND, parentGroup != null ? parentGroup.command() : null), true);
253 String env = expand(cfg.getString(ENV, parentGroup != null ? parentGroup.env() : null)); 253 String env = expand(cfg.getString(ENV, parentGroup != null ? parentGroup.env() : null));
254 String cwd = expand(cfg.getString(CWD, parentGroup != null ? parentGroup.cwd() : null)); 254 String cwd = expand(cfg.getString(CWD, parentGroup != null ? parentGroup.cwd() : null));
255 255
...@@ -388,13 +388,14 @@ public class Compiler { ...@@ -388,13 +388,14 @@ public class Compiler {
388 } 388 }
389 389
390 /** 390 /**
391 - * Expands any environment variables in the specified 391 + * Expands any environment variables in the specified string. These are
392 - * string. These are specified as ${property} tokens. 392 + * specified as ${property} tokens.
393 * 393 *
394 * @param string string to be processed 394 * @param string string to be processed
395 + * @param keepTokens true if the original unresolved tokens should be kept
395 * @return original string with expanded substitutions 396 * @return original string with expanded substitutions
396 */ 397 */
397 - private String expand(String string) { 398 + private String expand(String string, boolean... keepTokens) {
398 if (string == null) { 399 if (string == null) {
399 return null; 400 return null;
400 } 401 }
...@@ -421,7 +422,11 @@ public class Compiler { ...@@ -421,7 +422,11 @@ public class Compiler {
421 value = System.getenv(prop); 422 value = System.getenv(prop);
422 } 423 }
423 } 424 }
425 + if (value == null && keepTokens.length == 1 && keepTokens[0]) {
426 + sb.append("${").append(prop).append("}");
427 + } else {
424 sb.append(value != null ? value : ""); 428 sb.append(value != null ? value : "");
429 + }
425 last = end + 1; 430 last = end + 1;
426 } 431 }
427 sb.append(pString.substring(last)); 432 sb.append(pString.substring(last));
......
...@@ -16,16 +16,24 @@ ...@@ -16,16 +16,24 @@
16 package org.onlab.stc; 16 package org.onlab.stc;
17 17
18 import com.google.common.collect.ImmutableList; 18 import com.google.common.collect.ImmutableList;
19 +import com.google.common.collect.Maps;
19 import com.google.common.collect.Sets; 20 import com.google.common.collect.Sets;
20 21
21 import java.io.File; 22 import java.io.File;
22 import java.util.List; 23 import java.util.List;
24 +import java.util.Map;
23 import java.util.Set; 25 import java.util.Set;
24 import java.util.concurrent.CountDownLatch; 26 import java.util.concurrent.CountDownLatch;
25 import java.util.concurrent.ExecutorService; 27 import java.util.concurrent.ExecutorService;
28 +import java.util.function.Function;
29 +import java.util.regex.Matcher;
30 +import java.util.regex.Pattern;
26 31
32 +import static com.google.common.base.Preconditions.checkArgument;
27 import static com.google.common.base.Preconditions.checkNotNull; 33 import static com.google.common.base.Preconditions.checkNotNull;
28 import static java.util.concurrent.Executors.newFixedThreadPool; 34 import static java.util.concurrent.Executors.newFixedThreadPool;
35 +import static org.onlab.stc.Compiler.PROP_END;
36 +import static org.onlab.stc.Compiler.PROP_START;
29 import static org.onlab.stc.Coordinator.Directive.*; 37 import static org.onlab.stc.Coordinator.Directive.*;
30 import static org.onlab.stc.Coordinator.Status.*; 38 import static org.onlab.stc.Coordinator.Status.*;
31 39
...@@ -44,6 +52,10 @@ public class Coordinator { ...@@ -44,6 +52,10 @@ public class Coordinator {
44 private final CountDownLatch latch; 52 private final CountDownLatch latch;
45 private final ScenarioStore store; 53 private final ScenarioStore store;
46 54
55 + private static final Pattern PROP_ERE = Pattern.compile("^@stc ([a-zA-Z0-9_.]+)=(.*$)");
56 + private final Map<String, String> properties = Maps.newConcurrentMap();
57 + private final Function<String, String> substitutor = this::substitute;
58 +
47 private final Set<StepProcessListener> listeners = Sets.newConcurrentHashSet(); 59 private final Set<StepProcessListener> listeners = Sets.newConcurrentHashSet();
48 private File logDir; 60 private File logDir;
49 61
...@@ -208,10 +220,11 @@ public class Coordinator { ...@@ -208,10 +220,11 @@ public class Coordinator {
208 store.markStarted(step); 220 store.markStarted(step);
209 if (step instanceof Group) { 221 if (step instanceof Group) {
210 Group group = (Group) step; 222 Group group = (Group) step;
211 - delegate.onStart(group); 223 + delegate.onStart(group, null);
212 executeRoots(group); 224 executeRoots(group);
213 } else { 225 } else {
214 - executor.execute(new StepProcessor(step, logDir, delegate)); 226 + executor.execute(new StepProcessor(step, logDir, delegate,
227 + substitutor));
215 } 228 }
216 } else if (directive == SKIP) { 229 } else if (directive == SKIP) {
217 if (step instanceof Group) { 230 if (step instanceof Group) {
...@@ -278,6 +291,43 @@ public class Coordinator { ...@@ -278,6 +291,43 @@ public class Coordinator {
278 } 291 }
279 292
280 /** 293 /**
294 + * Expands the var references with values from the properties map.
295 + *
296 + * @param string string to perform substitutions on
297 + */
298 + private String substitute(String string) {
299 + StringBuilder sb = new StringBuilder();
300 + int start, end, last = 0;
301 + while ((start = string.indexOf(PROP_START, last)) >= 0) {
302 + end = string.indexOf(PROP_END, start + PROP_START.length());
303 + checkArgument(end > start, "Malformed property in %s", string);
304 + sb.append(string.substring(last, start));
305 + String prop = string.substring(start + PROP_START.length(), end);
306 + String value = properties.get(prop);
307 + sb.append(value != null ? value : "");
308 + last = end + 1;
309 + }
310 + sb.append(string.substring(last));
311 + return sb.toString().replace('\n', ' ').replace('\r', ' ');
312 + }
313 +
314 + /**
315 + * Scrapes the line of output for any variables to be captured and posted
316 + * in the properties for later use.
317 + *
318 + * @param line line of output to scrape for property exports
319 + */
320 + private void scrapeForVariables(String line) {
321 + Matcher matcher = PROP_ERE.matcher(line);
322 + if (matcher.matches()) {
323 + String prop = matcher.group(1);
324 + String value = matcher.group(2);
325 + properties.put(prop, value);
326 + }
327 + }
328 +
329 +
330 + /**
281 * Prints formatted output. 331 * Prints formatted output.
282 * 332 *
283 * @param format printf format string 333 * @param format printf format string
...@@ -291,10 +341,9 @@ public class Coordinator { ...@@ -291,10 +341,9 @@ public class Coordinator {
291 * Internal delegate to monitor the process execution. 341 * Internal delegate to monitor the process execution.
292 */ 342 */
293 private class Delegate implements StepProcessListener { 343 private class Delegate implements StepProcessListener {
294 -
295 @Override 344 @Override
296 - public void onStart(Step step) { 345 + public void onStart(Step step, String command) {
297 - listeners.forEach(listener -> listener.onStart(step)); 346 + listeners.forEach(listener -> listener.onStart(step, command));
298 } 347 }
299 348
300 @Override 349 @Override
...@@ -307,9 +356,9 @@ public class Coordinator { ...@@ -307,9 +356,9 @@ public class Coordinator {
307 356
308 @Override 357 @Override
309 public void onOutput(Step step, String line) { 358 public void onOutput(Step step, String line) {
359 + scrapeForVariables(line);
310 listeners.forEach(listener -> listener.onOutput(step, line)); 360 listeners.forEach(listener -> listener.onOutput(step, line));
311 } 361 }
312 -
313 } 362 }
314 363
315 } 364 }
......
...@@ -54,6 +54,7 @@ public final class Main { ...@@ -54,6 +54,7 @@ public final class Main {
54 private String runToPatterns = ""; 54 private String runToPatterns = "";
55 55
56 private Coordinator coordinator; 56 private Coordinator coordinator;
57 + private Monitor monitor;
57 private Listener delegate = new Listener(); 58 private Listener delegate = new Listener();
58 59
59 private static boolean useColor = Objects.equals("true", System.getenv("stcColor")); 60 private static boolean useColor = Objects.equals("true", System.getenv("stcColor"));
...@@ -105,13 +106,16 @@ public final class Main { ...@@ -105,13 +106,16 @@ public final class Main {
105 Compiler compiler = new Compiler(scenario); 106 Compiler compiler = new Compiler(scenario);
106 compiler.compile(); 107 compiler.compile();
107 108
108 - // Execute process flow 109 + // Setup the process flow coordinator
109 coordinator = new Coordinator(scenario, compiler.processFlow(), 110 coordinator = new Coordinator(scenario, compiler.processFlow(),
110 compiler.logDir()); 111 compiler.logDir());
111 coordinator.addListener(delegate); 112 coordinator.addListener(delegate);
112 113
113 - startMonitorServer(); 114 + // Prepare the GUI monitor
115 + monitor = new Monitor(coordinator, compiler);
116 + startMonitorServer(monitor);
114 117
118 + // Execute process flow
115 processCommand(); 119 processCommand();
116 120
117 } catch (FileNotFoundException e) { 121 } catch (FileNotFoundException e) {
...@@ -120,11 +124,12 @@ public final class Main { ...@@ -120,11 +124,12 @@ public final class Main {
120 } 124 }
121 125
122 // Initiates a web-server for the monitor GUI. 126 // Initiates a web-server for the monitor GUI.
123 - private static void startMonitorServer() { 127 + private static void startMonitorServer(Monitor monitor) {
124 org.eclipse.jetty.util.log.Log.setLog(new NullLogger()); 128 org.eclipse.jetty.util.log.Log.setLog(new NullLogger());
125 Server server = new Server(9999); 129 Server server = new Server(9999);
126 ServletHandler handler = new ServletHandler(); 130 ServletHandler handler = new ServletHandler();
127 server.setHandler(handler); 131 server.setHandler(handler);
132 + MonitorWebSocketServlet.setMonitor(monitor);
128 handler.addServletWithMapping(MonitorWebSocketServlet.class, "/*"); 133 handler.addServletWithMapping(MonitorWebSocketServlet.class, "/*");
129 try { 134 try {
130 server.start(); 135 server.start();
...@@ -187,8 +192,8 @@ public final class Main { ...@@ -187,8 +192,8 @@ public final class Main {
187 */ 192 */
188 private static class Listener implements StepProcessListener { 193 private static class Listener implements StepProcessListener {
189 @Override 194 @Override
190 - public void onStart(Step step) { 195 + public void onStart(Step step, String command) {
191 - logStatus(currentTimeMillis(), step.name(), IN_PROGRESS, step.command()); 196 + logStatus(currentTimeMillis(), step.name(), IN_PROGRESS, command);
192 } 197 }
193 198
194 @Override 199 @Override
......
1 +/*
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 + */
16 +package org.onlab.stc;
17 +
18 +import com.fasterxml.jackson.databind.ObjectMapper;
19 +import com.fasterxml.jackson.databind.node.ArrayNode;
20 +import com.fasterxml.jackson.databind.node.ObjectNode;
21 +import com.google.common.collect.Maps;
22 +import org.onlab.stc.MonitorLayout.Box;
23 +
24 +import java.io.FileWriter;
25 +import java.io.IOException;
26 +import java.io.PrintWriter;
27 +import java.util.Map;
28 +
29 +import static org.onlab.stc.Coordinator.Status.IN_PROGRESS;
30 +
31 +/**
32 + * Scenario test monitor.
33 + */
34 +public class Monitor implements StepProcessListener {
35 +
36 + private final ObjectMapper mapper = new ObjectMapper();
37 +
38 + private final Coordinator coordinator;
39 + private final Compiler compiler;
40 + private final MonitorLayout layout;
41 +
42 + private MonitorDelegate delegate;
43 +
44 + private Map<Step, Box> boxes = Maps.newHashMap();
45 +
46 + /**
47 + * Creates a new shared process flow monitor.
48 + *
49 + * @param coordinator process flow coordinator
50 + * @param compiler scenario compiler
51 + */
52 + Monitor(Coordinator coordinator, Compiler compiler) {
53 + this.coordinator = coordinator;
54 + this.compiler = compiler;
55 + this.layout = new MonitorLayout(compiler);
56 + coordinator.addListener(this);
57 + }
58 +
59 + /**
60 + * Sets the process monitor delegate.
61 + *
62 + * @param delegate process monitor delegate
63 + */
64 + void setDelegate(MonitorDelegate delegate) {
65 + this.delegate = delegate;
66 + }
67 +
68 + /**
69 + * Notifies the process monitor delegate with the specified event.
70 + *
71 + * @param event JSON event data
72 + */
73 + public void notify(ObjectNode event) {
74 + if (delegate != null) {
75 + delegate.notify(event);
76 + }
77 + }
78 +
79 + /**
80 + * Returns the scenario process flow as JSON data.
81 + *
82 + * @return scenario process flow data
83 + */
84 + ObjectNode scenarioData() {
85 + ObjectNode root = mapper.createObjectNode();
86 + ArrayNode steps = mapper.createArrayNode();
87 + ArrayNode requirements = mapper.createArrayNode();
88 +
89 + ProcessFlow pf = compiler.processFlow();
90 + pf.getVertexes().forEach(step -> add(step, steps));
91 + pf.getEdges().forEach(requirement -> add(requirement, requirements));
92 +
93 + root.set("steps", steps);
94 + root.set("requirements", requirements);
95 +
96 + try (FileWriter fw = new FileWriter("/tmp/data.json");
97 + PrintWriter pw = new PrintWriter(fw)) {
98 + pw.println(root.toString());
99 + } catch (IOException e) {
100 + e.printStackTrace();
101 + }
102 + return root;
103 + }
104 +
105 +
106 + private void add(Step step, ArrayNode steps) {
107 + Box box = layout.get(step);
108 + ObjectNode sn = mapper.createObjectNode()
109 + .put("name", step.name())
110 + .put("isGroup", step instanceof Group)
111 + .put("status", status(coordinator.getStatus(step)))
112 + .put("tier", box.tier())
113 + .put("depth", box.depth());
114 + if (step.group() != null) {
115 + sn.put("group", step.group().name());
116 + }
117 + steps.add(sn);
118 + }
119 +
120 + private String status(Coordinator.Status status) {
121 + return status.toString().toLowerCase();
122 + }
123 +
124 + private void add(Dependency requirement, ArrayNode requirements) {
125 + ObjectNode rn = mapper.createObjectNode();
126 + rn.put("src", requirement.src().name())
127 + .put("dst", requirement.dst().name())
128 + .put("isSoft", requirement.isSoft());
129 + requirements.add(rn);
130 + }
131 +
132 + @Override
133 + public void onStart(Step step, String command) {
134 + notify(event(step, status(IN_PROGRESS)));
135 + }
136 +
137 + @Override
138 + public void onCompletion(Step step, Coordinator.Status status) {
139 + notify(event(step, status(status)));
140 + }
141 +
142 + @Override
143 + public void onOutput(Step step, String line) {
144 +
145 + }
146 +
147 + private ObjectNode event(Step step, String status) {
148 + ObjectNode event = mapper.createObjectNode()
149 + .put("name", step.name())
150 + .put("status", status);
151 + return event;
152 + }
153 +
154 +}
1 +/*
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 + */
16 +package org.onlab.stc;
17 +
18 +import com.fasterxml.jackson.databind.node.ObjectNode;
19 +
20 +/**
21 + * Delegate to which monitor can send notifications.
22 + */
23 +public interface MonitorDelegate {
24 +
25 + /**
26 + * Issues JSON event to be sent to any connected monitor clients.
27 + *
28 + * @param event JSON event data
29 + */
30 + void notify(ObjectNode event);
31 +}
This diff is collapsed. Click to expand it.
...@@ -18,24 +18,24 @@ package org.onlab.stc; ...@@ -18,24 +18,24 @@ package org.onlab.stc;
18 import com.fasterxml.jackson.databind.ObjectMapper; 18 import com.fasterxml.jackson.databind.ObjectMapper;
19 import com.fasterxml.jackson.databind.node.ObjectNode; 19 import com.fasterxml.jackson.databind.node.ObjectNode;
20 import org.eclipse.jetty.websocket.WebSocket; 20 import org.eclipse.jetty.websocket.WebSocket;
21 -import org.slf4j.Logger;
22 -import org.slf4j.LoggerFactory;
23 21
24 import java.io.IOException; 22 import java.io.IOException;
25 23
24 +import static org.onlab.stc.Coordinator.print;
25 +
26 /** 26 /**
27 * Web socket capable of interacting with the STC monitor GUI. 27 * Web socket capable of interacting with the STC monitor GUI.
28 */ 28 */
29 public class MonitorWebSocket implements WebSocket.OnTextMessage, WebSocket.OnControl { 29 public class MonitorWebSocket implements WebSocket.OnTextMessage, WebSocket.OnControl {
30 30
31 - private static final Logger log = LoggerFactory.getLogger(MonitorWebSocket.class);
32 -
33 private static final long MAX_AGE_MS = 30_000; 31 private static final long MAX_AGE_MS = 30_000;
34 32
35 private static final byte PING = 0x9; 33 private static final byte PING = 0x9;
36 private static final byte PONG = 0xA; 34 private static final byte PONG = 0xA;
37 private static final byte[] PING_DATA = new byte[]{(byte) 0xde, (byte) 0xad}; 35 private static final byte[] PING_DATA = new byte[]{(byte) 0xde, (byte) 0xad};
38 36
37 + private final Monitor monitor;
38 +
39 private Connection connection; 39 private Connection connection;
40 private FrameConnection control; 40 private FrameConnection control;
41 41
...@@ -44,6 +44,15 @@ public class MonitorWebSocket implements WebSocket.OnTextMessage, WebSocket.OnCo ...@@ -44,6 +44,15 @@ public class MonitorWebSocket implements WebSocket.OnTextMessage, WebSocket.OnCo
44 private long lastActive = System.currentTimeMillis(); 44 private long lastActive = System.currentTimeMillis();
45 45
46 /** 46 /**
47 + * Creates a new monitor client GUI web-socket.
48 + *
49 + * @param monitor shared process flow monitor
50 + */
51 + MonitorWebSocket(Monitor monitor) {
52 + this.monitor = monitor;
53 + }
54 +
55 + /**
47 * Issues a close on the connection. 56 * Issues a close on the connection.
48 */ 57 */
49 synchronized void close() { 58 synchronized void close() {
...@@ -62,13 +71,12 @@ public class MonitorWebSocket implements WebSocket.OnTextMessage, WebSocket.OnCo ...@@ -62,13 +71,12 @@ public class MonitorWebSocket implements WebSocket.OnTextMessage, WebSocket.OnCo
62 long quietFor = System.currentTimeMillis() - lastActive; 71 long quietFor = System.currentTimeMillis() - lastActive;
63 boolean idle = quietFor > MAX_AGE_MS; 72 boolean idle = quietFor > MAX_AGE_MS;
64 if (idle || (connection != null && !connection.isOpen())) { 73 if (idle || (connection != null && !connection.isOpen())) {
65 - log.debug("IDLE (or closed) websocket [{} ms]", quietFor);
66 return true; 74 return true;
67 } else if (connection != null) { 75 } else if (connection != null) {
68 try { 76 try {
69 control.sendControl(PING, PING_DATA, 0, PING_DATA.length); 77 control.sendControl(PING, PING_DATA, 0, PING_DATA.length);
70 } catch (IOException e) { 78 } catch (IOException e) {
71 - log.warn("Unable to send ping message due to: ", e); 79 + print("Unable to send ping message due to: %s", e);
72 } 80 }
73 } 81 }
74 return false; 82 return false;
...@@ -80,10 +88,10 @@ public class MonitorWebSocket implements WebSocket.OnTextMessage, WebSocket.OnCo ...@@ -80,10 +88,10 @@ public class MonitorWebSocket implements WebSocket.OnTextMessage, WebSocket.OnCo
80 this.control = (FrameConnection) connection; 88 this.control = (FrameConnection) connection;
81 try { 89 try {
82 createHandlers(); 90 createHandlers();
83 - log.info("GUI client connected"); 91 + sendMessage(message("flow", monitor.scenarioData()));
84 92
85 } catch (Exception e) { 93 } catch (Exception e) {
86 - log.warn("Unable to open monitor connection: {}", e); 94 + print("Unable to open monitor connection: %s", e);
87 this.connection.close(); 95 this.connection.close();
88 this.connection = null; 96 this.connection = null;
89 this.control = null; 97 this.control = null;
...@@ -93,8 +101,6 @@ public class MonitorWebSocket implements WebSocket.OnTextMessage, WebSocket.OnCo ...@@ -93,8 +101,6 @@ public class MonitorWebSocket implements WebSocket.OnTextMessage, WebSocket.OnCo
93 @Override 101 @Override
94 public synchronized void onClose(int closeCode, String message) { 102 public synchronized void onClose(int closeCode, String message) {
95 destroyHandlers(); 103 destroyHandlers();
96 - log.info("GUI client disconnected [close-code={}, message={}]",
97 - closeCode, message);
98 } 104 }
99 105
100 @Override 106 @Override
...@@ -109,10 +115,9 @@ public class MonitorWebSocket implements WebSocket.OnTextMessage, WebSocket.OnCo ...@@ -109,10 +115,9 @@ public class MonitorWebSocket implements WebSocket.OnTextMessage, WebSocket.OnCo
109 try { 115 try {
110 ObjectNode message = (ObjectNode) mapper.reader().readTree(data); 116 ObjectNode message = (ObjectNode) mapper.reader().readTree(data);
111 // TODO: 117 // TODO:
112 - log.info("Got message: {}", message); 118 + print("Got message: %s", message);
113 } catch (Exception e) { 119 } catch (Exception e) {
114 - log.warn("Unable to parse GUI message {} due to {}", data, e); 120 + print("Unable to parse GUI message %s due to %s", data, e);
115 - log.debug("Boom!!!", e);
116 } 121 }
117 } 122 }
118 123
...@@ -122,20 +127,14 @@ public class MonitorWebSocket implements WebSocket.OnTextMessage, WebSocket.OnCo ...@@ -122,20 +127,14 @@ public class MonitorWebSocket implements WebSocket.OnTextMessage, WebSocket.OnCo
122 connection.sendMessage(message.toString()); 127 connection.sendMessage(message.toString());
123 } 128 }
124 } catch (IOException e) { 129 } catch (IOException e) {
125 - log.warn("Unable to send message {} to GUI due to {}", message, e); 130 + print("Unable to send message %s to GUI due to %s", message, e);
126 - log.debug("Boom!!!", e);
127 } 131 }
128 } 132 }
129 133
130 - public synchronized void sendMessage(String type, long sid, ObjectNode payload) { 134 + public ObjectNode message(String type, ObjectNode payload) {
131 - ObjectNode message = mapper.createObjectNode(); 135 + ObjectNode message = mapper.createObjectNode().put("event", type);
132 - message.put("event", type);
133 - if (sid > 0) {
134 - message.put("sid", sid);
135 - }
136 message.set("payload", payload); 136 message.set("payload", payload);
137 - sendMessage(message); 137 + return message;
138 -
139 } 138 }
140 139
141 // Creates new message handlers. 140 // Creates new message handlers.
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
15 */ 15 */
16 package org.onlab.stc; 16 package org.onlab.stc;
17 17
18 +import com.fasterxml.jackson.databind.node.ObjectNode;
18 import com.google.common.io.ByteStreams; 19 import com.google.common.io.ByteStreams;
19 import com.google.common.net.MediaType; 20 import com.google.common.net.MediaType;
20 import org.eclipse.jetty.websocket.WebSocket; 21 import org.eclipse.jetty.websocket.WebSocket;
...@@ -34,11 +35,13 @@ import java.util.TimerTask; ...@@ -34,11 +35,13 @@ import java.util.TimerTask;
34 /** 35 /**
35 * Web socket servlet capable of creating web sockets for the STC monitor. 36 * Web socket servlet capable of creating web sockets for the STC monitor.
36 */ 37 */
37 -public class MonitorWebSocketServlet extends WebSocketServlet { 38 +public class MonitorWebSocketServlet extends WebSocketServlet
39 + implements MonitorDelegate {
38 40
39 private static final long PING_DELAY_MS = 5000; 41 private static final long PING_DELAY_MS = 5000;
40 private static final String DOT = "."; 42 private static final String DOT = ".";
41 43
44 + private static Monitor monitor;
42 private static MonitorWebSocketServlet instance; 45 private static MonitorWebSocketServlet instance;
43 46
44 private final Set<MonitorWebSocket> sockets = new HashSet<>(); 47 private final Set<MonitorWebSocket> sockets = new HashSet<>();
...@@ -46,6 +49,15 @@ public class MonitorWebSocketServlet extends WebSocketServlet { ...@@ -46,6 +49,15 @@ public class MonitorWebSocketServlet extends WebSocketServlet {
46 private final TimerTask pruner = new Pruner(); 49 private final TimerTask pruner = new Pruner();
47 50
48 /** 51 /**
52 + * Binds the shared process flow monitor.
53 + *
54 + * @param m process monitor reference
55 + */
56 + public static void setMonitor(Monitor m) {
57 + monitor = m;
58 + }
59 +
60 + /**
49 * Closes all currently open monitor web-sockets. 61 * Closes all currently open monitor web-sockets.
50 */ 62 */
51 public static void closeAll() { 63 public static void closeAll() {
...@@ -59,7 +71,7 @@ public class MonitorWebSocketServlet extends WebSocketServlet { ...@@ -59,7 +71,7 @@ public class MonitorWebSocketServlet extends WebSocketServlet {
59 public void init() throws ServletException { 71 public void init() throws ServletException {
60 super.init(); 72 super.init();
61 instance = this; 73 instance = this;
62 - System.out.println("Yo!!!!"); 74 + monitor.setDelegate(this);
63 timer.schedule(pruner, PING_DELAY_MS, PING_DELAY_MS); 75 timer.schedule(pruner, PING_DELAY_MS, PING_DELAY_MS);
64 } 76 }
65 77
...@@ -92,14 +104,20 @@ public class MonitorWebSocketServlet extends WebSocketServlet { ...@@ -92,14 +104,20 @@ public class MonitorWebSocketServlet extends WebSocketServlet {
92 104
93 @Override 105 @Override
94 public WebSocket doWebSocketConnect(HttpServletRequest request, String protocol) { 106 public WebSocket doWebSocketConnect(HttpServletRequest request, String protocol) {
95 - System.out.println("Wazup????"); 107 + MonitorWebSocket socket = new MonitorWebSocket(monitor);
96 - MonitorWebSocket socket = new MonitorWebSocket();
97 synchronized (sockets) { 108 synchronized (sockets) {
98 sockets.add(socket); 109 sockets.add(socket);
99 } 110 }
100 return socket; 111 return socket;
101 } 112 }
102 113
114 + @Override
115 + public void notify(ObjectNode event) {
116 + if (instance != null) {
117 + instance.sockets.forEach(ws -> ws.sendMessage(event));
118 + }
119 + }
120 +
103 // Task for pruning web-sockets that are idle. 121 // Task for pruning web-sockets that are idle.
104 private class Pruner extends TimerTask { 122 private class Pruner extends TimerTask {
105 @Override 123 @Override
......
...@@ -24,8 +24,9 @@ public interface StepProcessListener { ...@@ -24,8 +24,9 @@ public interface StepProcessListener {
24 * Indicates that process step has started. 24 * Indicates that process step has started.
25 * 25 *
26 * @param step subject step 26 * @param step subject step
27 + * @param command actual command executed; includes run-time substitutions
27 */ 28 */
28 - default void onStart(Step step) { 29 + default void onStart(Step step, String command) {
29 } 30 }
30 31
31 /** 32 /**
......
...@@ -23,6 +23,7 @@ import java.io.IOException; ...@@ -23,6 +23,7 @@ import java.io.IOException;
23 import java.io.InputStream; 23 import java.io.InputStream;
24 import java.io.InputStreamReader; 24 import java.io.InputStreamReader;
25 import java.io.PrintWriter; 25 import java.io.PrintWriter;
26 +import java.util.function.Function;
26 27
27 import static java.lang.String.format; 28 import static java.lang.String.format;
28 import static org.onlab.stc.Coordinator.Status.FAILED; 29 import static org.onlab.stc.Coordinator.Status.FAILED;
...@@ -41,9 +42,11 @@ class StepProcessor implements Runnable { ...@@ -41,9 +42,11 @@ class StepProcessor implements Runnable {
41 42
42 private final Step step; 43 private final Step step;
43 private final File logDir; 44 private final File logDir;
45 + private String command;
44 46
45 private Process process; 47 private Process process;
46 private StepProcessListener delegate; 48 private StepProcessListener delegate;
49 + private Function<String, String> substitutor;
47 50
48 /** 51 /**
49 * Creates a process monitor. 52 * Creates a process monitor.
...@@ -51,16 +54,20 @@ class StepProcessor implements Runnable { ...@@ -51,16 +54,20 @@ class StepProcessor implements Runnable {
51 * @param step step or group to be executed 54 * @param step step or group to be executed
52 * @param logDir directory where step process log should be stored 55 * @param logDir directory where step process log should be stored
53 * @param delegate process lifecycle listener 56 * @param delegate process lifecycle listener
57 + * @param substitutor function to substitute var reference in command
54 */ 58 */
55 - StepProcessor(Step step, File logDir, StepProcessListener delegate) { 59 + StepProcessor(Step step, File logDir, StepProcessListener delegate,
60 + Function<String, String> substitutor) {
56 this.step = step; 61 this.step = step;
57 this.logDir = logDir; 62 this.logDir = logDir;
58 this.delegate = delegate; 63 this.delegate = delegate;
64 + this.substitutor = substitutor;
59 } 65 }
60 66
61 @Override 67 @Override
62 public void run() { 68 public void run() {
63 - delegate.onStart(step); 69 + command = substitutor != null ? substitutor.apply(command()) : command();
70 + delegate.onStart(step, command);
64 int code = execute(); 71 int code = execute();
65 boolean ignoreCode = step.env() != null && step.env.equals(IGNORE_CODE); 72 boolean ignoreCode = step.env() != null && step.env.equals(IGNORE_CODE);
66 Status status = ignoreCode || code == 0 ? SUCCEEDED : FAILED; 73 Status status = ignoreCode || code == 0 ? SUCCEEDED : FAILED;
...@@ -74,7 +81,7 @@ class StepProcessor implements Runnable { ...@@ -74,7 +81,7 @@ class StepProcessor implements Runnable {
74 */ 81 */
75 private int execute() { 82 private int execute() {
76 try (PrintWriter pw = new PrintWriter(logFile())) { 83 try (PrintWriter pw = new PrintWriter(logFile())) {
77 - process = Runtime.getRuntime().exec(command()); 84 + process = Runtime.getRuntime().exec(command);
78 processOutput(pw); 85 processOutput(pw);
79 86
80 // Wait for the process to complete and get its exit code. 87 // Wait for the process to complete and get its exit code.
......
This diff is collapsed. Click to expand it.
...@@ -16,14 +16,14 @@ ...@@ -16,14 +16,14 @@
16 --> 16 -->
17 <html> 17 <html>
18 <head lang="en"> 18 <head lang="en">
19 - <meta charset="UTF-8"> 19 + <meta charset="utf-8">
20 <title>Scenario Test Coordinator</title> 20 <title>Scenario Test Coordinator</title>
21 21
22 - <script src="stc.js"></script>
23 <link rel="stylesheet" href="stc.css"> 22 <link rel="stylesheet" href="stc.css">
23 +
24 + <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script>
25 + <script src="stc.js"></script>
24 </head> 26 </head>
25 <body> 27 <body>
26 -<h1>Scenario Test Coordinator</h1>
27 -
28 </body> 28 </body>
29 </html> 29 </html>
...\ No newline at end of file ...\ No newline at end of file
......
...@@ -15,5 +15,23 @@ ...@@ -15,5 +15,23 @@
15 */ 15 */
16 16
17 .body { 17 .body {
18 - font-family: Helvetica, Arial; 18 + font-family: Helvetica, Arial, sans-serif;
19 +}
20 +
21 +.node {
22 + stroke: #fff;
23 + stroke-width: 1.5px;
24 +}
25 +
26 +.link {
27 + stroke: #999;
28 + stroke-opacity: .6;
29 +}
30 +
31 +text {
32 + font-family: 'DejaVu Sans', Arial, Helvetica, sans-serif;
33 + stroke: #000;
34 + stroke-width: 0.2;
35 + font-weight: normal;
36 + font-size: 0.6em;
19 } 37 }
...\ No newline at end of file ...\ No newline at end of file
......
...@@ -15,4 +15,134 @@ ...@@ -15,4 +15,134 @@
15 */ 15 */
16 (function () { 16 (function () {
17 17
18 + var ws, flow,
19 + nodes = [],
20 + links = [],
21 + nodeIndexes = {};
22 +
23 + var width = 2400,
24 + height = 2400;
25 +
26 + var color = d3.scale.category20();
27 +
28 + var force = d3.layout.force()
29 + .charge(-820)
30 + .linkDistance(50)
31 + .size([width, height]);
32 +
33 + // Process flow graph layout
34 + function createNode(n) {
35 + nodeIndexes[n.name] = nodes.push(n) - 1;
36 + }
37 +
38 + function createLink(e) {
39 + e.source = nodeIndexes[e.src];
40 + e.target = nodeIndexes[e.dst];
41 + links.push(e);
42 + }
43 +
44 + // Returns the newly computed bounding box of the rectangle
45 + function adjustRectToFitText(n) {
46 + var text = n.select('text'),
47 + box = text.node().getBBox();
48 +
49 + text.attr('text-anchor', 'left')
50 + .attr('y', 2)
51 + .attr('x', 4);
52 +
53 + // add padding
54 + box.x -= 4;
55 + box.width += 8;
56 + box.y -= 2;
57 + box.height += 4;
58 +
59 + n.select("rect").attr(box);
60 + }
61 +
62 + function processFlow() {
63 + var svg = d3.select("body").append("svg")
64 + .attr("width", width)
65 + .attr("height", height);
66 +
67 + flow.steps.forEach(createNode);
68 + flow.requirements.forEach(createLink);
69 +
70 + force
71 + .nodes(nodes)
72 + .links(links)
73 + .start();
74 +
75 + var link = svg.selectAll(".link")
76 + .data(links)
77 + .enter().append("line")
78 + .attr("class", "link")
79 + .style("stroke-width", function(d) { return d.isSoft ? 1 : 2; });
80 +
81 + var node = svg.selectAll(".node")
82 + .data(nodes)
83 + .enter().append("g")
84 + .attr("class", "node")
85 + .call(force.drag);
86 +
87 + node.append("rect")
88 + .attr({ rx: 5, ry:5, width:180, height:18 })
89 + .style("fill", function(d) { return color(d.group); });
90 +
91 + node.append("text").text( function(d) { return d.name; })
92 + .attr({ dy:"1.1em", width:100, height:16, x:4, y:2 });
93 +
94 + node.append("title")
95 + .text(function(d) { return d.name; });
96 +
97 + force.on("tick", function() {
98 + link.attr("x1", function(d) { return d.source.x; })
99 + .attr("y1", function(d) { return d.source.y; })
100 + .attr("x2", function(d) { return d.target.x; })
101 + .attr("y2", function(d) { return d.target.y; });
102 +
103 + node.attr("transform", function(d) { return "translate(" + (d.x - 180/2) + "," + (d.y - 18/2) + ")"; });
104 + });
105 + }
106 +
107 +
108 + // Web socket callbacks
109 +
110 + function handleOpen() {
111 + console.log('WebSocket open');
112 + }
113 +
114 + // Handles the specified (incoming) message using handler bindings.
115 + function handleMessage(msg) {
116 + console.log('rx: ', msg);
117 + evt = JSON.parse(msg.data);
118 + if (evt.event === 'progress') {
119 +
120 + } else if (evt.event === 'log') {
121 +
122 + } else if (evt.event === 'flow') {
123 + flow = evt.payload;
124 + processFlow();
125 + }
126 + }
127 +
128 + function handleClose() {
129 + console.log('WebSocket closed');
130 + }
131 +
132 + if (false) {
133 + d3.json("data.json", function (error, data) {
134 + flow = data;
135 + processFlow();
136 + });
137 + return;
138 + }
139 +
140 + // Open the web-socket
141 + ws = new WebSocket(document.location.href.replace('http:', 'ws:'));
142 + if (ws) {
143 + ws.onopen = handleOpen;
144 + ws.onmessage = handleMessage;
145 + ws.onclose = handleClose;
146 + }
147 +
18 })(); 148 })();
...\ No newline at end of file ...\ No newline at end of file
......
...@@ -52,11 +52,11 @@ public class CompilerTest { ...@@ -52,11 +52,11 @@ public class CompilerTest {
52 System.setProperty("test.dir", TEST_DIR.getAbsolutePath()); 52 System.setProperty("test.dir", TEST_DIR.getAbsolutePath());
53 } 53 }
54 54
55 - public static FileInputStream getStream(String name) throws FileNotFoundException { 55 + static FileInputStream getStream(String name) throws FileNotFoundException {
56 return new FileInputStream(new File(TEST_DIR, name)); 56 return new FileInputStream(new File(TEST_DIR, name));
57 } 57 }
58 58
59 - private static void stageTestResource(String name) throws IOException { 59 + static void stageTestResource(String name) throws IOException {
60 byte[] bytes = toByteArray(CompilerTest.class.getResourceAsStream(name)); 60 byte[] bytes = toByteArray(CompilerTest.class.getResourceAsStream(name));
61 write(bytes, new File(TEST_DIR, name)); 61 write(bytes, new File(TEST_DIR, name));
62 } 62 }
......
...@@ -66,8 +66,8 @@ public class CoordinatorTest { ...@@ -66,8 +66,8 @@ public class CoordinatorTest {
66 66
67 private class Listener implements StepProcessListener { 67 private class Listener implements StepProcessListener {
68 @Override 68 @Override
69 - public void onStart(Step step) { 69 + public void onStart(Step step, String command) {
70 - print("> %s: started", step.name()); 70 + print("> %s: started; %s", step.name(), command);
71 } 71 }
72 72
73 @Override 73 @Override
......
1 +/*
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 + */
16 +package org.onlab.stc;
17 +
18 +import org.junit.Test;
19 +import org.onlab.stc.MonitorLayout.Box;
20 +
21 +import java.io.IOException;
22 +
23 +import static org.junit.Assert.assertEquals;
24 +import static org.onlab.stc.CompilerTest.getStream;
25 +import static org.onlab.stc.CompilerTest.stageTestResource;
26 +import static org.onlab.stc.MonitorLayout.SLOT_WIDTH;
27 +import static org.onlab.stc.Scenario.loadScenario;
28 +
29 +/**
30 + * Tests of the monitor layout functionality.
31 + */
32 +public class MonitorLayoutTest {
33 +
34 + private MonitorLayout layout;
35 +
36 + private Compiler getCompiler(String name) throws IOException {
37 + stageTestResource(name);
38 + Scenario scenario = loadScenario(getStream(name));
39 + Compiler compiler = new Compiler(scenario);
40 + compiler.compile();
41 + return compiler;
42 + }
43 +
44 + @Test
45 + public void basic() throws IOException {
46 + layout = new MonitorLayout(getCompiler("layout-basic.xml"));
47 + validate(layout, null, 0, 1, 5, 2);
48 + validate(layout, "a", 1, 1, 1, 1, 1, -SLOT_WIDTH / 2);
49 + validate(layout, "b", 2, 2, 1, 1, 0, 0);
50 + validate(layout, "f", 3, 3, 1);
51 +
52 + validate(layout, "g", 1, 1, 4, 1, 1, SLOT_WIDTH / 2);
53 + validate(layout, "c", 2, 1, 1);
54 + validate(layout, "d", 3, 2, 1);
55 + validate(layout, "e", 4, 3, 1);
56 + }
57 +
58 + @Test
59 + public void basicNest() throws IOException {
60 + layout = new MonitorLayout(getCompiler("layout-basic-nest.xml"));
61 + validate(layout, null, 0, 1, 6, 2);
62 + validate(layout, "a", 1, 1, 1, 1, 1, -SLOT_WIDTH / 2);
63 + validate(layout, "b", 2, 2, 1);
64 + validate(layout, "f", 3, 3, 1);
65 +
66 + validate(layout, "g", 1, 1, 5, 1);
67 + validate(layout, "c", 2, 1, 1);
68 +
69 + validate(layout, "gg", 3, 2, 3, 1);
70 + validate(layout, "d", 4, 1, 1);
71 + validate(layout, "e", 5, 2, 1);
72 + }
73 +
74 + @Test
75 + public void staggeredDependencies() throws IOException {
76 + layout = new MonitorLayout(getCompiler("layout-staggered-dependencies.xml"));
77 + validate(layout, null, 0, 1, 7, 4);
78 + validate(layout, "a", 1, 1, 1, 1, 1, -SLOT_WIDTH - SLOT_WIDTH / 2);
79 + validate(layout, "aa", 1, 1, 1, 1, 1, -SLOT_WIDTH / 2);
80 + validate(layout, "b", 2, 2, 1);
81 + validate(layout, "f", 3, 3, 1);
82 +
83 + validate(layout, "g", 1, 1, 5, 2, 1, +SLOT_WIDTH / 2);
84 + validate(layout, "c", 2, 1, 1);
85 +
86 + validate(layout, "gg", 3, 2, 3, 2);
87 + validate(layout, "d", 4, 1, 1);
88 + validate(layout, "dd", 4, 1, 1);
89 + validate(layout, "e", 5, 2, 1);
90 +
91 + validate(layout, "i", 6, 6, 1);
92 + }
93 +
94 + @Test
95 + public void deepNext() throws IOException {
96 + layout = new MonitorLayout(getCompiler("layout-deep-nest.xml"));
97 + validate(layout, null, 0, 1, 7, 6);
98 + validate(layout, "a", 1, 1, 1);
99 + validate(layout, "aa", 1, 1, 1);
100 + validate(layout, "b", 2, 2, 1);
101 + validate(layout, "f", 3, 3, 1);
102 +
103 + validate(layout, "g", 1, 1, 5, 2);
104 + validate(layout, "c", 2, 1, 1);
105 +
106 + validate(layout, "gg", 3, 2, 3, 2);
107 + validate(layout, "d", 4, 1, 1);
108 + validate(layout, "dd", 4, 1, 1);
109 + validate(layout, "e", 5, 2, 1);
110 +
111 + validate(layout, "i", 6, 6, 1);
112 +
113 + validate(layout, "g1", 1, 1, 6, 2);
114 + validate(layout, "g2", 2, 1, 5, 2);
115 + validate(layout, "g3", 3, 1, 4, 2);
116 + validate(layout, "u", 4, 1, 1);
117 + validate(layout, "v", 4, 1, 1);
118 + validate(layout, "w", 5, 2, 1);
119 + validate(layout, "z", 6, 3, 1);
120 + }
121 +
122 +
123 + private void validate(MonitorLayout layout, String name,
124 + int absoluteTier, int tier, int depth, int breadth) {
125 + Box b = layout.get(name);
126 + assertEquals("incorrect absolute tier", absoluteTier, b.absoluteTier());
127 + assertEquals("incorrect tier", tier, b.tier());
128 + assertEquals("incorrect depth", depth, b.depth());
129 + assertEquals("incorrect breadth", breadth, b.breadth());
130 + }
131 +
132 + private void validate(MonitorLayout layout, String name,
133 + int absoluteTier, int tier, int depth, int breadth,
134 + int top, int center) {
135 + validate(layout, name, absoluteTier, tier, depth, breadth);
136 + Box b = layout.get(name);
137 + assertEquals("incorrect top", top, b.top());
138 + assertEquals("incorrect center", center, b.center());
139 + }
140 +
141 + private void validate(MonitorLayout layout, String name,
142 + int absoluteTier, int tier, int depth) {
143 + validate(layout, name, absoluteTier, tier, depth, 1);
144 + }
145 +
146 +}
...\ No newline at end of file ...\ No newline at end of file
...@@ -51,7 +51,7 @@ public class StepProcessorTest { ...@@ -51,7 +51,7 @@ public class StepProcessorTest {
51 @Test 51 @Test
52 public void basics() { 52 public void basics() {
53 Step step = new Step("foo", "ls " + DIR.getAbsolutePath(), null, null, null); 53 Step step = new Step("foo", "ls " + DIR.getAbsolutePath(), null, null, null);
54 - StepProcessor processor = new StepProcessor(step, DIR, delegate); 54 + StepProcessor processor = new StepProcessor(step, DIR, delegate, null);
55 processor.run(); 55 processor.run();
56 assertTrue("should be started", delegate.started); 56 assertTrue("should be started", delegate.started);
57 assertTrue("should be stopped", delegate.stopped); 57 assertTrue("should be stopped", delegate.stopped);
...@@ -65,7 +65,7 @@ public class StepProcessorTest { ...@@ -65,7 +65,7 @@ public class StepProcessorTest {
65 private boolean started, stopped, output; 65 private boolean started, stopped, output;
66 66
67 @Override 67 @Override
68 - public void onStart(Step step) { 68 + public void onStart(Step step, String command) {
69 started = true; 69 started = true;
70 } 70 }
71 71
......
1 +<!--
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 + -->
16 +<scenario name="basic-nest">
17 + <step name="a"/>
18 + <step name="b" requires="a"/>
19 + <step name="f" requires="b"/>
20 + <group name="g">
21 + <step name="c"/>
22 + <group name="gg" requires="c">
23 + <step name="d"/>
24 + <step name="e" requires="d"/>
25 + </group>
26 + </group>
27 +</scenario>
...\ No newline at end of file ...\ No newline at end of file
1 +<!--
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 + -->
16 +<scenario name="basic">
17 + <step name="a"/>
18 + <step name="b" requires="a"/>
19 + <step name="f" requires="b"/>
20 + <group name="g">
21 + <step name="c"/>
22 + <step name="d" requires="c"/>
23 + <step name="e" requires="d"/>
24 + </group>
25 +</scenario>
...\ No newline at end of file ...\ No newline at end of file
1 +<!--
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 + -->
16 +<scenario name="basic-nest">
17 + <step name="a"/>
18 + <step name="aa"/>
19 + <step name="b" requires="a"/>
20 + <step name="f" requires="b,aa"/>
21 + <group name="g">
22 + <step name="c"/>
23 + <group name="gg" requires="c">
24 + <step name="d"/>
25 + <step name="dd" requires="c"/>
26 + <step name="e" requires="d"/>
27 + </group>
28 + </group>
29 + <step name="i" requires="f,g"/>
30 +
31 + <group name="g1">
32 + <group name="g2">
33 + <group name="g3">
34 + <step name="u"/>
35 + <step name="v"/>
36 + <step name="w" requires="u,v"/>
37 + <step name="z" requires="u,w"/>
38 + </group>
39 + </group>
40 + </group>
41 +</scenario>
...\ No newline at end of file ...\ No newline at end of file
1 +<!--
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 + -->
16 +<scenario name="basic-nest">
17 + <step name="a"/>
18 + <step name="aa"/>
19 + <step name="b" requires="a"/>
20 + <step name="f" requires="b,aa"/>
21 + <group name="g">
22 + <step name="c"/>
23 + <group name="gg" requires="c">
24 + <step name="d"/>
25 + <step name="dd" requires="c"/>
26 + <step name="e" requires="d"/>
27 + </group>
28 + </group>
29 + <step name="i" requires="f,g"/>
30 +</scenario>
...\ No newline at end of file ...\ No newline at end of file