support named steps in named scenarios

This commit is contained in:
Jonathan Shook 2020-04-30 16:50:35 -05:00
parent cc5b57ebd9
commit 579a3c1ec2
4 changed files with 102 additions and 55 deletions

View File

@ -4,26 +4,30 @@ import java.util.*;
public class RawScenarios extends LinkedHashMap<String, LinkedList<String>> {
public static String STEPNAME = "%03d";
public List<String> getScenarioNames() {
return new LinkedList<>(this.keySet());
}
public List<String> getNamedScenario(String scenarioName) {
public Map<String,String> getNamedScenario(String scenarioName) {
Object v = this.get(scenarioName);
if (v==null) { return null; }
// Yes this looks strange. Yes it will work. SnakeYaml and generics are a bad combo.
if (v instanceof List) {
return (List<String>) v;
} else if (v instanceof CharSequence) {
return List.of(v.toString());
} else if (v instanceof Map) {
Object[] o = ((Map) v).values().toArray();
ArrayList<String> strings = new ArrayList<>(o.length);
for (Object o1 : o) {
strings.add(o1.toString());
List<String> list = (List<String>) v;
Map<String,String> map = new LinkedHashMap<>();
for (int i = 0; i < list.size(); i++) {
map.put(String.format(STEPNAME,i),list.get(i));
}
return strings;
return map;
} else if (v instanceof CharSequence) {
return Map.of(String.format(STEPNAME,1),v.toString());
} else if (v instanceof Map) {
return ((Map)v);
} else {
throw new RuntimeException("Unknown type while access raw named scenarios data: " + v.getClass().getCanonicalName());
}

View File

@ -3,8 +3,10 @@ package io.nosqlbench.engine.api.activityconfig.yaml;
import io.nosqlbench.engine.api.activityconfig.rawyaml.RawScenarios;
import java.util.List;
import java.util.Map;
public class Scenarios {
private RawScenarios rawScenarios;
public Scenarios(RawScenarios rawScenarios) {
@ -15,7 +17,7 @@ public class Scenarios {
return rawScenarios.getScenarioNames();
}
public List<String> getNamedScenario(String scenarioName) {
public Map<String,String> getNamedScenario(String scenarioName) {
return rawScenarios.getNamedScenario(scenarioName);
}
}

View File

@ -28,6 +28,7 @@ public class NBCLIScenarioParser {
private final static Logger logger = LoggerFactory.getLogger(NBCLIScenarioParser.class);
private static final String SEARCH_IN = "activities";
public static final String WORKLOAD_SCENARIO_STEP = "WORKLOAD_SCENARIO_STEP";
public static boolean isFoundWorkload(String workload,
String... includes) {
@ -84,7 +85,7 @@ public class NBCLIScenarioParser {
StrInterpolator userParamsInterp = new StrInterpolator(userParams);
// This will hold the command to be prepended to the main arglist
// This will buffer the new command before adding it to the main arg list
LinkedList<String> buildCmdBuffer = new LinkedList<>();
for (String scenarioName : scenarioNames) {
@ -97,63 +98,74 @@ public class NBCLIScenarioParser {
.extension("yaml")
.one();
// // TODO: ugly hack remove this
// workloadName = (workloadName.endsWith(".yaml")) ? workloadName : workloadName + ".yaml";
// StmtsDocList stmts = StatementsLoader.load(logger, workloadName, SEARCH_IN);
StmtsDocList stmts = StatementsLoader.load(logger,yamlWithNamedScenarios);
StmtsDocList stmts = StatementsLoader.load(logger, yamlWithNamedScenarios);
Scenarios scenarios = stmts.getDocScenarios();
List<String> cmds = scenarios.getNamedScenario(scenarioName);
if (cmds == null) {
Map<String, String> namedSteps = scenarios.getNamedScenario(scenarioName);
if (namedSteps == null) {
throw new BasicError("Unable to find named scenario '" + scenarioName + "' in workload '" + workloadName
+ "', but you can pick from " + String.join(",", scenarios.getScenarioNames()));
}
Pattern cmdpattern = Pattern.compile("(?<name>\\w+)((?<oper>=+)(?<val>.+))?");
for (String cmd : cmds) { // each command line of the named scenario
// each named command line step of the named scenario
for (Map.Entry<String, String> cmdEntry : namedSteps.entrySet()) {
String stepName = cmdEntry.getKey();
String cmd = cmdEntry.getValue();
cmd = userParamsInterp.apply(cmd);
LinkedHashMap<String, CmdArg> parsedStep = parseStep(cmd);
LinkedHashMap<String, String> usersCopy = new LinkedHashMap<>(userParams);
LinkedHashMap<String, CmdArg> cmdline = new LinkedHashMap<>();
LinkedHashMap<String, String> buildingCmd = new LinkedHashMap<>();
String[] cmdparts = cmd.split(" ");
for (String cmdpart : cmdparts) {
Matcher matcher = cmdpattern.matcher(cmdpart);
if (!matcher.matches()) {
throw new BasicError("Unable to recognize scenario cmd spec in '" + cmdpart + "'");
}
String name = Synonyms.canonicalize(matcher.group("name"), logger);
String oper = matcher.group("oper");
String val = matcher.group("val");
cmdline.put(name, new CmdArg(name, oper, val));
}
// consume each of the parameters from the steps to produce a composited command
// order is primarily based on the step template, then on user-provided parameters
for (CmdArg cmdarg : parsedStep.values()) {
LinkedHashMap<String, String> builtcmd = new LinkedHashMap<>();
for (CmdArg cmdarg : cmdline.values()) {
// allow user provided parameter values to override those in the template,
// if the assignment operator used in the template allows for it
if (usersCopy.containsKey(cmdarg.getName())) {
cmdarg = cmdarg.override(usersCopy.remove(cmdarg.getName()));
}
builtcmd.put(cmdarg.getName(), cmdarg.toString());
}
usersCopy.forEach((k, v) -> builtcmd.put(k, k + "=" + v));
if (!builtcmd.containsKey("workload")) {
builtcmd.put("workload", "workload=" + workloadName);
buildingCmd.put(cmdarg.getName(), cmdarg.toString());
}
usersCopy.forEach((k, v) -> buildingCmd.put(k, k + "=" + v));
// Undefine any keys with a value of 'undef'
List<String> undefKeys = builtcmd.entrySet()
List<String> undefKeys = buildingCmd.entrySet()
.stream()
.filter(e -> e.getValue().toLowerCase().endsWith("=undef"))
.map(Map.Entry::getKey)
.collect(Collectors.toList());
undefKeys.forEach(builtcmd::remove);
undefKeys.forEach(buildingCmd::remove);
logger.debug("Named scenario built command: " + String.join(" ", builtcmd.values()));
buildCmdBuffer.addAll(builtcmd.values());
if (!buildingCmd.containsKey("workload")) {
buildingCmd.put("workload", "workload=" + workloadName);
}
if (!buildingCmd.containsKey("alias")) {
buildingCmd.put("alias", WORKLOAD_SCENARIO_STEP);
}
String alias = buildingCmd.get("alias");
for (String token : new String[]{"WORKLOAD", "SCENARIO", "STEP"}) {
if (!alias.contains(token)) {
logger.warn("Your alias template '" + alias + "' does not contain " + token + ", which will " +
"cause your metrics to be combined under the same name. It is strongly advised that you " +
"include them in a template like " + WORKLOAD_SCENARIO_STEP + ".");
}
}
alias = alias.replaceAll("WORKLOAD", workloadContent.asPath().getFileName().toString().replaceAll(
".yaml",""));
alias = alias.replaceAll("SCENARIO", scenarioName);
alias = alias.replaceAll("STEP", stepName);
buildingCmd.put("alias", "alias="+alias);
logger.debug("Named scenario built command: " + String.join(" ", buildingCmd.values()));
buildCmdBuffer.addAll(buildingCmd.values());
}
}
@ -161,6 +173,26 @@ public class NBCLIScenarioParser {
}
private static final Pattern WordAndMaybeAssignment = Pattern.compile("(?<name>\\w+)((?<oper>=+)(?<val>.+))?");
private static LinkedHashMap<String, CmdArg> parseStep(String cmd) {
LinkedHashMap<String, CmdArg> parsedStep = new LinkedHashMap<>();
String[] namedStepPieces = cmd.split(" ");
for (String commandFragment : namedStepPieces) {
Matcher matcher = WordAndMaybeAssignment.matcher(commandFragment);
if (!matcher.matches()) {
throw new BasicError("Unable to recognize scenario cmd spec in '" + commandFragment + "'");
}
String commandName = matcher.group("name");
commandName = Synonyms.canonicalize(commandName, logger);
String assignmentOp = matcher.group("oper");
String assignedValue = matcher.group("val");
parsedStep.put(commandName, new CmdArg(commandName, assignmentOp, assignedValue));
}
return parsedStep;
}
private final static class CmdArg {
private final String name;
private final String operator;
@ -176,9 +208,11 @@ public class NBCLIScenarioParser {
public boolean isReassignable() {
return UNLOCKED.equals(operator);
}
public boolean isFinalSilent() {
return SILENT_LOCKED.equals(operator);
}
public boolean isFinalVerbose() {
return VERBOSE_LOCKED.equals(operator);
}
@ -298,7 +332,7 @@ public class NBCLIScenarioParser {
.name(referenced).extension("yaml")
.one();
StmtsDocList stmts = StatementsLoader.load(logger,content);
StmtsDocList stmts = StatementsLoader.load(logger, content);
Map<String, String> templates = new HashMap<>();
try {
@ -315,7 +349,7 @@ public class NBCLIScenarioParser {
List<String> scenarioNames = scenarios.getScenarioNames();
if (scenarioNames != null && scenarioNames.size() >0){
if (scenarioNames != null && scenarioNames.size() > 0) {
String path = yamlPath.toString();
path = path.startsWith(FileSystems.getDefault().getSeparator()) ? path.substring(1) : path;
workloadDescriptions.add(new WorkloadDesc(path, scenarioNames, templates));
@ -340,7 +374,7 @@ public class NBCLIScenarioParser {
//We want the outer name with the inner default value
templates.put(matchArray[0], innerMatch[1]);
}else{
} else {
templates.put(matchArray[0], matchArray[1]);
}
}
@ -349,7 +383,7 @@ public class NBCLIScenarioParser {
while (matcher.find()) {
String match = matcher.group(1);
String[] matchArray = match.split(":");
templates.put(matchArray[0],matchArray[1]);
templates.put(matchArray[0], matchArray[1]);
}
return templates;
}

View File

@ -22,6 +22,7 @@ import org.slf4j.LoggerFactory;
import org.junit.Test;
import java.util.List;
import java.util.Map;
import static org.assertj.core.api.Assertions.assertThat;
@ -68,10 +69,16 @@ public class RawYamlStatementLoaderTest {
RawScenarios rawScenarios = rawStmtsDoc.getRawScenarios();
assertThat(rawScenarios.getScenarioNames()).containsExactly("default", "schema-only");
List<String> defaultScenario = rawScenarios.getNamedScenario("default");
assertThat(defaultScenario).containsExactly("run driver=stdout alias=step1","run driver=stdout alias=step2");
List<String> schemaOnlyScenario = rawScenarios.getNamedScenario("schema-only");
assertThat(schemaOnlyScenario).containsExactly("run driver=blah tags=phase:schema");
Map<String, String> defaultScenario = rawScenarios.getNamedScenario("default");
assertThat(defaultScenario.keySet())
.containsExactly("000","001");
assertThat(defaultScenario.values())
.containsExactly("run driver=stdout alias=step1","run driver=stdout alias=step2");
Map<String, String> schemaOnlyScenario = rawScenarios.getNamedScenario("schema-only");
assertThat(schemaOnlyScenario.keySet())
.containsExactly("000");
assertThat(schemaOnlyScenario.values())
.containsExactly("run driver=blah tags=phase:schema");
assertThat(rawStmtsDoc.getName()).isEqualTo("doc1");
assertThat(blocks).hasSize(1);