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
Showing
26 changed files
with
781 additions
and
61 deletions
... | @@ -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 | ... | ... |
tools/test/scenarios/example.xml
0 → 100644
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. | ... | ... |
utils/stc/src/main/resources/data.json
0 → 100644
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 |
-
Please register or login to post a comment