summaryrefslogtreecommitdiffstats
path: root/framework/src/onos/utils/stc/src/main/java/org/onlab/stc/Compiler.java
diff options
context:
space:
mode:
Diffstat (limited to 'framework/src/onos/utils/stc/src/main/java/org/onlab/stc/Compiler.java')
-rw-r--r--framework/src/onos/utils/stc/src/main/java/org/onlab/stc/Compiler.java541
1 files changed, 0 insertions, 541 deletions
diff --git a/framework/src/onos/utils/stc/src/main/java/org/onlab/stc/Compiler.java b/framework/src/onos/utils/stc/src/main/java/org/onlab/stc/Compiler.java
deleted file mode 100644
index 919cbd5b..00000000
--- a/framework/src/onos/utils/stc/src/main/java/org/onlab/stc/Compiler.java
+++ /dev/null
@@ -1,541 +0,0 @@
-/*
- * Copyright 2015 Open Networking Laboratory
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.onlab.stc;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Maps;
-import com.google.common.collect.Sets;
-import org.apache.commons.configuration.HierarchicalConfiguration;
-import org.onlab.graph.DepthFirstSearch;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-import static com.google.common.base.Preconditions.*;
-import static com.google.common.base.Strings.isNullOrEmpty;
-import static org.onlab.graph.DepthFirstSearch.EdgeType.BACK_EDGE;
-import static org.onlab.graph.GraphPathSearch.ALL_PATHS;
-import static org.onlab.stc.Scenario.loadScenario;
-
-/**
- * Entity responsible for loading a scenario and producing a redy-to-execute
- * process flow graph.
- */
-public class Compiler {
-
- private static final String DEFAULT_LOG_DIR = "${WORKSPACE}/tmp/stc/";
-
- private static final String IMPORT = "import";
- private static final String GROUP = "group";
- private static final String STEP = "step";
- private static final String PARALLEL = "parallel";
- private static final String SEQUENTIAL = "sequential";
- private static final String DEPENDENCY = "dependency";
-
- private static final String LOG_DIR = "[@logDir]";
- private static final String NAME = "[@name]";
- private static final String COMMAND = "[@exec]";
- private static final String ENV = "[@env]";
- private static final String CWD = "[@cwd]";
- private static final String REQUIRES = "[@requires]";
- private static final String IF = "[@if]";
- private static final String UNLESS = "[@unless]";
- private static final String VAR = "[@var]";
- private static final String STARTS = "[@starts]";
- private static final String ENDS = "[@ends]";
- private static final String FILE = "[@file]";
- private static final String NAMESPACE = "[@namespace]";
-
- static final String PROP_START = "${";
- static final String PROP_END = "}";
-
- private static final String HASH = "#";
- private static final String HASH_PREV = "#-1";
-
- private final Scenario scenario;
-
- private final Map<String, Step> steps = Maps.newHashMap();
- private final Map<String, Step> inactiveSteps = Maps.newHashMap();
- private final Map<String, String> requirements = Maps.newHashMap();
- private final Set<Dependency> dependencies = Sets.newHashSet();
- private final List<Integer> clonables = Lists.newArrayList();
-
- private ProcessFlow processFlow;
- private File logDir;
-
- private String previous = null;
- private String pfx = "";
- private boolean debugOn = System.getenv("debug") != null;
-
- /**
- * Creates a new compiler for the specified scenario.
- *
- * @param scenario scenario to be compiled
- */
- public Compiler(Scenario scenario) {
- this.scenario = scenario;
- }
-
- /**
- * Returns the scenario being compiled.
- *
- * @return test scenario
- */
- public Scenario scenario() {
- return scenario;
- }
-
- /**
- * Compiles the specified scenario to produce a final process flow graph.
- */
- public void compile() {
- compile(scenario.definition(), null, null);
- compileRequirements();
-
- // Produce the process flow
- processFlow = new ProcessFlow(ImmutableSet.copyOf(steps.values()),
- ImmutableSet.copyOf(dependencies));
-
- scanForCycles();
-
- // Extract the log directory if there was one specified
- String defaultPath = DEFAULT_LOG_DIR + scenario.name();
- String path = scenario.definition().getString(LOG_DIR, defaultPath);
- logDir = new File(expand(path));
- }
-
- /**
- * Returns the step with the specified name.
- *
- * @param name step or group name
- * @return test step or group
- */
- public Step getStep(String name) {
- return steps.get(name);
- }
-
- /**
- * Returns the process flow generated from this scenario definition.
- *
- * @return process flow as a graph
- */
- public ProcessFlow processFlow() {
- return processFlow;
- }
-
- /**
- * Returns the log directory where scenario logs should be kept.
- *
- * @return scenario logs directory
- */
- public File logDir() {
- return logDir;
- }
-
- /**
- * Recursively elaborates this definition to produce a final process flow graph.
- *
- * @param cfg hierarchical definition
- * @param namespace optional namespace
- * @param parentGroup optional parent group
- */
- private void compile(HierarchicalConfiguration cfg,
- String namespace, Group parentGroup) {
- String opfx = pfx;
- pfx = pfx + ">";
- print("pfx=%s namespace=%s", pfx, namespace);
-
- // Scan all imports
- cfg.configurationsAt(IMPORT)
- .forEach(c -> processImport(c, namespace, parentGroup));
-
- // Scan all steps
- cfg.configurationsAt(STEP)
- .forEach(c -> processStep(c, namespace, parentGroup));
-
- // Scan all groups
- cfg.configurationsAt(GROUP)
- .forEach(c -> processGroup(c, namespace, parentGroup));
-
- // Scan all parallel groups
- cfg.configurationsAt(PARALLEL)
- .forEach(c -> processParallelGroup(c, namespace, parentGroup));
-
- // Scan all sequential groups
- cfg.configurationsAt(SEQUENTIAL)
- .forEach(c -> processSequentialGroup(c, namespace, parentGroup));
-
- // Scan all dependencies
- cfg.configurationsAt(DEPENDENCY)
- .forEach(c -> processDependency(c, namespace));
-
- pfx = opfx;
- }
-
- /**
- * Compiles requirements for all steps and groups accrued during the
- * overall compilation process.
- */
- private void compileRequirements() {
- requirements.forEach((name, requires) ->
- compileRequirements(getStep(name), requires));
- }
-
- private void compileRequirements(Step src, String requires) {
- split(requires).forEach(n -> {
- boolean isSoft = n.startsWith("~");
- String name = n.replaceFirst("^~", "");
- Step dst = getStep(name);
- if (dst != null) {
- dependencies.add(new Dependency(src, dst, isSoft));
- }
- });
- }
-
- /**
- * Processes an import directive.
- *
- * @param cfg hierarchical definition
- * @param namespace optional namespace
- * @param parentGroup optional parent group
- */
- private void processImport(HierarchicalConfiguration cfg,
- String namespace, Group parentGroup) {
- String file = checkNotNull(expand(cfg.getString(FILE)),
- "Import directive must specify 'file'");
- String newNamespace = expand(prefix(cfg.getString(NAMESPACE), namespace));
- print("import file=%s namespace=%s", file, newNamespace);
- try {
- Scenario importScenario = loadScenario(new FileInputStream(file));
- compile(importScenario.definition(), newNamespace, parentGroup);
- } catch (IOException e) {
- throw new IllegalArgumentException("Unable to import scenario", e);
- }
- }
-
- /**
- * Processes a step directive.
- *
- * @param cfg hierarchical definition
- * @param namespace optional namespace
- * @param parentGroup optional parent group
- */
- private void processStep(HierarchicalConfiguration cfg,
- String namespace, Group parentGroup) {
- String name = expand(prefix(cfg.getString(NAME), namespace));
- String command = expand(cfg.getString(COMMAND, parentGroup != null ? parentGroup.command() : null), true);
- String env = expand(cfg.getString(ENV, parentGroup != null ? parentGroup.env() : null));
- String cwd = expand(cfg.getString(CWD, parentGroup != null ? parentGroup.cwd() : null));
-
- print("step name=%s command=%s env=%s cwd=%s", name, command, env, cwd);
- Step step = new Step(name, command, env, cwd, parentGroup);
- registerStep(step, cfg, namespace, parentGroup);
- }
-
- /**
- * Processes a group directive.
- *
- * @param cfg hierarchical definition
- * @param namespace optional namespace
- * @param parentGroup optional parent group
- */
- private void processGroup(HierarchicalConfiguration cfg,
- String namespace, Group parentGroup) {
- String name = expand(prefix(cfg.getString(NAME), namespace));
- String command = expand(cfg.getString(COMMAND, parentGroup != null ? parentGroup.command() : null), true);
- String env = expand(cfg.getString(ENV, parentGroup != null ? parentGroup.env() : null));
- String cwd = expand(cfg.getString(CWD, parentGroup != null ? parentGroup.cwd() : null));
-
- print("group name=%s command=%s env=%s cwd=%s", name, command, env, cwd);
- Group group = new Group(name, command, env, cwd, parentGroup);
- if (registerStep(group, cfg, namespace, parentGroup)) {
- compile(cfg, namespace, group);
- }
- }
-
- /**
- * Registers the specified step or group.
- *
- * @param step step or group
- * @param cfg hierarchical definition
- * @param namespace optional namespace
- * @param parentGroup optional parent group
- * @return true of the step or group was registered as active
- */
- private boolean registerStep(Step step, HierarchicalConfiguration cfg,
- String namespace, Group parentGroup) {
- checkState(!steps.containsKey(step.name()), "Step %s already exists", step.name());
- String ifClause = expand(cfg.getString(IF));
- String unlessClause = expand(cfg.getString(UNLESS));
-
- if ((ifClause != null && ifClause.length() == 0) ||
- (unlessClause != null && unlessClause.length() > 0) ||
- (parentGroup != null && inactiveSteps.containsValue(parentGroup))) {
- inactiveSteps.put(step.name(), step);
- return false;
- }
-
- if (parentGroup != null) {
- parentGroup.addChild(step);
- }
-
- steps.put(step.name(), step);
- processRequirements(step, expand(cfg.getString(REQUIRES)), namespace);
- previous = step.name();
- return true;
- }
-
- /**
- * Processes a parallel clone group directive.
- *
- * @param cfg hierarchical definition
- * @param namespace optional namespace
- * @param parentGroup optional parent group
- */
- private void processParallelGroup(HierarchicalConfiguration cfg,
- String namespace, Group parentGroup) {
- String var = cfg.getString(VAR);
- print("parallel var=%s", var);
-
- int i = 1;
- while (condition(var, i).length() > 0) {
- clonables.add(0, i);
- compile(cfg, namespace, parentGroup);
- clonables.remove(0);
- i++;
- }
- }
-
- /**
- * Processes a sequential clone group directive.
- *
- * @param cfg hierarchical definition
- * @param namespace optional namespace
- * @param parentGroup optional parent group
- */
- private void processSequentialGroup(HierarchicalConfiguration cfg,
- String namespace, Group parentGroup) {
- String var = cfg.getString(VAR);
- String starts = cfg.getString(STARTS);
- String ends = cfg.getString(ENDS);
- print("sequential var=%s", var);
-
- int i = 1;
- while (condition(var, i).length() > 0) {
- clonables.add(0, i);
- compile(cfg, namespace, parentGroup);
- if (i > 1) {
- processSequentialRequirements(starts, ends, namespace);
- }
- clonables.remove(0);
- i++;
- }
- }
-
- /**
- * Hooks starts of this sequence tier to the previous tier.
- *
- * @param starts comma-separated list of start steps
- * @param ends comma-separated list of end steps
- * @param namespace optional namespace
- */
- private void processSequentialRequirements(String starts, String ends,
- String namespace) {
- for (String s : split(starts)) {
- String start = expand(prefix(s, namespace));
- String reqs = requirements.get(s);
- for (String n : split(ends)) {
- boolean isSoft = n.startsWith("~");
- String name = n.replaceFirst("^~", "");
- name = (isSoft ? "~" : "") + expand(prefix(name, namespace));
- reqs = reqs == null ? name : (reqs + "," + name);
- }
- requirements.put(start, reqs);
- }
- }
-
- /**
- * Returns the elaborated repetition construct conditional.
- *
- * @param var repetition var property
- * @param i index to elaborate
- * @return elaborated string
- */
- private String condition(String var, Integer i) {
- return expand(var.replaceFirst("#", i.toString())).trim();
- }
-
- /**
- * Processes a dependency directive.
- *
- * @param cfg hierarchical definition
- * @param namespace optional namespace
- */
- private void processDependency(HierarchicalConfiguration cfg, String namespace) {
- String name = expand(prefix(cfg.getString(NAME), namespace));
- String requires = expand(cfg.getString(REQUIRES));
-
- print("dependency name=%s requires=%s", name, requires);
- Step step = getStep(name, namespace);
- if (!inactiveSteps.containsValue(step)) {
- processRequirements(step, requires, namespace);
- }
- }
-
- /**
- * Processes the specified requiremenst string and adds dependency for
- * each requirement of the given step.
- *
- * @param src source step
- * @param requires comma-separated list of required steps
- * @param namespace optional namespace
- */
- private void processRequirements(Step src, String requires, String namespace) {
- String reqs = requirements.get(src.name());
- for (String n : split(requires)) {
- boolean isSoft = n.startsWith("~");
- String name = n.replaceFirst("^~", "");
- name = previous != null && name.equals("^") ? previous : name;
- name = (isSoft ? "~" : "") + expand(prefix(name, namespace));
- reqs = reqs == null ? name : (reqs + "," + name);
- }
- requirements.put(src.name(), reqs);
- }
-
- /**
- * Retrieves the step or group with the specified name.
- *
- * @param name step or group name
- * @param namespace optional namespace
- * @return step or group; null if none found in active or inactive steps
- */
- private Step getStep(String name, String namespace) {
- String dName = prefix(name, namespace);
- Step step = steps.get(dName);
- step = step != null ? step : inactiveSteps.get(dName);
- checkArgument(step != null, "Unknown step %s", dName);
- return step;
- }
-
- /**
- * Prefixes the specified name with the given namespace.
- *
- * @param name name of a step or a group
- * @param namespace optional namespace
- * @return composite name
- */
- private String prefix(String name, String namespace) {
- return isNullOrEmpty(namespace) ? name : namespace + "." + name;
- }
-
- /**
- * Expands any environment variables in the specified string. These are
- * specified as ${property} tokens.
- *
- * @param string string to be processed
- * @param keepTokens true if the original unresolved tokens should be kept
- * @return original string with expanded substitutions
- */
- private String expand(String string, boolean... keepTokens) {
- if (string == null) {
- return null;
- }
-
- String pString = string;
- StringBuilder sb = new StringBuilder();
- int start, end, last = 0;
- while ((start = pString.indexOf(PROP_START, last)) >= 0) {
- end = pString.indexOf(PROP_END, start + PROP_START.length());
- checkArgument(end > start, "Malformed property in %s", pString);
- sb.append(pString.substring(last, start));
- String prop = pString.substring(start + PROP_START.length(), end);
- String value;
- if (prop.equals(HASH)) {
- value = Integer.toString(clonables.get(0));
- } else if (prop.equals(HASH_PREV)) {
- value = Integer.toString(clonables.get(0) - 1);
- } else if (prop.endsWith(HASH)) {
- pString = pString.replaceFirst("#}", clonables.get(0) + "}");
- last = start;
- continue;
- } else {
- // Try system property first, then fall back to env. variable.
- value = System.getProperty(prop);
- if (value == null) {
- value = System.getenv(prop);
- }
- }
- if (value == null && keepTokens.length == 1 && keepTokens[0]) {
- sb.append("${").append(prop).append("}");
- } else {
- sb.append(value != null ? value : "");
- }
- last = end + 1;
- }
- sb.append(pString.substring(last));
- return sb.toString().replace('\n', ' ').replace('\r', ' ');
- }
-
- /**
- * Splits the comma-separated string into a list of strings.
- *
- * @param string string to split
- * @return list of strings
- */
- private List<String> split(String string) {
- ImmutableList.Builder<String> builder = ImmutableList.builder();
- String[] fields = string != null ? string.split(",") : new String[0];
- for (String field : fields) {
- builder.add(field.trim());
- }
- return builder.build();
- }
-
- /**
- * Scans the process flow graph for cyclic dependencies.
- */
- private void scanForCycles() {
- DepthFirstSearch<Step, Dependency> dfs = new DepthFirstSearch<>();
- // Use a brute-force method of searching paths from all vertices.
- processFlow().getVertexes().forEach(s -> {
- DepthFirstSearch<Step, Dependency>.SpanningTreeResult r =
- dfs.search(processFlow, s, null, null, ALL_PATHS);
- r.edges().forEach((e, et) -> checkArgument(et != BACK_EDGE,
- "Process flow has a cycle involving dependency from %s to %s",
- e.src().name, e.dst().name));
- });
- }
-
-
- /**
- * Prints formatted output.
- *
- * @param format printf format string
- * @param args arguments to be printed
- */
- private void print(String format, Object... args) {
- if (debugOn) {
- System.err.println(pfx + String.format(format, args));
- }
- }
-
-}