diff --git a/adapters-api/src/main/java/io/nosqlbench/adapters/api/activityconfig/rawyaml/RawOpDef.java b/adapters-api/src/main/java/io/nosqlbench/adapters/api/activityconfig/rawyaml/RawOpDef.java index ae8931c3d..49ce7e894 100644 --- a/adapters-api/src/main/java/io/nosqlbench/adapters/api/activityconfig/rawyaml/RawOpDef.java +++ b/adapters-api/src/main/java/io/nosqlbench/adapters/api/activityconfig/rawyaml/RawOpDef.java @@ -69,7 +69,7 @@ public class RawOpDef extends RawOpFields { Object op = map.remove(keyName); if (op instanceof CharSequence s) { if (!keyName.equals("stmt")) { - logger.warn("Used implied stmt field under name '" + keyName + "'. You can just use 'stmt: ... "+ s +"' or the equivalent to avoid this warning."); + logger.info("Used implied stmt field under name '" + keyName + "'. You can just use 'stmt: ... "+ s +"' or the equivalent to avoid this warning."); } map.put("stmt",s.toString()); // setOp(new LinkedHashMap(Map.of("stmt",s.toString()))); @@ -79,7 +79,7 @@ public class RawOpDef extends RawOpFields { } if (found.size() > 1) { throw new BasicError("You used " + found + " as an op name, but only one of these is allowed at a time."); - } else if ((getName() == null || getName().isEmpty()) && op == null && map.size() > 0) { + } else if ((getName() == null || getName().isEmpty()) && op == null && !map.isEmpty()) { Map.Entry first = map.entrySet().iterator().next(); setName(first.getKey()); setOp(first.getValue()); @@ -90,7 +90,7 @@ public class RawOpDef extends RawOpFields { if (_op) { if (_params) { - if (map.size() > 0) { + if (!map.isEmpty()) { throw new OpConfigError("If you have scoped op and params, you may not have dangling fields. Op template named '" + this.getName() + "' is invalid. Move dangling params ("+ map.keySet() +") under another field."); } } else { // no params. Op was a scoped field and there are dangling fields, so assume they belong to params diff --git a/engine-cli/src/main/java/io/nosqlbench/engine/cli/NBCLI.java b/engine-cli/src/main/java/io/nosqlbench/engine/cli/NBCLI.java index d96066004..2a9ba50c9 100644 --- a/engine-cli/src/main/java/io/nosqlbench/engine/cli/NBCLI.java +++ b/engine-cli/src/main/java/io/nosqlbench/engine/cli/NBCLI.java @@ -86,6 +86,8 @@ public class NBCLI implements Function, NBLabeledElement { private NBLabels labels; private String sessionName; + private String sessionCode; + private long sessionTime; public NBCLI(final String commandName) { this.commandName = commandName; @@ -153,10 +155,17 @@ public class NBCLI implements Function, NBLabeledElement { // logger = LogManager.getLogger("NBCLI"); NBCLI.loggerConfig.setConsoleLevel(NBLogLevel.ERROR); - + this.sessionTime = System.currentTimeMillis(); final NBCLIOptions globalOptions = new NBCLIOptions(args, Mode.ParseGlobalsOnly); - this.labels=NBLabels.forKV("command",commandName).and(globalOptions.getLabelMap()); - this.sessionName = SessionNamer.format(globalOptions.getSessionName()); + + this.sessionCode = SystemId.genSessionCode(sessionTime); + this.sessionName = SessionNamer.format(globalOptions.getSessionName(),sessionTime).replaceAll("SESSIONCODE",sessionCode); + this.labels = NBLabels.forKV("command", commandName, "appname", "nosqlbench") + .andInstances("node",SystemId.getNodeId()) + .andInstances("nodeid",SystemId.getPackedNodeId()) +// .andInstances("sesscode",sessionCode) + .andInstances("session",sessionName) + .and(globalOptions.getLabelMap()); NBCLI.loggerConfig .setSessionName(sessionName) diff --git a/nb-api/src/main/java/io/nosqlbench/api/metadata/SystemId.java b/nb-api/src/main/java/io/nosqlbench/api/metadata/SystemId.java index 6be75b6c1..ab67ffb5d 100644 --- a/nb-api/src/main/java/io/nosqlbench/api/metadata/SystemId.java +++ b/nb-api/src/main/java/io/nosqlbench/api/metadata/SystemId.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 nosqlbench + * Copyright (c) 2022-2023 nosqlbench * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,6 +23,7 @@ import oshi.hardware.CentralProcessor; import oshi.hardware.HardwareAbstractionLayer; import oshi.hardware.NetworkIF; +import java.net.*; import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; @@ -36,32 +37,37 @@ public class SystemId { * 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, + *

+ * If you are needing an identifier for a node but do not with 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() { + return getMainInetAddrDirect().map(InetAddress::getHostAddress).orElse("UNKNOWN_HOST_ID"); + } + + private static String getNodeIdOSHI() { SystemInfo sysinfo = new SystemInfo(); HardwareAbstractionLayer hal = sysinfo.getHardware(); List interfaces = hal.getNetworkIFs(); Optional first = interfaces.stream() - .filter(i -> !i.getName().startsWith("docker" )) - .filter(i -> !i.getName().equals("lo" )) - .sorted((o1, o2) -> { - if (o1.getName().startsWith("e" ) && o2.getName().startsWith("e" )) { - return 0; - } - if (o1.getName().startsWith("e" )) { - return -1; - } - if (o2.getName().startsWith("e" )) { - return 1; - } + .filter(i -> !i.getName().startsWith("docker")) + .filter(i -> !i.getName().equals("lo")) + .sorted((o1, o2) -> { + if (o1.getName().startsWith("e") && o2.getName().startsWith("e")) { return 0; - }) + } + if (o1.getName().startsWith("e")) { + return -1; + } + if (o2.getName().startsWith("e")) { + return 1; + } + return 0; + }) .flatMap(iface -> Arrays.stream(iface.getIPv4addr().clone())) .filter(addr -> !(addr.startsWith("127."))) .findFirst(); @@ -69,10 +75,51 @@ public class SystemId { return systemID; } + private static Optional getMainInetAddrDirect() { + List ifaces = getInterfacesDirect(); + Optional first = ifaces.stream() + .filter(i -> !i.getName().startsWith("docker")) + .filter(i -> !i.getName().equals("lo")) + .sorted((o1, o2) -> { + if (o1.getName().startsWith("e") && o2.getName().startsWith("e")) return 0; + if (o1.getName().startsWith("e")) return -1; + if (o2.getName().startsWith("e")) return 1; + return 0; + }).findFirst(); + if (first.isEmpty()) return Optional.empty(); + + Optional firstInetAddrForInterface = first.get().getInterfaceAddresses().stream() + .map(ia -> ia.getAddress()) + .sorted((i1, i2) -> { + if (i1 instanceof Inet4Address && i2 instanceof Inet4Address) return 0; + if (i1 instanceof Inet4Address) return -1; + if (i2 instanceof Inet4Address) return 1; + return 0; + }).findFirst(); + return firstInetAddrForInterface; + } + + + /** + * Using this to bypass OSHI because it calls logger init before we want it. + * TODO: Maybe remove OSHI altogether if there is a reasonable Java HAL view in current Java editions. + * + * @return a list of network interfaces + */ + private static List getInterfacesDirect() { + try { + Enumeration ni = NetworkInterface.getNetworkInterfaces(); + return new ArrayList<>(Collections.list(ni)); + } catch (SocketException e) { + throw new RuntimeException(e); + } + } + /** * 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() { @@ -81,9 +128,9 @@ public class SystemId { 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++) { + for (int i = 0; i < addrBytes.length; i++) { fingerprint += - Integer.toString( ( addrBytes[i] & 0xff ) + 0x100, 16).substring( 1 ); + Integer.toString((addrBytes[i] & 0xff) + 0x100, 16).substring(1); } return fingerprint.toUpperCase(Locale.ROOT); } catch (NoSuchAlgorithmException e) { @@ -125,4 +172,75 @@ public class SystemId { return gson.toJson(details); } + + private final static String radixSymbols = "01234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ~-"; // 64 symbols, for 2^6! + private final int brailleStart = '⠀'; + private final int brailleEnd = '⣿'; + private final int brailleRadix = brailleEnd - brailleStart; + + public static String getBrailleNodeId() { + String nodeId = getNodeId(); + String[] fields = nodeId.split("\\."); + byte[] addr; + try { + InetAddress inetAddr = Inet4Address.getByName(nodeId); + addr = inetAddr.getAddress(); + } catch (UnknownHostException e) { + throw new RuntimeException(e); + } + return braille((addr[0] << 24) + (addr[1] << 16) + (addr[2] << 8) + addr[3]); + } + + private static String braille(int value) { + StringBuilder buf = new StringBuilder(4); + for (int i = 0; i < 4; i++) { + int mask = value & 0xF; + value >>= 8; + int charat = '⠀' + mask; + buf.append((char) charat); + } + return buf.toString(); + } + + private static String braille(long value) { + StringBuilder buf = new StringBuilder(8); + for (int i = 0; i < 8; i++) { + int mask = (int) value & 0xF; + value >>= 8; + int charat = '⠀' + mask; + buf.append((char) charat); + } + return buf.toString(); + } + + public static String genSessionCode(long epochMillis) { + return pack(epochMillis) + "_" + getPackedNodeId(); + } + + public static String getPackedNodeId() { + String nodeId = getNodeId(); + String[] fields = nodeId.split("\\."); + byte[] addr; + try { + InetAddress inetAddr = Inet4Address.getByName(nodeId); + addr = inetAddr.getAddress(); + } catch (UnknownHostException e) { + throw new RuntimeException(e); + } + return pack((addr[0] << 24) + (addr[1] << 16) + (addr[2] << 8) + addr[3]); + } + + public static String pack(long bitfield) { + StringBuilder sb = new StringBuilder(11); + while (bitfield > 0) { + long tail = bitfield & 0b00111111; + bitfield >>= 6; + sb.append(radixSymbols.charAt((int) tail)); + } + return sb.toString(); + } + + public static String genSessionBits() { + return getBrailleNodeId() + ":" + braille(System.currentTimeMillis()); + } } 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 5593b2e49..61fda0163 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,5 +1,5 @@ /* - * Copyright (c) 2022 nosqlbench + * Copyright (c) 2022-2023 nosqlbench * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,28 +17,61 @@ package io.nosqlbench.nb.api; import io.nosqlbench.api.metadata.SystemId; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; public class SystemIdTest { + private static final Logger logger = LogManager.getLogger(SystemIdTest.class); @Test public void testHostInfo() { - String info = SystemId.getHostSummary(); - System.out.println(info); + String hostSummary = SystemId.getHostSummary(); + logger.info("host summary: " + hostSummary); } @Test public void testNostId() { - String info = SystemId.getNodeId(); - assertThat(info).matches("\\d+\\.\\d+\\.\\d+\\.\\d+"); + String nodeId = SystemId.getNodeId(); + assertThat(nodeId).matches("\\d+\\.\\d+\\.\\d+\\.\\d+"); + logger.info("node id: " + nodeId); } @Test public void testNodeFingerprint() { - String hash = SystemId.getNodeFingerprint(); - assertThat(hash).matches("[A-Z0-9]+"); + String nodeFingerprint = SystemId.getNodeFingerprint(); + assertThat(nodeFingerprint).matches("[A-Z0-9]+"); + logger.info("node fingerprint: " + nodeFingerprint); + } + + @Test + public void testBrailleNodeId() { + String brailleNodeId = SystemId.getBrailleNodeId(); + assertThat(brailleNodeId).matches("[⠀-⣿]{4}"); // note, that is not a space. It is the starting braille value of empty + logger.info("braille node id: " + brailleNodeId); + } + + @Test + public void testPackedNodeId() { + String packedNodeId = SystemId.getPackedNodeId(); + assertThat(packedNodeId).matches("[0-9A-Za-z_-]+"); + logger.info("packed node id: " + packedNodeId); + } + + @Test + public void testGenSessionCode() { + String sessionCode=SystemId.genSessionCode(234L); + assertThat(sessionCode).matches("[0-9a-zA-Z~-]+_[0-9a-zA-Z~-]+"); + logger.info("session code: " + sessionCode); + } + + @Test + public void testGenSessionBits() { + String sessionBits = SystemId.genSessionBits(); + assertThat(sessionBits).matches("[⠀-⣿]+:[⠀-⣿]+"); + logger.info("session bits: " + sessionBits); } } diff --git a/pom.xml b/pom.xml index 75216c96f..a2d800651 100644 --- a/pom.xml +++ b/pom.xml @@ -61,7 +61,7 @@ adapter-tcp adapter-dynamodb adapter-mongodb - adapter-venice + adapter-pulsar adapter-s4j adapter-kafka @@ -102,7 +102,7 @@ adapters-api - adapter-venice + adapter-diag adapter-stdout adapter-cqld4