partial completion of named scenario overrides

This commit is contained in:
Jonathan Shook 2020-03-24 18:01:23 -05:00
parent 73c3e713a6
commit c3058bcb9c
5 changed files with 294 additions and 144 deletions

View File

@ -1,21 +1,13 @@
package io.nosqlbench.engine.cli;
import ch.qos.logback.classic.Level;
import io.nosqlbench.engine.api.activityconfig.StatementsLoader;
import io.nosqlbench.engine.api.activityconfig.yaml.Scenarios;
import io.nosqlbench.engine.api.activityconfig.yaml.StmtsDocList;
import io.nosqlbench.engine.api.activityimpl.ActivityDef;
import io.nosqlbench.engine.api.activityimpl.ParameterMap;
import io.nosqlbench.engine.api.metrics.IndicatorMode;
import io.nosqlbench.engine.api.util.NosqlBenchFiles;
import io.nosqlbench.engine.api.util.StrInterpolator;
import io.nosqlbench.engine.api.util.Unit;
import org.apache.commons.text.StrSubstitutor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.InputStream;
import java.nio.file.Path;
import java.security.InvalidParameterException;
import java.util.*;
import java.util.stream.Collectors;
@ -77,9 +69,9 @@ public class NBCLIOptions {
private static final Set<String> reserved_words = new HashSet<String>() {{
addAll(
Arrays.asList(
ACTIVITY, SCRIPT, ACTIVITY_TYPES, HELP, METRICS_PREFIX, REPORT_GRAPHITE_TO
)
Arrays.asList(
ACTIVITY, SCRIPT, ACTIVITY_TYPES, HELP, METRICS_PREFIX, REPORT_GRAPHITE_TO
)
);
}};
private static final String DEFAULT_CONSOLE_LOGGING_PATTERN = "%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n";
@ -105,13 +97,13 @@ public class NBCLIOptions {
private List<String> classicHistoConfigs = new ArrayList<>();
private String progressSpec = "console:1m";
private String logsDirectory = "logs";
private boolean wantsInputTypes=false;
private boolean wantsMarkerTypes=false;
private boolean wantsInputTypes = false;
private boolean wantsMarkerTypes = false;
private String[] rleDumpOptions = new String[0];
private String[] cyclelogImportOptions = new String[0];
private String consoleLoggingPattern = DEFAULT_CONSOLE_LOGGING_PATTERN;
private String logsLevel = "INFO";
private Map<String,Level> logLevelsOverrides = new HashMap<>();
private Map<String, Level> logLevelsOverrides = new HashMap<>();
private boolean enableChart = false;
private boolean dockerMetrics = false;
private boolean wantsWorkloads = false;
@ -198,7 +190,7 @@ public class NBCLIOptions {
break;
case LOGS_MAX:
arglist.removeFirst();
logsMax = Integer.valueOf(readWordOrThrow(arglist,"max logfiles to keep"));
logsMax = Integer.valueOf(readWordOrThrow(arglist, "max logfiles to keep"));
break;
case LOGS_LEVEL:
arglist.removeFirst();
@ -237,7 +229,7 @@ public class NBCLIOptions {
logger.info("getting basic help");
} else {
wantsActivityHelp = true;
wantsActivityHelpFor = readWordOrThrow(arglist,"topic");
wantsActivityHelpFor = readWordOrThrow(arglist, "topic");
}
break;
case DUMP_CYCLELOG:
@ -313,7 +305,7 @@ public class NBCLIOptions {
break;
default:
Optional<InputStream> optionalScript =
NosqlBenchFiles.findOptionalStreamOrFile(word, "js", "scripts/auto");
NosqlBenchFiles.findOptionalStreamOrFile(word, "js", "scripts/auto");
//Script
if (optionalScript.isPresent()) {
arglist.removeFirst();
@ -322,101 +314,25 @@ public class NBCLIOptions {
Cmd script = parseScriptCmd(arglist);
cmdList.add(script);
//Scripted yaml
} else if (NBCLIScenarioParser.isFoundWorkload(word)) {
NBCLIScenarioParser.parseScenarioCommand(arglist);
} else {
Optional<Path> path = NosqlBenchFiles.findOptionalPath(word, "yaml", "activities");
if(path.isPresent()){
arglist.removeFirst();
String scenarioFilter = null;
//Named scenario
if (arglist.size() > 0 && !arglist.peekFirst().contains("=")){
scenarioFilter = arglist.peekFirst();
arglist.removeFirst();
}
parseWorkloadYamlCmds(path.get().toString(), arglist, scenarioFilter);
}
else {
throw new InvalidParameterException("unrecognized option:" + word);
}
throw new InvalidParameterException("unrecognized option:" + word);
}
break;
}
}
}
private void parseWorkloadYamlCmds(String yamlPath, LinkedList<String> arglist, String scenarioFilter) {
StmtsDocList stmts = StatementsLoader.load(logger, yamlPath);
Scenarios scenarios = stmts.getDocScenarios();
String scenarioName = "default";
if (scenarioFilter != null){
scenarioName = scenarioFilter;
}
List<String> cmds = scenarios.getNamedScenario(scenarioName);
Map<String, String> paramMap = new HashMap<>();
while(arglist.size() > 0 && arglist.peekFirst().contains("=")){
String arg = arglist.removeFirst();
String oldArg = arg;
arg = Synonyms.canonicalize(arg, logger);
for(int i =0 ; i< cmds.size(); i++){
String yamlCmd = cmds.get(i);
String[] argArray = arg.split("=");
String argKey = argArray[0];
String argValue = argArray[1];
if (!yamlCmd.contains(argKey)) {
cmds.set(i, yamlCmd +" " + arg);
}else{
paramMap.put(argKey, argValue);
}
}
}
if (cmds == null){
List<String> names = scenarios.getScenarioNames();
throw new RuntimeException("Unknown scenario name, make sure the scenario name you provide exists in the workload definition (yaml):\n" + String.join(",", names));
}
for (String cmd : cmds) {
String[] cmdArray = cmd.split(" ");
for (String parameter: cmdArray) {
if (parameter.contains("=")){
if ( !parameter.contains("TEMPLATE(") && !parameter.contains("<<")) {
String[] paramArray = parameter.split("=");
paramMap.put(paramArray[0], paramArray[1]);
}
}
}
StrSubstitutor sub1 = new StrSubstitutor(paramMap, "<<", ">>", '\\', ",");
StrSubstitutor sub2 = new StrSubstitutor(paramMap, "TEMPLATE(", ")", '\\', ",");
cmd = sub2.replace(sub1.replace(cmd));
if (cmd.contains("yaml=") || cmd.contains("workload=")){
parse(cmd.split(" "));
}else{
parse((cmd + " workload="+yamlPath).split(" "));
}
// Is there a better way to do this than regex?
}
}
private Map<String, Level> parseLogLevelOverrides(String levelsSpec) {
Map<String,Level> levels = new HashMap<>();
Map<String, Level> levels = new HashMap<>();
Arrays.stream(levelsSpec.split("[,;]")).forEach(kp -> {
String[] ll = kp.split(":");
if (ll.length!=2) {
if (ll.length != 2) {
throw new RuntimeException("Log level must have name:level format");
}
levels.put(ll[0],Level.toLevel(ll[1]));
levels.put(ll[0], Level.toLevel(ll[1]));
});
return levels;
}
@ -549,7 +465,7 @@ public class NBCLIOptions {
private Cmd parseFragmentCmd(LinkedList<String> arglist) {
String cmdType = arglist.removeFirst();
String scriptFragment = arglist.removeFirst();
return new Cmd(CmdType.valueOf(cmdType),scriptFragment);
return new Cmd(CmdType.valueOf(cmdType), scriptFragment);
}
private Cmd parseActivityCmd(LinkedList<String> arglist) {
@ -609,15 +525,17 @@ public class NBCLIOptions {
}
public boolean wantsToDumpCyclelog() {
return rleDumpOptions.length>0;
return rleDumpOptions.length > 0;
}
public boolean wantsToImportCycleLog() {
return cyclelogImportOptions.length>0;
return cyclelogImportOptions.length > 0;
}
public String[] getCyclelogImportOptions() {
return cyclelogImportOptions;
}
public String[] getCycleLogExporterOptions() {
return rleDumpOptions;
}
@ -632,7 +550,7 @@ public class NBCLIOptions {
public void setHistoLoggerConfigs(String pattern, String file, String interval) {
//--log-histograms 'hdrdata.log:.*:2m'
histoLoggerConfigs.add(String.format("%s:%s:%s",file,pattern,interval));
histoLoggerConfigs.add(String.format("%s:%s:%s", file, pattern, interval));
}
public boolean wantsWorkloads() {
@ -669,10 +587,10 @@ public class NBCLIOptions {
public String getCmdSpec() {
if (cmdSpec.startsWith("'") && cmdSpec.endsWith("'")) {
return cmdSpec.substring(1,cmdSpec.length()-1);
return cmdSpec.substring(1, cmdSpec.length() - 1);
}
if (cmdSpec.startsWith("\"") && cmdSpec.endsWith("\"")) {
return cmdSpec.substring(1,cmdSpec.length()-1);
return cmdSpec.substring(1, cmdSpec.length() - 1);
}
return cmdSpec;
}
@ -729,8 +647,9 @@ public class NBCLIOptions {
private static class ProgressSpec {
public String intervalSpec;
public IndicatorMode indicatorMode;
public String toString() {
return indicatorMode.toString()+":" + intervalSpec;
return indicatorMode.toString() + ":" + intervalSpec;
}
}
@ -753,5 +672,4 @@ public class NBCLIOptions {
}
}

View File

@ -0,0 +1,200 @@
package io.nosqlbench.engine.cli;
import io.nosqlbench.engine.api.activityconfig.StatementsLoader;
import io.nosqlbench.engine.api.activityconfig.yaml.Scenarios;
import io.nosqlbench.engine.api.activityconfig.yaml.StmtsDocList;
import io.nosqlbench.engine.api.exceptions.BasicError;
import io.nosqlbench.engine.api.util.NosqlBenchFiles;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.nio.file.Path;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class NBCLIScenarioParser {
private final static Logger logger = LoggerFactory.getLogger(NBCLIScenarioParser.class);
public static boolean isFoundWorkload(String word) {
Optional<Path> workloadPath = NosqlBenchFiles.findOptionalPath(word, "yaml", "activities");
return workloadPath.isPresent();
}
public static void parseScenarioCommand(LinkedList<String> arglist) {
String workloadName = arglist.removeFirst();
Optional<Path> workloadPathSearch = NosqlBenchFiles.findOptionalPath(workloadName, "yaml", "activities");
Path workloadPath = workloadPathSearch.orElseThrow();
String scenarioName = (arglist.size()>0 && !arglist.peekFirst().contains("=")) ? arglist.removeFirst() : "default";
// Load in user's CLI options
LinkedHashMap<String,String> userCli = new LinkedHashMap<>();
while (arglist.size()>0 && arglist.peekFirst().contains("=")) {
String[] arg = arglist.removeFirst().split("=");
arg[0] = Synonyms.canonicalize(arg[0], logger);
if (userCli.containsKey(arg[0])) {
throw new BasicError("duplicate occurence of option on command line: " + arg[0]);
}
userCli.put(arg[0],arg[1]);
}
// Load in named scenario
StmtsDocList stmts = StatementsLoader.load(logger, workloadPath.toString());
Scenarios scenarios = stmts.getDocScenarios();
List<String> cmds = scenarios.getNamedScenario(scenarioName);
if (cmds==null) {
throw new BasicError("Unable to find named scenario '" + scenarioName + "' in workload '" + workloadName);
}
// This will hold the command to be prepended to the main arglist
LinkedList<String> buildCmdBuffer = new LinkedList<>();
Pattern cmdpattern = Pattern.compile("(?<name>\\w+)((?<oper>==|:=|=:|=)(?<val>.+))?");
for (String cmd : cmds) { // each command line of the named scenario
LinkedHashMap<String,String> usersCopy = new LinkedHashMap<>(userCli);
LinkedHashMap<String,CmdArg> cmdline = 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));
}
LinkedHashMap<String,String> builtcmd = new LinkedHashMap<>();
for (CmdArg cmdarg : cmdline.values()) {
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="+workloadPath.toString());
}
logger.debug("Named scenario built command: " + String.join(" ",builtcmd.values()));
buildCmdBuffer.addAll(builtcmd.values());
}
buildCmdBuffer.descendingIterator().forEachRemaining(arglist::addFirst);
}
private final static class CmdArg {
private final String name;
private final String operator;
private final String value;
public CmdArg(String name, String operator, String value) {
this.name = name;
this.operator = operator;
this.value = value;
}
public boolean isReassignable() {
return "=".equals(operator);
}
public boolean isFinalVerbose() {
return "==".equals(operator);
}
public boolean isFinalSilent() {
return ":=".equals(operator) || "=:".equals(operator);
}
public CmdArg override(String value) {
if (isReassignable()) {
return new CmdArg(this.name, this.operator, value);
} else if (isFinalSilent()) {
return this;
} else if (isFinalVerbose()) {
throw new BasicError("Unable to reassign value for named scenario: '" + value + "'");
} else {
throw new RuntimeException("impossible!");
}
}
@Override
public String toString() {
return name + (operator!=null ? "=" : "") + (value!=null ? value : "");
}
public String getName() {
return name;
}
}
// private static void parseWorkloadYamlCmds(String yamlPath, LinkedList<String> arglist, String scenarioName) {
// StmtsDocList stmts = StatementsLoader.load(logger, yamlPath);
//
// Scenarios scenarios = stmts.getDocScenarios();
//
// String scenarioName = "default";
// if (scenarioName != null) {
// scenarioName = scenarioName;
// }
//
// List<String> cmds = scenarios.getNamedScenario(scenarioName);
//
//
// Map<String, String> paramMap = new HashMap<>();
// while (arglist.size() > 0 && arglist.peekFirst().contains("=")) {
// String arg = arglist.removeFirst();
// String oldArg = arg;
// arg = Synonyms.canonicalize(arg, logger);
//
// for (int i = 0; i < cmds.size(); i++) {
// String yamlCmd = cmds.get(i);
// String[] argArray = arg.split("=");
// String argKey = argArray[0];
// String argValue = argArray[1];
// if (!yamlCmd.contains(argKey)) {
// cmds.set(i, yamlCmd + " " + arg);
// } else {
// paramMap.put(argKey, argValue);
// }
// }
// }
//
//
// if (cmds == null) {
// List<String> names = scenarios.getScenarioNames();
// throw new RuntimeException("Unknown scenario name, make sure the scenario name you provide exists in the workload definition (yaml):\n" + String.join(",", names));
// }
//
// for (String cmd : cmds) {
// String[] cmdArray = cmd.split(" ");
//
// for (String parameter : cmdArray) {
// if (parameter.contains("=")) {
// if (!parameter.contains("TEMPLATE(") && !parameter.contains("<<")) {
// String[] paramArray = parameter.split("=");
// paramMap.put(paramArray[0], paramArray[1]);
// }
// }
// }
//
// StrSubstitutor sub1 = new StrSubstitutor(paramMap, "<<", ">>", '\\', ",");
// StrSubstitutor sub2 = new StrSubstitutor(paramMap, "TEMPLATE(", ")", '\\', ",");
//
// cmd = sub2.replace(sub1.replace(cmd));
//
// if (cmd.contains("yaml=") || cmd.contains("workload=")) {
// parse(cmd.split(" "));
// } else {
// parse((cmd + " workload=" + yamlPath).split(" "));
// }
//
// // Is there a better way to do this than regex?
//
// }
// }
}

View File

@ -0,0 +1,65 @@
package io.nosqlbench.engine.cli;
import io.nosqlbench.engine.api.exceptions.BasicError;
import org.junit.Test;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.*;
public class NBCLIScenarioParserTest {
@Test
public void providePathForScenario() {
NBCLIOptions opts = new NBCLIOptions(new String[]{ "local/example-scenarios" });
List<NBCLIOptions.Cmd> cmds = opts.getCommands();
}
@Test
public void defaultScenario() {
NBCLIOptions opts = new NBCLIOptions(new String[]{ "scenario-test" });
List<NBCLIOptions.Cmd> cmds = opts.getCommands();
}
@Test
public void defaultScenarioWithParams() {
NBCLIOptions opts = new NBCLIOptions(new String[]{ "scenario-test", "cycles=100"});
List<NBCLIOptions.Cmd> cmds = opts.getCommands();
assertThat(cmds.get(0).getCmdSpec()).containsOnlyOnce("cycles=100");
assertThat(cmds.get(0).getCmdSpec()).containsOnlyOnce("cycles=");
}
@Test
public void namedScenario() {
NBCLIOptions opts = new NBCLIOptions(new String[]{ "scenario-test", "schema-only"});
List<NBCLIOptions.Cmd> cmds = opts.getCommands();
}
@Test
public void namedScenarioWithParams() {
NBCLIOptions opts = new NBCLIOptions(new String[]{ "scenario-test", "schema-only", "cycles=100"});
List<NBCLIOptions.Cmd> cmds = opts.getCommands();
assertThat(cmds.get(0).getCmdSpec()).containsOnlyOnce("cycles=100");
assertThat(cmds.get(0).getCmdSpec()).containsOnlyOnce("cycles=");
}
@Test
public void testThatSilentFinalParametersPersist() {
NBCLIOptions opts = new NBCLIOptions(new String[]{ "scenario-test", "type=foo"});
List<NBCLIOptions.Cmd> cmds = opts.getCommands();
assertThat(cmds.get(0).getCmdSpec()).containsOnlyOnce("driver=stdout");
assertThat(cmds.get(1).getCmdSpec()).containsOnlyOnce("driver=foo");
}
@Test(expected = BasicError.class)
public void testThatVerboseFinalParameterThrowsError() {
NBCLIOptions opts = new NBCLIOptions(new String[]{ "scenario-test", "yaml=canttouchthis"});
List<NBCLIOptions.Cmd> cmds = opts.getCommands();
}
@Test(expected = BasicError.class)
public void testThatMissingScenarioNameThrowsError() {
NBCLIOptions opts = new NBCLIOptions(new String[]{ "scenario-test", "missing-scenario"});
}
}

View File

@ -193,39 +193,6 @@ public class TestNBCLIOptions {
}
@Test
public void providePathForScenario() {
NBCLIOptions opts = new NBCLIOptions(new String[]{ "local/example-scenarios" });
List<NBCLIOptions.Cmd> cmds = opts.getCommands();
}
@Test
public void defaultScenario() {
NBCLIOptions opts = new NBCLIOptions(new String[]{ "scenario-test" });
List<NBCLIOptions.Cmd> cmds = opts.getCommands();
}
@Test
public void defaultScenarioWithParams() {
NBCLIOptions opts = new NBCLIOptions(new String[]{ "scenario-test", "cycles=100"});
List<NBCLIOptions.Cmd> cmds = opts.getCommands();
assertThat(cmds.get(0).getCmdSpec()).containsOnlyOnce("cycles=100");
assertThat(cmds.get(0).getCmdSpec()).containsOnlyOnce("cycles=");
}
@Test
public void namedScenario() {
NBCLIOptions opts = new NBCLIOptions(new String[]{ "scenario-test", "schema-only"});
List<NBCLIOptions.Cmd> cmds = opts.getCommands();
}
@Test
public void namedScenarioWithParams() {
NBCLIOptions opts = new NBCLIOptions(new String[]{ "scenario-test", "schema-only", "cycles=100"});
List<NBCLIOptions.Cmd> cmds = opts.getCommands();
assertThat(cmds.get(0).getCmdSpec()).containsOnlyOnce("cycles=100");
assertThat(cmds.get(0).getCmdSpec()).containsOnlyOnce("cycles=");
}
@Test
public void listWorkloads() {

View File

@ -1,11 +1,11 @@
# nb -v run type=cql yaml=cql-iot tags=phase:schema host=dsehost
scenarios:
default:
- run type=stdout yaml=scenario-test tags=phase:schema
- run type=stdout yaml=scenario-test tags=phase:rampup cycles=TEMPLATE(cycles,10)
- run type=stdout yaml=scenario-test tags=phase:main cycles=TEMPLATE(cycles,10)
- run type=:stdout yaml==scenario-test tags=phase:schema
- "run type=stdout yaml==scenario-test tags=phase:rampup cycles=TEMPLATE(cycles,10)"
- "run type=stdout yaml==scenario-test tags=phase:main cycles=TEMPLATE(cycles,10)"
schema-only:
- run type=stdout yaml=scenario-test tags=phase:schema
- "run type=stdout yaml=scenario-test tags=phase:schema"
blocks:
- tags:
phase: schema