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
2181 additions
and
67 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 | } |
424 | - sb.append(value != null ? value : ""); | 425 | + if (value == null && keepTokens.length == 1 && keepTokens[0]) { |
426 | + sb.append("${").append(prop).append("}"); | ||
427 | + } else { | ||
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 | +} |
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.google.common.collect.Lists; | ||
19 | +import com.google.common.collect.Maps; | ||
20 | + | ||
21 | +import java.util.List; | ||
22 | +import java.util.Map; | ||
23 | +import java.util.Set; | ||
24 | +import java.util.stream.Collectors; | ||
25 | +import java.util.stream.IntStream; | ||
26 | + | ||
27 | +/** | ||
28 | + * Computes scenario process flow layout for the Monitor GUI. | ||
29 | + */ | ||
30 | +public class MonitorLayout { | ||
31 | + | ||
32 | + public static final int WIDTH = 210; | ||
33 | + public static final int HEIGHT = 30; | ||
34 | + public static final int W_GAP = 40; | ||
35 | + public static final int H_GAP = 50; | ||
36 | + public static final int SLOT_WIDTH = WIDTH + H_GAP; | ||
37 | + | ||
38 | + private final Compiler compiler; | ||
39 | + private final ProcessFlow flow; | ||
40 | + | ||
41 | + private Map<Step, Box> boxes = Maps.newHashMap(); | ||
42 | + | ||
43 | + /** | ||
44 | + * Creates a new shared process flow monitor. | ||
45 | + * | ||
46 | + * @param compiler scenario compiler | ||
47 | + */ | ||
48 | + MonitorLayout(Compiler compiler) { | ||
49 | + this.compiler = compiler; | ||
50 | + this.flow = compiler.processFlow(); | ||
51 | + | ||
52 | + // Extract the flow and create initial bounding boxes. | ||
53 | + boxes.put(null, new Box(null, 0)); | ||
54 | + flow.getVertexes().forEach(this::createBox); | ||
55 | + | ||
56 | + computeLayout(null, 0, 1); | ||
57 | + } | ||
58 | + | ||
59 | + // Computes the graph layout giving preference to group associations. | ||
60 | + private void computeLayout(Group group, int absoluteTier, int tier) { | ||
61 | + Box box = boxes.get(group); | ||
62 | + | ||
63 | + // Find all children of the group, or items with no group if at top. | ||
64 | + Set<Step> children = group != null ? group.children() : | ||
65 | + flow.getVertexes().stream().filter(s -> s.group() == null) | ||
66 | + .collect(Collectors.toSet()); | ||
67 | + | ||
68 | + children.forEach(s -> visit(s, absoluteTier, 1, group)); | ||
69 | + | ||
70 | + // Figure out what the group root vertexes are. | ||
71 | + Set<Step> roots = findRoots(group); | ||
72 | + | ||
73 | + // Compute the boxes for each of the roots. | ||
74 | + roots.forEach(s -> updateBox(s, absoluteTier + 1, 1, group)); | ||
75 | + | ||
76 | + // Update the tier and depth of the group bounding box. | ||
77 | + computeTiersAndDepth(group, box, absoluteTier, tier, children); | ||
78 | + | ||
79 | + // Compute the minimum breadth of this group's bounding box. | ||
80 | + computeBreadth(group, box, children); | ||
81 | + | ||
82 | + // Compute child placements | ||
83 | + computeChildPlacements(group, box, children); | ||
84 | + } | ||
85 | + | ||
86 | + // Updates the box for the specified step, given the tier number, which | ||
87 | + // is relative to the parent. | ||
88 | + private Box updateBox(Step step, int absoluteTier, int tier, Group group) { | ||
89 | + Box box = boxes.get(step); | ||
90 | + if (step instanceof Group) { | ||
91 | + computeLayout((Group) step, absoluteTier, tier); | ||
92 | + } else { | ||
93 | + box.setTierAndDepth(absoluteTier, tier, 1, group); | ||
94 | + } | ||
95 | + | ||
96 | + // Follow the steps downstream of this one. | ||
97 | + follow(step, absoluteTier + box.depth(), box.tier() + box.depth()); | ||
98 | + return box; | ||
99 | + } | ||
100 | + | ||
101 | + // Backwards follows edges leading towards the specified step to visit | ||
102 | + // the source vertex and compute layout of those vertices that had | ||
103 | + // sufficient number of visits to compute their tier. | ||
104 | + private void follow(Step step, int absoluteTier, int tier) { | ||
105 | + Group from = step.group(); | ||
106 | + flow.getEdgesTo(step).stream() | ||
107 | + .filter(d -> visit(d.src(), absoluteTier, tier, from)) | ||
108 | + .forEach(d -> updateBox(d.src(), absoluteTier, tier, from)); | ||
109 | + } | ||
110 | + | ||
111 | + // Visits each step, records maximum tier and returns true if this | ||
112 | + // was the last expected visit. | ||
113 | + private boolean visit(Step step, int absoluteTier, int tier, Group from) { | ||
114 | + Box box = boxes.get(step); | ||
115 | + return box.visitAndLatchMaxTier(absoluteTier, tier, from); | ||
116 | + } | ||
117 | + | ||
118 | + // Computes the absolute and relative tiers and the depth of the group | ||
119 | + // bounding box. | ||
120 | + private void computeTiersAndDepth(Group group, Box box, | ||
121 | + int absoluteTier, int tier, Set<Step> children) { | ||
122 | + int depth = children.stream().mapToInt(this::bottomMostTier).max().getAsInt(); | ||
123 | + box.setTierAndDepth(absoluteTier, tier, depth, group); | ||
124 | + } | ||
125 | + | ||
126 | + // Returns the bottom-most tier this step occupies relative to its parent. | ||
127 | + private int bottomMostTier(Step step) { | ||
128 | + Box box = boxes.get(step); | ||
129 | + return box.tier() + box.depth(); | ||
130 | + } | ||
131 | + | ||
132 | + // Computes breadth of the specified group. | ||
133 | + private void computeBreadth(Group group, Box box, Set<Step> children) { | ||
134 | + if (box.breadth() == 0) { | ||
135 | + // Scan through all tiers and determine the maximum breadth of each. | ||
136 | + IntStream.range(1, box.depth) | ||
137 | + .forEach(t -> computeTierBreadth(t, box, children)); | ||
138 | + box.latchBreadth(children.stream() | ||
139 | + .mapToInt(s -> boxes.get(s).breadth()) | ||
140 | + .max().getAsInt()); | ||
141 | + } | ||
142 | + } | ||
143 | + | ||
144 | + // Computes tier width. | ||
145 | + private void computeTierBreadth(int t, Box box, Set<Step> children) { | ||
146 | + box.latchBreadth(children.stream().map(boxes::get) | ||
147 | + .filter(b -> isSpanningTier(b, t)) | ||
148 | + .mapToInt(Box::breadth).sum()); | ||
149 | + } | ||
150 | + | ||
151 | + // Computes the actual child box placements relative to the parent using | ||
152 | + // the previously established tier, depth and breadth attributes. | ||
153 | + private void computeChildPlacements(Group group, Box box, | ||
154 | + Set<Step> children) { | ||
155 | + // Order the root-nodes in alphanumeric order first. | ||
156 | + List<Box> tierBoxes = Lists.newArrayList(boxesOnTier(1, children)); | ||
157 | + tierBoxes.sort((a, b) -> a.step().name().compareTo(b.step().name())); | ||
158 | + | ||
159 | + // Place the boxes centered on the parent box; left to right. | ||
160 | + int tierBreadth = tierBoxes.stream().mapToInt(Box::breadth).sum(); | ||
161 | + int slot = 1; | ||
162 | + for (Box b : tierBoxes) { | ||
163 | + b.updateCenter(1, slot(slot, tierBreadth)); | ||
164 | + slot += b.breadth(); | ||
165 | + } | ||
166 | + } | ||
167 | + | ||
168 | + // Returns the horizontal offset off the parent center. | ||
169 | + private int slot(int slot, int tierBreadth) { | ||
170 | + boolean even = tierBreadth % 2 == 0; | ||
171 | + int multiplier = -tierBreadth / 2 + slot - 1; | ||
172 | + return even ? multiplier * SLOT_WIDTH + SLOT_WIDTH / 2 : multiplier * SLOT_WIDTH; | ||
173 | + } | ||
174 | + | ||
175 | + // Returns a list of all child step boxes that start on the specified tier. | ||
176 | + private List<Box> boxesOnTier(int tier, Set<Step> children) { | ||
177 | + return boxes.values().stream() | ||
178 | + .filter(b -> b.tier() == tier && children.contains(b.step())) | ||
179 | + .collect(Collectors.toList()); | ||
180 | + } | ||
181 | + | ||
182 | + // Determines whether the specified box spans, or occupies a tier. | ||
183 | + private boolean isSpanningTier(Box b, int tier) { | ||
184 | + return (b.depth() == 1 && b.tier() == tier) || | ||
185 | + (b.tier() <= tier && tier < b.tier() + b.depth()); | ||
186 | + } | ||
187 | + | ||
188 | + | ||
189 | + // Determines roots of the specified group or of the entire graph. | ||
190 | + private Set<Step> findRoots(Group group) { | ||
191 | + Set<Step> steps = group != null ? group.children() : flow.getVertexes(); | ||
192 | + return steps.stream().filter(s -> isRoot(s, group)).collect(Collectors.toSet()); | ||
193 | + } | ||
194 | + | ||
195 | + private boolean isRoot(Step step, Group group) { | ||
196 | + if (step.group() != group) { | ||
197 | + return false; | ||
198 | + } | ||
199 | + | ||
200 | + Set<Dependency> requirements = flow.getEdgesFrom(step); | ||
201 | + return requirements.stream().filter(r -> r.dst().group() == group) | ||
202 | + .collect(Collectors.toSet()).isEmpty(); | ||
203 | + } | ||
204 | + | ||
205 | + /** | ||
206 | + * Returns the bounding box for the specified step. If null is given, it | ||
207 | + * returns the overall bounding box. | ||
208 | + * | ||
209 | + * @param step step or group; null for the overall bounding box | ||
210 | + * @return bounding box | ||
211 | + */ | ||
212 | + public Box get(Step step) { | ||
213 | + return boxes.get(step); | ||
214 | + } | ||
215 | + | ||
216 | + /** | ||
217 | + * Returns the bounding box for the specified step name. If null is given, | ||
218 | + * it returns the overall bounding box. | ||
219 | + * | ||
220 | + * @param name name of step or group; null for the overall bounding box | ||
221 | + * @return bounding box | ||
222 | + */ | ||
223 | + public Box get(String name) { | ||
224 | + return get(name == null ? null : compiler.getStep(name)); | ||
225 | + } | ||
226 | + | ||
227 | + // Creates a bounding box for the specified step or group. | ||
228 | + private void createBox(Step step) { | ||
229 | + boxes.put(step, new Box(step, flow.getEdgesFrom(step).size())); | ||
230 | + } | ||
231 | + | ||
232 | + /** | ||
233 | + * Bounding box data for a step or group. | ||
234 | + */ | ||
235 | + final class Box { | ||
236 | + | ||
237 | + private Step step; | ||
238 | + private int remainingRequirements; | ||
239 | + | ||
240 | + private int absoluteTier = 0; | ||
241 | + private int tier; | ||
242 | + private int depth = 1; | ||
243 | + private int breadth; | ||
244 | + private int center, top; | ||
245 | + | ||
246 | + private Box(Step step, int remainingRequirements) { | ||
247 | + this.step = step; | ||
248 | + this.remainingRequirements = remainingRequirements + 1; | ||
249 | + breadth = step == null || step instanceof Group ? 0 : 1; | ||
250 | + } | ||
251 | + | ||
252 | + private void latchTiers(int absoluteTier, int tier, Group from) { | ||
253 | + this.absoluteTier = Math.max(this.absoluteTier, absoluteTier); | ||
254 | + if (step == null || step.group() == from) { | ||
255 | + this.tier = Math.max(this.tier, tier); | ||
256 | + } | ||
257 | + } | ||
258 | + | ||
259 | + public void latchBreadth(int breadth) { | ||
260 | + this.breadth = Math.max(this.breadth, breadth); | ||
261 | + } | ||
262 | + | ||
263 | + void setTierAndDepth(int absoluteTier, int tier, int depth, Group from) { | ||
264 | + latchTiers(absoluteTier, tier, from); | ||
265 | + this.depth = depth; | ||
266 | + } | ||
267 | + | ||
268 | + boolean visitAndLatchMaxTier(int absoluteTier, int tier, Group from) { | ||
269 | + latchTiers(absoluteTier, tier, from); | ||
270 | + --remainingRequirements; | ||
271 | + return remainingRequirements == 0; | ||
272 | + } | ||
273 | + | ||
274 | + Step step() { | ||
275 | + return step; | ||
276 | + } | ||
277 | + | ||
278 | + public int absoluteTier() { | ||
279 | + return absoluteTier; | ||
280 | + } | ||
281 | + | ||
282 | + int tier() { | ||
283 | + return tier; | ||
284 | + } | ||
285 | + | ||
286 | + int depth() { | ||
287 | + return depth; | ||
288 | + } | ||
289 | + | ||
290 | + int breadth() { | ||
291 | + return breadth; | ||
292 | + } | ||
293 | + | ||
294 | + int top() { | ||
295 | + return top; | ||
296 | + } | ||
297 | + | ||
298 | + int center() { | ||
299 | + return center; | ||
300 | + } | ||
301 | + | ||
302 | + public void updateCenter(int top, int center) { | ||
303 | + this.top = top; | ||
304 | + this.center = center; | ||
305 | + } | ||
306 | + } | ||
307 | +} |
... | @@ -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 | ... | ... |
... | @@ -23,9 +23,10 @@ public interface StepProcessListener { | ... | @@ -23,9 +23,10 @@ public interface StepProcessListener { |
23 | /** | 23 | /** |
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,26 +42,32 @@ class StepProcessor implements Runnable { | ... | @@ -41,26 +42,32 @@ 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. |
50 | * | 53 | * |
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
1 | +{ | ||
2 | + "requirements": [ | ||
3 | + { | ||
4 | + "dst": "Reactive-Forwarding.Ping-2", | ||
5 | + "isSoft": false, | ||
6 | + "src": "Reactive-Forwarding.Link-2-Down" | ||
7 | + }, | ||
8 | + { | ||
9 | + "dst": "Final-Check-Logs-2", | ||
10 | + "isSoft": true, | ||
11 | + "src": "Fetch-Logs-2" | ||
12 | + }, | ||
13 | + { | ||
14 | + "dst": "Host-Intent.Ping-4", | ||
15 | + "isSoft": false, | ||
16 | + "src": "Host-Intent.Link-2-Up" | ||
17 | + }, | ||
18 | + { | ||
19 | + "dst": "Install-1", | ||
20 | + "isSoft": false, | ||
21 | + "src": "Wait-for-Start-1" | ||
22 | + }, | ||
23 | + { | ||
24 | + "dst": "Host-Intent.Link-1-Down", | ||
25 | + "isSoft": false, | ||
26 | + "src": "Host-Intent.Ping-2" | ||
27 | + }, | ||
28 | + { | ||
29 | + "dst": "Host-Intent.Link-2-Up", | ||
30 | + "isSoft": false, | ||
31 | + "src": "Host-Intent.Ping-5" | ||
32 | + }, | ||
33 | + { | ||
34 | + "dst": "Host-Intent.Ping-2", | ||
35 | + "isSoft": false, | ||
36 | + "src": "Host-Intent.Link-2-Down" | ||
37 | + }, | ||
38 | + { | ||
39 | + "dst": "Reinstall-App-With-CLI", | ||
40 | + "isSoft": false, | ||
41 | + "src": "Verify-CLI" | ||
42 | + }, | ||
43 | + { | ||
44 | + "dst": "Create-App-UI-Overlay", | ||
45 | + "isSoft": false, | ||
46 | + "src": "Build-App-With-UI" | ||
47 | + }, | ||
48 | + { | ||
49 | + "dst": "Secure-SSH", | ||
50 | + "isSoft": true, | ||
51 | + "src": "Wait-for-Start-1" | ||
52 | + }, | ||
53 | + { | ||
54 | + "dst": "Pause-For-Masters", | ||
55 | + "isSoft": true, | ||
56 | + "src": "Check-Flows" | ||
57 | + }, | ||
58 | + { | ||
59 | + "dst": "Secure-SSH", | ||
60 | + "isSoft": true, | ||
61 | + "src": "Wait-for-Start-3" | ||
62 | + }, | ||
63 | + { | ||
64 | + "dst": "Uninstall-3", | ||
65 | + "isSoft": false, | ||
66 | + "src": "Kill-3" | ||
67 | + }, | ||
68 | + { | ||
69 | + "dst": "Balance-Masters", | ||
70 | + "isSoft": false, | ||
71 | + "src": "Pause-For-Masters" | ||
72 | + }, | ||
73 | + { | ||
74 | + "dst": "Reactive-Forwarding.Net-Pingall", | ||
75 | + "isSoft": true, | ||
76 | + "src": "Reactive-Forwarding.Net-Link-Down-Up" | ||
77 | + }, | ||
78 | + { | ||
79 | + "dst": "Wait-for-Start-3", | ||
80 | + "isSoft": true, | ||
81 | + "src": "Check-Logs-3" | ||
82 | + }, | ||
83 | + { | ||
84 | + "dst": "Wait-for-Start-2", | ||
85 | + "isSoft": true, | ||
86 | + "src": "Check-Components-2" | ||
87 | + }, | ||
88 | + { | ||
89 | + "dst": "Uninstall-Reactive-Forwarding", | ||
90 | + "isSoft": false, | ||
91 | + "src": "Find-Host-1" | ||
92 | + }, | ||
93 | + { | ||
94 | + "dst": "Wipe-Out-Data-Before", | ||
95 | + "isSoft": true, | ||
96 | + "src": "Initial-Summary-Check" | ||
97 | + }, | ||
98 | + { | ||
99 | + "dst": "Reactive-Forwarding.Ping-3", | ||
100 | + "isSoft": false, | ||
101 | + "src": "Reactive-Forwarding.Link-1-Up" | ||
102 | + }, | ||
103 | + { | ||
104 | + "dst": "Archetypes", | ||
105 | + "isSoft": true, | ||
106 | + "src": "Wrapup" | ||
107 | + }, | ||
108 | + { | ||
109 | + "dst": "Reactive-Forwarding.Ping-4", | ||
110 | + "isSoft": false, | ||
111 | + "src": "Reactive-Forwarding.Link-2-Up" | ||
112 | + }, | ||
113 | + { | ||
114 | + "dst": "Host-Intent-Connectivity", | ||
115 | + "isSoft": true, | ||
116 | + "src": "Net-Teardown" | ||
117 | + }, | ||
118 | + { | ||
119 | + "dst": "Host-Intent.Ping-3", | ||
120 | + "isSoft": false, | ||
121 | + "src": "Host-Intent.Link-1-Up" | ||
122 | + }, | ||
123 | + { | ||
124 | + "dst": "Host-Intent.Ping-1", | ||
125 | + "isSoft": false, | ||
126 | + "src": "Host-Intent.Link-1-Down" | ||
127 | + }, | ||
128 | + { | ||
129 | + "dst": "Install-App", | ||
130 | + "isSoft": false, | ||
131 | + "src": "Create-App-CLI-Overlay" | ||
132 | + }, | ||
133 | + { | ||
134 | + "dst": "Final-Check-Logs-3", | ||
135 | + "isSoft": true, | ||
136 | + "src": "Fetch-Logs-3" | ||
137 | + }, | ||
138 | + { | ||
139 | + "dst": "Install-App", | ||
140 | + "isSoft": false, | ||
141 | + "src": "Verify-App" | ||
142 | + }, | ||
143 | + { | ||
144 | + "dst": "Host-Intent.Link-2-Down", | ||
145 | + "isSoft": false, | ||
146 | + "src": "Host-Intent.Ping-3" | ||
147 | + }, | ||
148 | + { | ||
149 | + "dst": "Prerequisites", | ||
150 | + "isSoft": false, | ||
151 | + "src": "Setup" | ||
152 | + }, | ||
153 | + { | ||
154 | + "dst": "Verify-App", | ||
155 | + "isSoft": true, | ||
156 | + "src": "Reinstall-App-With-CLI" | ||
157 | + }, | ||
158 | + { | ||
159 | + "dst": "Net-Smoke", | ||
160 | + "isSoft": true, | ||
161 | + "src": "Archetypes" | ||
162 | + }, | ||
163 | + { | ||
164 | + "dst": "Setup", | ||
165 | + "isSoft": true, | ||
166 | + "src": "Wrapup" | ||
167 | + }, | ||
168 | + { | ||
169 | + "dst": "Start-Mininet", | ||
170 | + "isSoft": false, | ||
171 | + "src": "Wait-For-Mininet" | ||
172 | + }, | ||
173 | + { | ||
174 | + "dst": "Verify-UI", | ||
175 | + "isSoft": false, | ||
176 | + "src": "Uninstall-App" | ||
177 | + }, | ||
178 | + { | ||
179 | + "dst": "Kill-3", | ||
180 | + "isSoft": false, | ||
181 | + "src": "Install-3" | ||
182 | + }, | ||
183 | + { | ||
184 | + "dst": "Wait-for-Start-1", | ||
185 | + "isSoft": true, | ||
186 | + "src": "Check-Components-1" | ||
187 | + }, | ||
188 | + { | ||
189 | + "dst": "Wait-for-Start-1", | ||
190 | + "isSoft": true, | ||
191 | + "src": "Check-Nodes-1" | ||
192 | + }, | ||
193 | + { | ||
194 | + "dst": "Push-Topos", | ||
195 | + "isSoft": false, | ||
196 | + "src": "Start-Mininet" | ||
197 | + }, | ||
198 | + { | ||
199 | + "dst": "Reactive-Forwarding.Check-Summary-For-Hosts", | ||
200 | + "isSoft": true, | ||
201 | + "src": "Reactive-Forwarding.Config-Topo" | ||
202 | + }, | ||
203 | + { | ||
204 | + "dst": "Reactive-Forwarding.Install-Apps", | ||
205 | + "isSoft": false, | ||
206 | + "src": "Reactive-Forwarding.Check-Apps" | ||
207 | + }, | ||
208 | + { | ||
209 | + "dst": "Push-Bits", | ||
210 | + "isSoft": false, | ||
211 | + "src": "Install-2" | ||
212 | + }, | ||
213 | + { | ||
214 | + "dst": "Install-1", | ||
215 | + "isSoft": false, | ||
216 | + "src": "Secure-SSH" | ||
217 | + }, | ||
218 | + { | ||
219 | + "dst": "Create-Intent", | ||
220 | + "isSoft": false, | ||
221 | + "src": "Host-Intent.Net-Link-Down-Up" | ||
222 | + }, | ||
223 | + { | ||
224 | + "dst": "Verify-CLI", | ||
225 | + "isSoft": true, | ||
226 | + "src": "Reinstall-App-With-UI" | ||
227 | + }, | ||
228 | + { | ||
229 | + "dst": "Wait-for-Start-3", | ||
230 | + "isSoft": true, | ||
231 | + "src": "Check-Apps-3" | ||
232 | + }, | ||
233 | + { | ||
234 | + "dst": "Net-Smoke", | ||
235 | + "isSoft": true, | ||
236 | + "src": "Wrapup" | ||
237 | + }, | ||
238 | + { | ||
239 | + "dst": "Initial-Summary-Check", | ||
240 | + "isSoft": false, | ||
241 | + "src": "Start-Mininet" | ||
242 | + }, | ||
243 | + { | ||
244 | + "dst": "Install-3", | ||
245 | + "isSoft": false, | ||
246 | + "src": "Wait-for-Start-3" | ||
247 | + }, | ||
248 | + { | ||
249 | + "dst": "Reactive-Forwarding.Link-1-Up", | ||
250 | + "isSoft": false, | ||
251 | + "src": "Reactive-Forwarding.Ping-4" | ||
252 | + }, | ||
253 | + { | ||
254 | + "dst": "Check-Summary", | ||
255 | + "isSoft": true, | ||
256 | + "src": "Balance-Masters" | ||
257 | + }, | ||
258 | + { | ||
259 | + "dst": "Reactive-Forwarding.Net-Link-Down-Up", | ||
260 | + "isSoft": true, | ||
261 | + "src": "Host-Intent-Connectivity" | ||
262 | + }, | ||
263 | + { | ||
264 | + "dst": "Secure-SSH", | ||
265 | + "isSoft": true, | ||
266 | + "src": "Wait-for-Start-2" | ||
267 | + }, | ||
268 | + { | ||
269 | + "dst": "Build-App-With-CLI", | ||
270 | + "isSoft": false, | ||
271 | + "src": "Reinstall-App-With-CLI" | ||
272 | + }, | ||
273 | + { | ||
274 | + "dst": "Uninstall-1", | ||
275 | + "isSoft": false, | ||
276 | + "src": "Kill-1" | ||
277 | + }, | ||
278 | + { | ||
279 | + "dst": "Find-Host-1", | ||
280 | + "isSoft": false, | ||
281 | + "src": "Find-Host-2" | ||
282 | + }, | ||
283 | + { | ||
284 | + "dst": "Create-App-CLI-Overlay", | ||
285 | + "isSoft": false, | ||
286 | + "src": "Build-App-With-CLI" | ||
287 | + }, | ||
288 | + { | ||
289 | + "dst": "Net-Setup", | ||
290 | + "isSoft": false, | ||
291 | + "src": "Reactive-Forwarding.Net-Link-Down-Up" | ||
292 | + }, | ||
293 | + { | ||
294 | + "dst": "Kill-2", | ||
295 | + "isSoft": false, | ||
296 | + "src": "Install-2" | ||
297 | + }, | ||
298 | + { | ||
299 | + "dst": "Wait-for-Start-1", | ||
300 | + "isSoft": true, | ||
301 | + "src": "Check-Logs-1" | ||
302 | + }, | ||
303 | + { | ||
304 | + "dst": "Wait-for-Start-2", | ||
305 | + "isSoft": true, | ||
306 | + "src": "Check-Nodes-2" | ||
307 | + }, | ||
308 | + { | ||
309 | + "dst": "Reactive-Forwarding.Ping-All-And-Verify", | ||
310 | + "isSoft": true, | ||
311 | + "src": "Reactive-Forwarding.Check-Summary-For-Hosts" | ||
312 | + }, | ||
313 | + { | ||
314 | + "dst": "Clean-Up", | ||
315 | + "isSoft": false, | ||
316 | + "src": "Create-App" | ||
317 | + }, | ||
318 | + { | ||
319 | + "dst": "Host-Intent.Link-1-Up", | ||
320 | + "isSoft": false, | ||
321 | + "src": "Host-Intent.Ping-4" | ||
322 | + }, | ||
323 | + { | ||
324 | + "dst": "Build-App-With-UI", | ||
325 | + "isSoft": false, | ||
326 | + "src": "Reinstall-App-With-UI" | ||
327 | + }, | ||
328 | + { | ||
329 | + "dst": "Install-2", | ||
330 | + "isSoft": false, | ||
331 | + "src": "Secure-SSH" | ||
332 | + }, | ||
333 | + { | ||
334 | + "dst": "Wait-For-Mininet", | ||
335 | + "isSoft": false, | ||
336 | + "src": "Check-Summary" | ||
337 | + }, | ||
338 | + { | ||
339 | + "dst": "Host-Intent.Net-Link-Down-Up", | ||
340 | + "isSoft": false, | ||
341 | + "src": "Remove-Intent" | ||
342 | + }, | ||
343 | + { | ||
344 | + "dst": "Net-Setup", | ||
345 | + "isSoft": false, | ||
346 | + "src": "Host-Intent-Connectivity" | ||
347 | + }, | ||
348 | + { | ||
349 | + "dst": "Net-Setup", | ||
350 | + "isSoft": false, | ||
351 | + "src": "Reactive-Forwarding.Net-Pingall" | ||
352 | + }, | ||
353 | + { | ||
354 | + "dst": "Reactive-Forwarding.Link-2-Down", | ||
355 | + "isSoft": false, | ||
356 | + "src": "Reactive-Forwarding.Ping-3" | ||
357 | + }, | ||
358 | + { | ||
359 | + "dst": "Find-Host-2", | ||
360 | + "isSoft": false, | ||
361 | + "src": "Create-Intent" | ||
362 | + }, | ||
363 | + { | ||
364 | + "dst": "Wait-for-Start-2", | ||
365 | + "isSoft": true, | ||
366 | + "src": "Check-Apps-2" | ||
367 | + }, | ||
368 | + { | ||
369 | + "dst": "Final-Check-Logs-1", | ||
370 | + "isSoft": true, | ||
371 | + "src": "Fetch-Logs-1" | ||
372 | + }, | ||
373 | + { | ||
374 | + "dst": "Install-2", | ||
375 | + "isSoft": false, | ||
376 | + "src": "Wait-for-Start-2" | ||
377 | + }, | ||
378 | + { | ||
379 | + "dst": "Reactive-Forwarding.Ping-1", | ||
380 | + "isSoft": false, | ||
381 | + "src": "Reactive-Forwarding.Link-1-Down" | ||
382 | + }, | ||
383 | + { | ||
384 | + "dst": "Create-App", | ||
385 | + "isSoft": false, | ||
386 | + "src": "Build-App" | ||
387 | + }, | ||
388 | + { | ||
389 | + "dst": "Check-Summary", | ||
390 | + "isSoft": true, | ||
391 | + "src": "Check-Flows" | ||
392 | + }, | ||
393 | + { | ||
394 | + "dst": "Build-App", | ||
395 | + "isSoft": false, | ||
396 | + "src": "Install-App" | ||
397 | + }, | ||
398 | + { | ||
399 | + "dst": "Reinstall-App-With-UI", | ||
400 | + "isSoft": false, | ||
401 | + "src": "Verify-UI" | ||
402 | + }, | ||
403 | + { | ||
404 | + "dst": "Uninstall-2", | ||
405 | + "isSoft": false, | ||
406 | + "src": "Kill-2" | ||
407 | + }, | ||
408 | + { | ||
409 | + "dst": "Setup", | ||
410 | + "isSoft": false, | ||
411 | + "src": "Archetypes" | ||
412 | + }, | ||
413 | + { | ||
414 | + "dst": "Setup", | ||
415 | + "isSoft": false, | ||
416 | + "src": "Net-Smoke" | ||
417 | + }, | ||
418 | + { | ||
419 | + "dst": "Kill-1", | ||
420 | + "isSoft": false, | ||
421 | + "src": "Install-1" | ||
422 | + }, | ||
423 | + { | ||
424 | + "dst": "Reactive-Forwarding.Link-1-Down", | ||
425 | + "isSoft": false, | ||
426 | + "src": "Reactive-Forwarding.Ping-2" | ||
427 | + }, | ||
428 | + { | ||
429 | + "dst": "Wait-for-Start-2", | ||
430 | + "isSoft": true, | ||
431 | + "src": "Check-Logs-2" | ||
432 | + }, | ||
433 | + { | ||
434 | + "dst": "Wait-for-Start-3", | ||
435 | + "isSoft": true, | ||
436 | + "src": "Check-Components-3" | ||
437 | + }, | ||
438 | + { | ||
439 | + "dst": "Wait-for-Start-3", | ||
440 | + "isSoft": true, | ||
441 | + "src": "Check-Nodes-3" | ||
442 | + }, | ||
443 | + { | ||
444 | + "dst": "Stop-Mininet-If-Needed", | ||
445 | + "isSoft": false, | ||
446 | + "src": "Start-Mininet" | ||
447 | + }, | ||
448 | + { | ||
449 | + "dst": "Reactive-Forwarding.Link-2-Up", | ||
450 | + "isSoft": false, | ||
451 | + "src": "Reactive-Forwarding.Ping-5" | ||
452 | + }, | ||
453 | + { | ||
454 | + "dst": "Reactive-Forwarding.Check-Apps", | ||
455 | + "isSoft": false, | ||
456 | + "src": "Reactive-Forwarding.Ping-All-And-Verify" | ||
457 | + }, | ||
458 | + { | ||
459 | + "dst": "Install-3", | ||
460 | + "isSoft": false, | ||
461 | + "src": "Secure-SSH" | ||
462 | + }, | ||
463 | + { | ||
464 | + "dst": "Push-Bits", | ||
465 | + "isSoft": false, | ||
466 | + "src": "Install-3" | ||
467 | + }, | ||
468 | + { | ||
469 | + "dst": "Reinstall-App-With-CLI", | ||
470 | + "isSoft": false, | ||
471 | + "src": "Create-App-UI-Overlay" | ||
472 | + }, | ||
473 | + { | ||
474 | + "dst": "Push-Bits", | ||
475 | + "isSoft": false, | ||
476 | + "src": "Install-1" | ||
477 | + }, | ||
478 | + { | ||
479 | + "dst": "Wait-for-Start-1", | ||
480 | + "isSoft": true, | ||
481 | + "src": "Check-Apps-1" | ||
482 | + } | ||
483 | + ], | ||
484 | + "steps": [ | ||
485 | + { | ||
486 | + "group": "Net-Setup", | ||
487 | + "isGroup": false, | ||
488 | + "name": "Check-Summary", | ||
489 | + "status": "waiting" | ||
490 | + }, | ||
491 | + { | ||
492 | + "group": "Net-Setup", | ||
493 | + "isGroup": false, | ||
494 | + "name": "Check-Flows", | ||
495 | + "status": "waiting" | ||
496 | + }, | ||
497 | + { | ||
498 | + "group": "Wrapup", | ||
499 | + "isGroup": false, | ||
500 | + "name": "Final-Check-Logs-1", | ||
501 | + "status": "waiting" | ||
502 | + }, | ||
503 | + { | ||
504 | + "group": "Wrapup", | ||
505 | + "isGroup": false, | ||
506 | + "name": "Final-Check-Logs-2", | ||
507 | + "status": "waiting" | ||
508 | + }, | ||
509 | + { | ||
510 | + "group": "Archetypes", | ||
511 | + "isGroup": false, | ||
512 | + "name": "Clean-Up", | ||
513 | + "status": "waiting" | ||
514 | + }, | ||
515 | + { | ||
516 | + "group": "Archetypes", | ||
517 | + "isGroup": false, | ||
518 | + "name": "Build-App-With-UI", | ||
519 | + "status": "waiting" | ||
520 | + }, | ||
521 | + { | ||
522 | + "group": "Archetypes", | ||
523 | + "isGroup": false, | ||
524 | + "name": "Uninstall-App", | ||
525 | + "status": "waiting" | ||
526 | + }, | ||
527 | + { | ||
528 | + "group": "Wrapup", | ||
529 | + "isGroup": false, | ||
530 | + "name": "Final-Check-Logs-3", | ||
531 | + "status": "waiting" | ||
532 | + }, | ||
533 | + { | ||
534 | + "group": "Host-Intent.Net-Link-Down-Up", | ||
535 | + "isGroup": false, | ||
536 | + "name": "Host-Intent.Link-2-Down", | ||
537 | + "status": "waiting" | ||
538 | + }, | ||
539 | + { | ||
540 | + "group": "Wrapup", | ||
541 | + "isGroup": false, | ||
542 | + "name": "Fetch-Logs-3", | ||
543 | + "status": "waiting" | ||
544 | + }, | ||
545 | + { | ||
546 | + "group": "Wrapup", | ||
547 | + "isGroup": false, | ||
548 | + "name": "Fetch-Logs-2", | ||
549 | + "status": "waiting" | ||
550 | + }, | ||
551 | + { | ||
552 | + "group": "Setup", | ||
553 | + "isGroup": false, | ||
554 | + "name": "Check-Components-3", | ||
555 | + "status": "waiting" | ||
556 | + }, | ||
557 | + { | ||
558 | + "group": "Wrapup", | ||
559 | + "isGroup": false, | ||
560 | + "name": "Fetch-Logs-1", | ||
561 | + "status": "waiting" | ||
562 | + }, | ||
563 | + { | ||
564 | + "group": "Net-Setup", | ||
565 | + "isGroup": false, | ||
566 | + "name": "Push-Topos", | ||
567 | + "status": "waiting" | ||
568 | + }, | ||
569 | + { | ||
570 | + "group": "Reactive-Forwarding.Net-Pingall", | ||
571 | + "isGroup": false, | ||
572 | + "name": "Reactive-Forwarding.Check-Apps", | ||
573 | + "status": "waiting" | ||
574 | + }, | ||
575 | + { | ||
576 | + "group": "Setup", | ||
577 | + "isGroup": false, | ||
578 | + "name": "Wait-for-Start-3", | ||
579 | + "status": "waiting" | ||
580 | + }, | ||
581 | + { | ||
582 | + "group": "Setup", | ||
583 | + "isGroup": false, | ||
584 | + "name": "Wait-for-Start-2", | ||
585 | + "status": "waiting" | ||
586 | + }, | ||
587 | + { | ||
588 | + "group": "Setup", | ||
589 | + "isGroup": false, | ||
590 | + "name": "Wait-for-Start-1", | ||
591 | + "status": "waiting" | ||
592 | + }, | ||
593 | + { | ||
594 | + "group": "Net-Smoke", | ||
595 | + "isGroup": true, | ||
596 | + "name": "Host-Intent-Connectivity", | ||
597 | + "status": "waiting" | ||
598 | + }, | ||
599 | + { | ||
600 | + "group": "Host-Intent-Connectivity", | ||
601 | + "isGroup": false, | ||
602 | + "name": "Create-Intent", | ||
603 | + "status": "waiting" | ||
604 | + }, | ||
605 | + { | ||
606 | + "isGroup": true, | ||
607 | + "name": "Prerequisites", | ||
608 | + "status": "in_progress" | ||
609 | + }, | ||
610 | + { | ||
611 | + "group": "Setup", | ||
612 | + "isGroup": false, | ||
613 | + "name": "Push-Bits", | ||
614 | + "status": "waiting" | ||
615 | + }, | ||
616 | + { | ||
617 | + "group": "Setup", | ||
618 | + "isGroup": false, | ||
619 | + "name": "Check-Logs-2", | ||
620 | + "status": "waiting" | ||
621 | + }, | ||
622 | + { | ||
623 | + "group": "Setup", | ||
624 | + "isGroup": false, | ||
625 | + "name": "Check-Logs-3", | ||
626 | + "status": "waiting" | ||
627 | + }, | ||
628 | + { | ||
629 | + "group": "Setup", | ||
630 | + "isGroup": false, | ||
631 | + "name": "Kill-1", | ||
632 | + "status": "waiting" | ||
633 | + }, | ||
634 | + { | ||
635 | + "group": "Setup", | ||
636 | + "isGroup": false, | ||
637 | + "name": "Kill-3", | ||
638 | + "status": "waiting" | ||
639 | + }, | ||
640 | + { | ||
641 | + "group": "Setup", | ||
642 | + "isGroup": false, | ||
643 | + "name": "Kill-2", | ||
644 | + "status": "waiting" | ||
645 | + }, | ||
646 | + { | ||
647 | + "group": "Host-Intent-Connectivity", | ||
648 | + "isGroup": true, | ||
649 | + "name": "Host-Intent.Net-Link-Down-Up", | ||
650 | + "status": "waiting" | ||
651 | + }, | ||
652 | + { | ||
653 | + "group": "Host-Intent.Net-Link-Down-Up", | ||
654 | + "isGroup": false, | ||
655 | + "name": "Host-Intent.Ping-1", | ||
656 | + "status": "waiting" | ||
657 | + }, | ||
658 | + { | ||
659 | + "group": "Archetypes", | ||
660 | + "isGroup": false, | ||
661 | + "name": "Verify-UI", | ||
662 | + "status": "waiting" | ||
663 | + }, | ||
664 | + { | ||
665 | + "group": "Host-Intent.Net-Link-Down-Up", | ||
666 | + "isGroup": false, | ||
667 | + "name": "Host-Intent.Ping-2", | ||
668 | + "status": "waiting" | ||
669 | + }, | ||
670 | + { | ||
671 | + "group": "Host-Intent.Net-Link-Down-Up", | ||
672 | + "isGroup": false, | ||
673 | + "name": "Host-Intent.Ping-3", | ||
674 | + "status": "waiting" | ||
675 | + }, | ||
676 | + { | ||
677 | + "group": "Setup", | ||
678 | + "isGroup": false, | ||
679 | + "name": "Uninstall-1", | ||
680 | + "status": "waiting" | ||
681 | + }, | ||
682 | + { | ||
683 | + "group": "Setup", | ||
684 | + "isGroup": false, | ||
685 | + "name": "Check-Logs-1", | ||
686 | + "status": "waiting" | ||
687 | + }, | ||
688 | + { | ||
689 | + "group": "Host-Intent.Net-Link-Down-Up", | ||
690 | + "isGroup": false, | ||
691 | + "name": "Host-Intent.Ping-4", | ||
692 | + "status": "waiting" | ||
693 | + }, | ||
694 | + { | ||
695 | + "group": "Setup", | ||
696 | + "isGroup": false, | ||
697 | + "name": "Uninstall-3", | ||
698 | + "status": "waiting" | ||
699 | + }, | ||
700 | + { | ||
701 | + "group": "Host-Intent.Net-Link-Down-Up", | ||
702 | + "isGroup": false, | ||
703 | + "name": "Host-Intent.Ping-5", | ||
704 | + "status": "waiting" | ||
705 | + }, | ||
706 | + { | ||
707 | + "group": "Setup", | ||
708 | + "isGroup": false, | ||
709 | + "name": "Uninstall-2", | ||
710 | + "status": "waiting" | ||
711 | + }, | ||
712 | + { | ||
713 | + "group": "Reactive-Forwarding.Net-Pingall", | ||
714 | + "isGroup": false, | ||
715 | + "name": "Reactive-Forwarding.Install-Apps", | ||
716 | + "status": "waiting" | ||
717 | + }, | ||
718 | + { | ||
719 | + "group": "Net-Smoke", | ||
720 | + "isGroup": true, | ||
721 | + "name": "Reactive-Forwarding.Net-Link-Down-Up", | ||
722 | + "status": "waiting" | ||
723 | + }, | ||
724 | + { | ||
725 | + "group": "Prerequisites", | ||
726 | + "isGroup": false, | ||
727 | + "name": "Check-ONOS-Bits", | ||
728 | + "status": "in_progress" | ||
729 | + }, | ||
730 | + { | ||
731 | + "isGroup": true, | ||
732 | + "name": "Wrapup", | ||
733 | + "status": "waiting" | ||
734 | + }, | ||
735 | + { | ||
736 | + "group": "Setup", | ||
737 | + "isGroup": false, | ||
738 | + "name": "Install-2", | ||
739 | + "status": "waiting" | ||
740 | + }, | ||
741 | + { | ||
742 | + "group": "Host-Intent-Connectivity", | ||
743 | + "isGroup": false, | ||
744 | + "name": "Find-Host-1", | ||
745 | + "status": "waiting" | ||
746 | + }, | ||
747 | + { | ||
748 | + "group": "Setup", | ||
749 | + "isGroup": false, | ||
750 | + "name": "Install-1", | ||
751 | + "status": "waiting" | ||
752 | + }, | ||
753 | + { | ||
754 | + "group": "Net-Setup", | ||
755 | + "isGroup": false, | ||
756 | + "name": "Wipe-Out-Data-Before", | ||
757 | + "status": "waiting" | ||
758 | + }, | ||
759 | + { | ||
760 | + "group": "Net-Setup", | ||
761 | + "isGroup": false, | ||
762 | + "name": "Pause-For-Masters", | ||
763 | + "status": "waiting" | ||
764 | + }, | ||
765 | + { | ||
766 | + "group": "Reactive-Forwarding.Net-Link-Down-Up", | ||
767 | + "isGroup": false, | ||
768 | + "name": "Reactive-Forwarding.Link-2-Up", | ||
769 | + "status": "waiting" | ||
770 | + }, | ||
771 | + { | ||
772 | + "group": "Net-Smoke", | ||
773 | + "isGroup": true, | ||
774 | + "name": "Reactive-Forwarding.Net-Pingall", | ||
775 | + "status": "waiting" | ||
776 | + }, | ||
777 | + { | ||
778 | + "group": "Setup", | ||
779 | + "isGroup": false, | ||
780 | + "name": "Check-Components-2", | ||
781 | + "status": "waiting" | ||
782 | + }, | ||
783 | + { | ||
784 | + "group": "Setup", | ||
785 | + "isGroup": false, | ||
786 | + "name": "Check-Components-1", | ||
787 | + "status": "waiting" | ||
788 | + }, | ||
789 | + { | ||
790 | + "group": "Archetypes", | ||
791 | + "isGroup": false, | ||
792 | + "name": "Reinstall-App-With-UI", | ||
793 | + "status": "waiting" | ||
794 | + }, | ||
795 | + { | ||
796 | + "group": "Archetypes", | ||
797 | + "isGroup": false, | ||
798 | + "name": "Reinstall-App-With-CLI", | ||
799 | + "status": "waiting" | ||
800 | + }, | ||
801 | + { | ||
802 | + "group": "Archetypes", | ||
803 | + "isGroup": false, | ||
804 | + "name": "Build-App-With-CLI", | ||
805 | + "status": "waiting" | ||
806 | + }, | ||
807 | + { | ||
808 | + "group": "Host-Intent-Connectivity", | ||
809 | + "isGroup": false, | ||
810 | + "name": "Uninstall-Reactive-Forwarding", | ||
811 | + "status": "waiting" | ||
812 | + }, | ||
813 | + { | ||
814 | + "group": "Host-Intent.Net-Link-Down-Up", | ||
815 | + "isGroup": false, | ||
816 | + "name": "Host-Intent.Link-2-Up", | ||
817 | + "status": "waiting" | ||
818 | + }, | ||
819 | + { | ||
820 | + "group": "Net-Teardown", | ||
821 | + "isGroup": false, | ||
822 | + "name": "Stop-Mininet", | ||
823 | + "status": "waiting" | ||
824 | + }, | ||
825 | + { | ||
826 | + "group": "Reactive-Forwarding.Net-Pingall", | ||
827 | + "isGroup": false, | ||
828 | + "name": "Reactive-Forwarding.Config-Topo", | ||
829 | + "status": "waiting" | ||
830 | + }, | ||
831 | + { | ||
832 | + "group": "Archetypes", | ||
833 | + "isGroup": false, | ||
834 | + "name": "Create-App-CLI-Overlay", | ||
835 | + "status": "waiting" | ||
836 | + }, | ||
837 | + { | ||
838 | + "group": "Reactive-Forwarding.Net-Link-Down-Up", | ||
839 | + "isGroup": false, | ||
840 | + "name": "Reactive-Forwarding.Link-1-Down", | ||
841 | + "status": "waiting" | ||
842 | + }, | ||
843 | + { | ||
844 | + "isGroup": true, | ||
845 | + "name": "Net-Smoke", | ||
846 | + "status": "waiting" | ||
847 | + }, | ||
848 | + { | ||
849 | + "group": "Prerequisites", | ||
850 | + "isGroup": false, | ||
851 | + "name": "Check-Passwordless-Login-2", | ||
852 | + "status": "in_progress" | ||
853 | + }, | ||
854 | + { | ||
855 | + "group": "Prerequisites", | ||
856 | + "isGroup": false, | ||
857 | + "name": "Check-Passwordless-Login-1", | ||
858 | + "status": "in_progress" | ||
859 | + }, | ||
860 | + { | ||
861 | + "group": "Prerequisites", | ||
862 | + "isGroup": false, | ||
863 | + "name": "Check-Passwordless-Login-3", | ||
864 | + "status": "in_progress" | ||
865 | + }, | ||
866 | + { | ||
867 | + "group": "Setup", | ||
868 | + "isGroup": false, | ||
869 | + "name": "Secure-SSH", | ||
870 | + "status": "waiting" | ||
871 | + }, | ||
872 | + { | ||
873 | + "group": "Net-Smoke", | ||
874 | + "isGroup": true, | ||
875 | + "name": "Net-Setup", | ||
876 | + "status": "waiting" | ||
877 | + }, | ||
878 | + { | ||
879 | + "group": "Setup", | ||
880 | + "isGroup": false, | ||
881 | + "name": "Check-Nodes-1", | ||
882 | + "status": "waiting" | ||
883 | + }, | ||
884 | + { | ||
885 | + "group": "Setup", | ||
886 | + "isGroup": false, | ||
887 | + "name": "Install-3", | ||
888 | + "status": "waiting" | ||
889 | + }, | ||
890 | + { | ||
891 | + "group": "Host-Intent-Connectivity", | ||
892 | + "isGroup": false, | ||
893 | + "name": "Find-Host-2", | ||
894 | + "status": "waiting" | ||
895 | + }, | ||
896 | + { | ||
897 | + "group": "Net-Setup", | ||
898 | + "isGroup": false, | ||
899 | + "name": "Initial-Summary-Check", | ||
900 | + "status": "waiting" | ||
901 | + }, | ||
902 | + { | ||
903 | + "group": "Archetypes", | ||
904 | + "isGroup": false, | ||
905 | + "name": "Create-App", | ||
906 | + "status": "waiting" | ||
907 | + }, | ||
908 | + { | ||
909 | + "group": "Setup", | ||
910 | + "isGroup": false, | ||
911 | + "name": "Check-Nodes-3", | ||
912 | + "status": "waiting" | ||
913 | + }, | ||
914 | + { | ||
915 | + "group": "Setup", | ||
916 | + "isGroup": false, | ||
917 | + "name": "Check-Nodes-2", | ||
918 | + "status": "waiting" | ||
919 | + }, | ||
920 | + { | ||
921 | + "group": "Reactive-Forwarding.Net-Link-Down-Up", | ||
922 | + "isGroup": false, | ||
923 | + "name": "Reactive-Forwarding.Link-2-Down", | ||
924 | + "status": "waiting" | ||
925 | + }, | ||
926 | + { | ||
927 | + "isGroup": true, | ||
928 | + "name": "Setup", | ||
929 | + "status": "waiting" | ||
930 | + }, | ||
931 | + { | ||
932 | + "group": "Archetypes", | ||
933 | + "isGroup": false, | ||
934 | + "name": "Verify-App", | ||
935 | + "status": "waiting" | ||
936 | + }, | ||
937 | + { | ||
938 | + "group": "Reactive-Forwarding.Net-Link-Down-Up", | ||
939 | + "isGroup": false, | ||
940 | + "name": "Reactive-Forwarding.Ping-1", | ||
941 | + "status": "waiting" | ||
942 | + }, | ||
943 | + { | ||
944 | + "group": "Reactive-Forwarding.Net-Link-Down-Up", | ||
945 | + "isGroup": false, | ||
946 | + "name": "Reactive-Forwarding.Ping-2", | ||
947 | + "status": "waiting" | ||
948 | + }, | ||
949 | + { | ||
950 | + "group": "Net-Setup", | ||
951 | + "isGroup": false, | ||
952 | + "name": "Start-Mininet", | ||
953 | + "status": "waiting" | ||
954 | + }, | ||
955 | + { | ||
956 | + "group": "Reactive-Forwarding.Net-Link-Down-Up", | ||
957 | + "isGroup": false, | ||
958 | + "name": "Reactive-Forwarding.Ping-3", | ||
959 | + "status": "waiting" | ||
960 | + }, | ||
961 | + { | ||
962 | + "group": "Reactive-Forwarding.Net-Link-Down-Up", | ||
963 | + "isGroup": false, | ||
964 | + "name": "Reactive-Forwarding.Ping-4", | ||
965 | + "status": "waiting" | ||
966 | + }, | ||
967 | + { | ||
968 | + "group": "Reactive-Forwarding.Net-Link-Down-Up", | ||
969 | + "isGroup": false, | ||
970 | + "name": "Reactive-Forwarding.Ping-5", | ||
971 | + "status": "waiting" | ||
972 | + }, | ||
973 | + { | ||
974 | + "group": "Archetypes", | ||
975 | + "isGroup": false, | ||
976 | + "name": "Verify-CLI", | ||
977 | + "status": "waiting" | ||
978 | + }, | ||
979 | + { | ||
980 | + "group": "Reactive-Forwarding.Net-Pingall", | ||
981 | + "isGroup": false, | ||
982 | + "name": "Reactive-Forwarding.Check-Summary-For-Hosts", | ||
983 | + "status": "waiting" | ||
984 | + }, | ||
985 | + { | ||
986 | + "group": "Net-Smoke", | ||
987 | + "isGroup": true, | ||
988 | + "name": "Net-Teardown", | ||
989 | + "status": "waiting" | ||
990 | + }, | ||
991 | + { | ||
992 | + "group": "Host-Intent.Net-Link-Down-Up", | ||
993 | + "isGroup": false, | ||
994 | + "name": "Host-Intent.Link-1-Up", | ||
995 | + "status": "waiting" | ||
996 | + }, | ||
997 | + { | ||
998 | + "group": "Host-Intent-Connectivity", | ||
999 | + "isGroup": false, | ||
1000 | + "name": "Remove-Intent", | ||
1001 | + "status": "waiting" | ||
1002 | + }, | ||
1003 | + { | ||
1004 | + "group": "Archetypes", | ||
1005 | + "isGroup": false, | ||
1006 | + "name": "Install-App", | ||
1007 | + "status": "waiting" | ||
1008 | + }, | ||
1009 | + { | ||
1010 | + "group": "Archetypes", | ||
1011 | + "isGroup": false, | ||
1012 | + "name": "Create-App-UI-Overlay", | ||
1013 | + "status": "waiting" | ||
1014 | + }, | ||
1015 | + { | ||
1016 | + "group": "Reactive-Forwarding.Net-Link-Down-Up", | ||
1017 | + "isGroup": false, | ||
1018 | + "name": "Reactive-Forwarding.Link-1-Up", | ||
1019 | + "status": "waiting" | ||
1020 | + }, | ||
1021 | + { | ||
1022 | + "group": "Net-Setup", | ||
1023 | + "isGroup": false, | ||
1024 | + "name": "Wait-For-Mininet", | ||
1025 | + "status": "waiting" | ||
1026 | + }, | ||
1027 | + { | ||
1028 | + "group": "Setup", | ||
1029 | + "isGroup": false, | ||
1030 | + "name": "Check-Apps-3", | ||
1031 | + "status": "waiting" | ||
1032 | + }, | ||
1033 | + { | ||
1034 | + "group": "Setup", | ||
1035 | + "isGroup": false, | ||
1036 | + "name": "Check-Apps-2", | ||
1037 | + "status": "waiting" | ||
1038 | + }, | ||
1039 | + { | ||
1040 | + "group": "Setup", | ||
1041 | + "isGroup": false, | ||
1042 | + "name": "Check-Apps-1", | ||
1043 | + "status": "waiting" | ||
1044 | + }, | ||
1045 | + { | ||
1046 | + "group": "Net-Setup", | ||
1047 | + "isGroup": false, | ||
1048 | + "name": "Stop-Mininet-If-Needed", | ||
1049 | + "status": "waiting" | ||
1050 | + }, | ||
1051 | + { | ||
1052 | + "group": "Prerequisites", | ||
1053 | + "isGroup": false, | ||
1054 | + "name": "Check-Environment", | ||
1055 | + "status": "in_progress" | ||
1056 | + }, | ||
1057 | + { | ||
1058 | + "isGroup": true, | ||
1059 | + "name": "Archetypes", | ||
1060 | + "status": "waiting" | ||
1061 | + }, | ||
1062 | + { | ||
1063 | + "group": "Host-Intent.Net-Link-Down-Up", | ||
1064 | + "isGroup": false, | ||
1065 | + "name": "Host-Intent.Link-1-Down", | ||
1066 | + "status": "waiting" | ||
1067 | + }, | ||
1068 | + { | ||
1069 | + "group": "Net-Setup", | ||
1070 | + "isGroup": false, | ||
1071 | + "name": "Balance-Masters", | ||
1072 | + "status": "waiting" | ||
1073 | + }, | ||
1074 | + { | ||
1075 | + "group": "Reactive-Forwarding.Net-Pingall", | ||
1076 | + "isGroup": false, | ||
1077 | + "name": "Reactive-Forwarding.Ping-All-And-Verify", | ||
1078 | + "status": "waiting" | ||
1079 | + }, | ||
1080 | + { | ||
1081 | + "group": "Archetypes", | ||
1082 | + "isGroup": false, | ||
1083 | + "name": "Build-App", | ||
1084 | + "status": "waiting" | ||
1085 | + } | ||
1086 | + ] | ||
1087 | +} |
... | @@ -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 | } | 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; | ||
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