You need to sign in or sign up before continuing.
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