From 8c6d0cfe57897db11f4ee4981b3d586e576741e5 Mon Sep 17 00:00:00 2001 From: Jonathan Shook Date: Mon, 11 Oct 2021 17:23:19 -0500 Subject: [PATCH] new decorator API: share Scenario metadata to interested types --- .../analyzer/GrafanaRegionAnalyzer.java | 2 +- .../annotator/GrafanaMetricsAnnotator.java | 2 +- .../engine/core/script/Scenario.java | 22 +++++++-- .../nb/api/metadata/ScenarioMetadata.java | 47 +++++++++++++++++++ .../api/metadata/ScenarioMetadataAware.java | 21 +++++++++ .../nb/api/{ => metadata}/SystemId.java | 39 ++++++++++++++- .../io/nosqlbench/nb/api/SystemIdTest.java | 16 +++++++ 7 files changed, 141 insertions(+), 8 deletions(-) create mode 100644 nb-api/src/main/java/io/nosqlbench/nb/api/metadata/ScenarioMetadata.java create mode 100644 nb-api/src/main/java/io/nosqlbench/nb/api/metadata/ScenarioMetadataAware.java rename nb-api/src/main/java/io/nosqlbench/nb/api/{ => metadata}/SystemId.java (60%) diff --git a/engine-clients/src/main/java/io/nosqlbench/engine/clients/grafana/analyzer/GrafanaRegionAnalyzer.java b/engine-clients/src/main/java/io/nosqlbench/engine/clients/grafana/analyzer/GrafanaRegionAnalyzer.java index 071499e2f..177318364 100644 --- a/engine-clients/src/main/java/io/nosqlbench/engine/clients/grafana/analyzer/GrafanaRegionAnalyzer.java +++ b/engine-clients/src/main/java/io/nosqlbench/engine/clients/grafana/analyzer/GrafanaRegionAnalyzer.java @@ -5,7 +5,7 @@ import io.nosqlbench.engine.clients.grafana.GStitcher; import io.nosqlbench.engine.clients.grafana.GrafanaClient; import io.nosqlbench.engine.clients.grafana.GrafanaClientConfig; import io.nosqlbench.engine.clients.grafana.transfer.*; -import io.nosqlbench.nb.api.SystemId; +import io.nosqlbench.nb.api.metadata.SystemId; import java.nio.file.Path; import java.time.Instant; diff --git a/engine-clients/src/main/java/io/nosqlbench/engine/clients/grafana/annotator/GrafanaMetricsAnnotator.java b/engine-clients/src/main/java/io/nosqlbench/engine/clients/grafana/annotator/GrafanaMetricsAnnotator.java index 05134d91a..a41f121f9 100644 --- a/engine-clients/src/main/java/io/nosqlbench/engine/clients/grafana/annotator/GrafanaMetricsAnnotator.java +++ b/engine-clients/src/main/java/io/nosqlbench/engine/clients/grafana/annotator/GrafanaMetricsAnnotator.java @@ -6,7 +6,7 @@ import io.nosqlbench.engine.clients.grafana.transfer.GAnnotation; import io.nosqlbench.nb.annotations.Service; import io.nosqlbench.nb.api.NBEnvironment; import io.nosqlbench.nb.api.OnError; -import io.nosqlbench.nb.api.SystemId; +import io.nosqlbench.nb.api.metadata.SystemId; import io.nosqlbench.nb.api.annotations.Annotation; import io.nosqlbench.nb.api.annotations.Annotator; import io.nosqlbench.nb.api.config.*; diff --git a/engine-core/src/main/java/io/nosqlbench/engine/core/script/Scenario.java b/engine-core/src/main/java/io/nosqlbench/engine/core/script/Scenario.java index 6b82d7917..6f8e98a5a 100644 --- a/engine-core/src/main/java/io/nosqlbench/engine/core/script/Scenario.java +++ b/engine-core/src/main/java/io/nosqlbench/engine/core/script/Scenario.java @@ -25,6 +25,9 @@ import io.nosqlbench.engine.core.lifecycle.ScenarioController; import io.nosqlbench.engine.core.lifecycle.ScenarioResult; import io.nosqlbench.engine.core.annotation.Annotators; import io.nosqlbench.engine.core.metrics.PolyglotMetricRegistryBindings; +import io.nosqlbench.nb.api.metadata.ScenarioMetadata; +import io.nosqlbench.nb.api.metadata.ScenarioMetadataAware; +import io.nosqlbench.nb.api.metadata.SystemId; import io.nosqlbench.nb.api.annotations.Layer; import io.nosqlbench.nb.api.annotations.Annotation; import org.apache.logging.log4j.LogManager; @@ -46,10 +49,7 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.StandardOpenOption; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Optional; +import java.util.*; import java.util.concurrent.Callable; import java.util.stream.Collectors; @@ -63,6 +63,7 @@ public class Scenario implements Callable { private State state = State.Scheduled; private volatile ScenarioShutdownHook scenarioShutdownHook; private Exception error; + private ScenarioMetadata scenarioMetadata; public enum State { @@ -236,12 +237,23 @@ public class Scenario implements Callable { metricRegistry, scriptEnv ); + ScenarioMetadataAware.apply(extensionObject,getScenarioMetadata()); logger.trace("Adding extension object: name=" + extensionDescriptor.getBaseVariableName() + " class=" + extensionObject.getClass().getSimpleName()); scriptEngine.put(extensionDescriptor.getBaseVariableName(), extensionObject); } + } - + private synchronized ScenarioMetadata getScenarioMetadata() { + if (this.scenarioMetadata==null) { + this.scenarioMetadata = new ScenarioMetadata( + this.startedAtMillis, + this.scenarioName, + SystemId.getNodeId(), + SystemId.getNodeFingerprint() + ); + } + return scenarioMetadata; } public void runScenario() { diff --git a/nb-api/src/main/java/io/nosqlbench/nb/api/metadata/ScenarioMetadata.java b/nb-api/src/main/java/io/nosqlbench/nb/api/metadata/ScenarioMetadata.java new file mode 100644 index 000000000..ffbd94e8e --- /dev/null +++ b/nb-api/src/main/java/io/nosqlbench/nb/api/metadata/ScenarioMetadata.java @@ -0,0 +1,47 @@ +package io.nosqlbench.nb.api.metadata; + +import java.util.Map; + +/** + * If an object is ScenarioMetadata, then they will be updated with a map of + * scenario metadata. Supported types are: + *
    + *
  • ScriptingPluginInfo
  • + *
+ */ +public class ScenarioMetadata { + private final long startedAt; + private final String sessionName; + private final String systemId; + private final String systemFingerprint; + + public ScenarioMetadata(long startedAt, String sessionName, String systemId, String systemFingerprint) { + this.startedAt = startedAt; + this.sessionName = sessionName; + this.systemId = systemId; + this.systemFingerprint = systemFingerprint; + } + + public long getStartedAt() { + return startedAt; + } + + public String getSessionName() { + return sessionName; + } + + public String getSystemId() { + return systemId; + } + + public String getSystemFingerprint() { + return systemFingerprint; + } + + public Map asMap() { + return Map.of("STARTED_AT",String.valueOf(startedAt), + "SESSION_NAME",sessionName, + "SYSTEM_ID",systemId, + "SYSTEM_FINGERPRINT", systemFingerprint); + } +} diff --git a/nb-api/src/main/java/io/nosqlbench/nb/api/metadata/ScenarioMetadataAware.java b/nb-api/src/main/java/io/nosqlbench/nb/api/metadata/ScenarioMetadataAware.java new file mode 100644 index 000000000..aee925961 --- /dev/null +++ b/nb-api/src/main/java/io/nosqlbench/nb/api/metadata/ScenarioMetadataAware.java @@ -0,0 +1,21 @@ +package io.nosqlbench.nb.api.metadata; + +/** + * Where supported, the following named fields are injected into object which + * implement this interface: + *
    + *
  • SCENARIO_NAME - The full scenario name, used for logging, metrics, etc
  • + *
  • STARTED_AT_MILLIS - The millisecond timestamp used to create the scenario name
  • + *
  • SYSTEM_ID - A stable identifier based on the available ip addresses
  • + *
  • SYSTEM_FINGERPRINT - a stable and pseudonymous identifier based on SYSTEM_ID
  • + *
+ */ +public interface ScenarioMetadataAware { + void setScenarioMetadata(ScenarioMetadata metadata); + + static void apply(Object target, ScenarioMetadata metadata) { + if (target instanceof ScenarioMetadataAware) { + ((ScenarioMetadataAware)target).setScenarioMetadata(metadata); + } + } +} diff --git a/nb-api/src/main/java/io/nosqlbench/nb/api/SystemId.java b/nb-api/src/main/java/io/nosqlbench/nb/api/metadata/SystemId.java similarity index 60% rename from nb-api/src/main/java/io/nosqlbench/nb/api/SystemId.java rename to nb-api/src/main/java/io/nosqlbench/nb/api/metadata/SystemId.java index 2a6b2d41d..30b5fc4d4 100644 --- a/nb-api/src/main/java/io/nosqlbench/nb/api/SystemId.java +++ b/nb-api/src/main/java/io/nosqlbench/nb/api/metadata/SystemId.java @@ -1,4 +1,4 @@ -package io.nosqlbench.nb.api; +package io.nosqlbench.nb.api.metadata; import com.google.gson.Gson; import com.google.gson.GsonBuilder; @@ -7,10 +7,25 @@ import oshi.hardware.CentralProcessor; import oshi.hardware.HardwareAbstractionLayer; import oshi.hardware.NetworkIF; +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; import java.util.*; public class SystemId { + /** + * Return the address of a node which is likely to be unique enough to identify + * it within a given subnet, after filtering out all local addresses. This is useful + * when you are managing configuration or results for a set of systems which + * share a common IP addressing scheme. This identifier should be stable as long + * as the node's addresses do not change. + * + * If you are needing an identifier for a node but wish to expose any address data, + * you can use the {@link #getNodeFingerprint()} which takes this value and hashes + * it with SHA-1 to produce a hex string. + * @return A address for the node, likely to be unique and stable for its lifetime + */ public static String getNodeId() { SystemInfo sysinfo = new SystemInfo(); HardwareAbstractionLayer hal = sysinfo.getHardware(); @@ -38,6 +53,28 @@ public class SystemId { return systemID; } + /** + * Produce a stable string identifier consisting of hexadecimal characters. + * The internal data used for this value is based on a stable ordering of non-local + * ip addresses available on the system. + * @return A stable node identifier + */ + public static String getNodeFingerprint() { + String addrId = getNodeId(); + try { + MessageDigest sha1_digest = MessageDigest.getInstance("SHA-1"); + byte[] addrBytes = sha1_digest.digest(addrId.getBytes(StandardCharsets.UTF_8)); + String fingerprint = ""; + for (int i=0; i < addrBytes.length; i++) { + fingerprint += + Integer.toString( ( addrBytes[i] & 0xff ) + 0x100, 16).substring( 1 ); + } + return fingerprint.toUpperCase(Locale.ROOT); + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException(e); + } + } + public static String getHostSummary() { SystemInfo sysinfo = new SystemInfo(); HardwareAbstractionLayer hal = sysinfo.getHardware(); diff --git a/nb-api/src/test/java/io/nosqlbench/nb/api/SystemIdTest.java b/nb-api/src/test/java/io/nosqlbench/nb/api/SystemIdTest.java index 62508270f..940282c33 100644 --- a/nb-api/src/test/java/io/nosqlbench/nb/api/SystemIdTest.java +++ b/nb-api/src/test/java/io/nosqlbench/nb/api/SystemIdTest.java @@ -1,7 +1,10 @@ package io.nosqlbench.nb.api; +import io.nosqlbench.nb.api.metadata.SystemId; import org.junit.jupiter.api.Test; +import static org.assertj.core.api.Assertions.assertThat; + public class SystemIdTest { @Test @@ -9,4 +12,17 @@ public class SystemIdTest { String info = SystemId.getHostSummary(); System.out.println(info); } + + @Test + public void testNostId() { + String info = SystemId.getNodeId(); + assertThat(info).matches("\\d+\\.\\d+\\.\\d+\\.\\d+"); + } + + @Test + public void testNodeFingerprint() { + String hash = SystemId.getNodeFingerprint(); + assertThat(hash).matches("[A-Z0-9]+"); + } + }