Merge pull request #943 from nosqlbench/nosqlbench-941-annotators

add engine-clients module back into the runtime
This commit is contained in:
Jonathan Shook 2023-01-23 14:18:41 -06:00 committed by GitHub
commit 839e4d16b9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
45 changed files with 120490 additions and 6 deletions

93
engine-clients/pom.xml Normal file
View File

@ -0,0 +1,93 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>mvn-defaults</artifactId>
<groupId>io.nosqlbench</groupId>
<version>5.17.1-SNAPSHOT</version>
<relativePath>../mvn-defaults</relativePath>
</parent>
<artifactId>engine-clients</artifactId>
<packaging>jar</packaging>
<name>${project.artifactId}</name>
<description>
A set of clients for calling nosqlbench and related services.
</description>
<dependencies>
<dependency>
<groupId>io.nosqlbench</groupId>
<artifactId>engine-api</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>io.nosqlbench</groupId>
<artifactId>nb-api</artifactId>
<version>5.17.1-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
</dependencies>
<build>
<testResources>
<testResource>
<directory>src/test/resources</directory>
<includes>
<include>examples/**</include>
<include />
</includes>
</testResource>
</testResources>
<plugins>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.8</version>
<executions>
<execution>
<id>prepare-agent</id>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
<execution>
<id>report</id>
<phase>test</phase>
<goals>
<goal>report</goal>
</goals>
</execution>
<execution>
<id>jacoco-check</id>
<phase>verify</phase>
<goals>
<goal>check</goal>
</goals>
<configuration>
<rules>
<rule>
<element>BUNDLE</element>
<limits>
<limit>
<counter>INSTRUCTION</counter>
<value>COVEREDRATIO</value>
<minimum>0.00</minimum>
<maximum>1.00</maximum>
</limit>
</limits>
</rule>
</rules>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,71 @@
/*
* Copyright (c) 2022 nosqlbench
*
* 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 io.nosqlbench.engine.clients.grafana;
public class ApiToken {
private String name;
private final String hashed;
private String key;
private final int id;
public ApiToken(String name, String key) {
this(1, name, key, null);
}
public ApiToken(int id, String name, String key) {
this(id, name, key, null);
}
public ApiToken(int id, String name, String key, String hashed) {
this.id = id;
this.name = name;
this.key = key;
this.hashed = hashed;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public String getHashed() {
return hashed;
}
@Override
public String toString() {
return "ApiToken{" +
"name='" + name + '\'' +
", hashed='" + hashed + '\'' +
", key='" + key + '\'' +
'}';
}
public int getId() {
return this.id;
}
}

View File

@ -0,0 +1,112 @@
/*
* Copyright (c) 2022 nosqlbench
*
* 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 io.nosqlbench.engine.clients.grafana;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class By {
private final String key;
private final Object value;
public By(String key, Object value) {
this.key = key;
this.value = value;
}
/**
* epoch datetime in milliseconds
*/
public static By from(long epoch) {
return new By("from", epoch);
}
/**
* epoch datetime in milliseconds
*/
public static By to(long epoch) {
return new By("to", epoch);
}
/**
* number. Optional - default is 100. Max limit for results returned.
*/
public static By limit(long limit) {
return new By("limit", limit);
}
/**
* Find annotations for a specified alert.
*/
public static By alertId(String id) {
return new By("alertId", id);
}
public static By panelId(String panelId) {
return new By("panelId", panelId);
}
public static By userId(String userId) {
return new By("userId", userId);
}
public static By typeAnnotation() {
return new By("type", "annotation");
}
public static By typeAlert() {
return new By("type", "alert");
}
/**
* Add one tag at a time, in either "tag" or "tag:value" form.
*/
public static By tag(String tag) {
return new By("tag", tag);
}
public static By id(int id) {
return new By("id", id);
}
public static String urlEncoded(By... bys) {
List<String> tags = new ArrayList<>();
StringBuilder sb = new StringBuilder();
for (By by : bys) {
if (by.key.equals("tag")) {
tags.addAll(Arrays.asList(by.value.toString().split(",")));
} else {
sb.append(by.key).append("=").append(by.value);
sb.append("&");
}
}
for (String tag : tags) {
sb.append("tags=").append(tag).append("&");
}
if (sb.length() > 0) {
sb.setLength(sb.length() - 1);
}
return sb.toString();
}
public static String fields(By... by) {
return urlEncoded(by);
}
}

View File

@ -0,0 +1,20 @@
/*
* Copyright (c) 2022 nosqlbench
*
* 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 io.nosqlbench.engine.clients.grafana;
public class GRangeResult {
}

View File

@ -0,0 +1,49 @@
/*
* Copyright (c) 2022 nosqlbench
*
* 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 io.nosqlbench.engine.clients.grafana;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class GStitcher {
private final Map<String, Set<String>> values;
private final static Pattern pattern = Pattern.compile("\\$(\\w+)");
public GStitcher(Map<String, Set<String>> values) {
this.values = values;
}
public String stitchRegex(String spec) {
StringBuilder sb = new StringBuilder();
Matcher matcher = pattern.matcher(spec);
while (matcher.find()) {
String word = matcher.group(1);
if (values.containsKey(word)) {
Set<String> elems = values.get(word);
String replacement = "(" + String.join("|", elems) + ")";
matcher.appendReplacement(sb, replacement);
} else {
matcher.appendReplacement(sb, "NOTFOUND[" + word + "]");
}
}
matcher.appendTail(sb);
return sb.toString();
}
}

View File

@ -0,0 +1,99 @@
/*
* Copyright (c) 2022 nosqlbench
*
* 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 io.nosqlbench.engine.clients.grafana;
import java.time.ZonedDateTime;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class GTimeUnit {
private enum Durations {
s("second", 1000L),
m("minute", 60000L),
h("hour", 3600000L),
d("day", 86400000L),
w("week", 86400000L * 7L),
M("month", 86400000L * 30L),
y("year", 86400000L * 365L);
private final String unitName;
private final long millisPerUnit;
Durations(String unitName, long millisPerUnit) {
this.unitName = unitName;
this.millisPerUnit = millisPerUnit;
}
public long durationInMillis(long count) {
return count * millisPerUnit;
}
public long durationInSeconds(long count) {
return (count * millisPerUnit) / 1000L;
}
}
private static final Pattern absolute = Pattern.compile("\\d+");
private static final Pattern ISO_INSTANT = Pattern.compile(
"(?<year>\\d+)-(?<month>\\d+)-(?<day>\\d+) " +
"(?<hour>\\d+):(?<minute>\\d+):(?<second>\\d+)(\\.(?<millis>\\d+))?Z?"
);
private static final Pattern relUnit = Pattern.compile(
"now((?<value>[-+]\\d+)(?<unit>[smhdwMy]))?(\\/(?<interval>[smhdwMy]))?"
);
public static long epochSecondsFor(String spec) {
if (spec.startsWith("now")) {
Matcher relative = relUnit.matcher(spec);
if (relative.matches()) {
if (relative.group("interval") != null) {
throw new RuntimeException("date field boundaries like '"
+ spec +
"' are not supported yet.");
}
long now = System.currentTimeMillis() / 1000L;
long delta = 0L;
String v = relative.group("value");
String u = relative.group("unit");
if (v != null && u != null) {
long value = Long.parseLong(v);
Durations duration = Durations.valueOf(u);
delta = duration.durationInSeconds(value);
}
long timevalue = now + delta;
return timevalue;
} else {
throw new RuntimeException("unable to match input '" + spec + "'");
}
} else if (spec.matches("\\\\d+")) {
return Long.parseLong(spec);
} else {
Matcher matcher = ISO_INSTANT.matcher(spec);
if (matcher.matches()) {
ZonedDateTime parsed = ZonedDateTime.parse(spec);
return parsed.toEpochSecond();
} else {
throw new RuntimeException("Unrecognized format for grafana time unit '" + spec + "'");
}
}
}
}

View File

@ -0,0 +1,733 @@
/*
* Copyright (c) 2022 nosqlbench
*
* 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 io.nosqlbench.engine.clients.grafana;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;
import io.nosqlbench.engine.clients.grafana.annotator.GrafanaMetricsAnnotator;
import io.nosqlbench.engine.clients.grafana.authorizers.RawSocketInjector;
import io.nosqlbench.engine.clients.grafana.transfer.*;
import io.nosqlbench.engine.clients.prometheus.PMatrixData;
import io.nosqlbench.engine.clients.prometheus.PromQueryResult;
import io.nosqlbench.engine.clients.prometheus.PromSeriesLookupResult;
import io.nosqlbench.api.system.NBEnvironment;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.io.File;
import java.lang.reflect.Type;
import java.net.URLEncoder;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.attribute.PosixFilePermissions;
import java.util.*;
import java.util.function.Supplier;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @see <a href="https://grafana.com/docs/grafana/latest/http_api/annotations/">Grafana Annotations API Docs</a>
*/
public class GrafanaClient {
private final static Logger logger = LogManager.getLogger(GrafanaClient.class);
private static final Gson gson = new GsonBuilder().setPrettyPrinting().create();
private final GrafanaClientConfig config;
private List<GDataSource> datasources;
public GrafanaClient(GrafanaClientConfig config) {
this.config = config;
}
public GrafanaClient(String baseuri) {
this(new GrafanaClientConfig().setBaseUri(baseuri));
}
public GrafanaClientConfig getConfig() {
return config;
}
/**
* <pre>{@code
* GET /api/annotations?from=1506676478816&to=1507281278816&tags=tag1&tags=tag2&limit=100
*
* Example Request:
*
* GET /api/annotations?from=1506676478816&to=1507281278816&tags=tag1&tags=tag2&limit=100 HTTP/1.1
* Accept: application/json
* Content-Type: application/json
* Authorization: Basic YWRtaW46YWRtaW4=
* Query Parameters:
*
* from: epoch datetime in milliseconds. Optional.
* to: epoch datetime in milliseconds. Optional.
* limit: number. Optional - default is 100. Max limit for results returned.
* alertId: number. Optional. Find annotations for a specified alert.
* dashboardId: number. Optional. Find annotations that are scoped to a specific dashboard
* panelId: number. Optional. Find annotations that are scoped to a specific panel
* userId: number. Optional. Find annotations created by a specific user
* type: string. Optional. alert|annotation Return alerts or user created annotations
* tags: string. Optional. Use this to filter global annotations. Global annotations are annotations from an annotation data source that are not connected specifically to a dashboard or panel. To do an AND filtering with multiple tags, specify the tags parameter multiple times e.g. tags=tag1&tags=tag2.
* Example Response:
*
* HTTP/1.1 200
* Content-Type: application/json
* [
* {
* "id": 1124,
* "alertId": 0,
* "dashboardId": 468,
* "panelId": 2,
* "userId": 1,
* "userName": "",
* "newState": "",
* "prevState": "",
* "time": 1507266395000,
* "timeEnd": 1507266395000,
* "text": "test",
* "metric": "",
* "type": "event",
* "tags": [
* "tag1",
* "tag2"
* ],
* "data": {}
* },
* {
* "id": 1123,
* "alertId": 0,
* "dashboardId": 468,
* "panelId": 2,
* "userId": 1,
* "userName": "",
* "newState": "",
* "prevState": "",
* "time": 1507265111000,
* "text": "test",
* "metric": "",
* "type": "event",
* "tags": [
* "tag1",
* "tag2"
* ],
* "data": {}
* }
* ]
* }</pre>
*
* @param by
* @return
*/
public List<GAnnotation> findAnnotations(By... by) {
String query = By.fields(by);
HttpRequest.Builder rqb = config.newRequest("api/annotations?" + query);
rqb.setHeader("Content-Type", "application/json");
HttpRequest request = rqb.build();
HttpClient client = config.newClient();
HttpResponse<String> response = null;
try {
response = client.send(request, HttpResponse.BodyHandlers.ofString());
} catch (Exception e) {
throw new RuntimeException(e);
}
String body = response.body();
Type gtype = new TypeToken<List<GAnnotation>>() {
}.getType();
List<GAnnotation> annotations = gson.fromJson(body, gtype);
return annotations;
}
/**
* <pre>{@code
* POST /api/annotations
*
* Example Request:
*
* POST /api/annotations HTTP/1.1
* Accept: application/json
* Content-Type: application/json
*
* {
* "dashboardId":468,
* "panelId":1,
* "time":1507037197339,
* "timeEnd":1507180805056,
* "tags":["tag1","tag2"],
* "text":"Annotation Description"
* }
* Example Response:
*
* HTTP/1.1 200
* Content-Type: application/json
*
* {
* "message":"Annotation added",
* "id": 1,
* }
* }</pre>
*
* @return
*/
public GAnnotation createAnnotation(GAnnotation gAnnotation) {
HttpClient client = config.newClient();
HttpRequest.Builder rqb = config.newRequest("api/annotations");
rqb.setHeader("Content-Type", "application/json");
String rqBody = gson.toJson(gAnnotation);
rqb = rqb.POST(HttpRequest.BodyPublishers.ofString(rqBody));
HttpResponse<String> response = null;
try {
HttpRequest request = rqb.build();
response = client.send(request, HttpResponse.BodyHandlers.ofString());
} catch (Exception e) {
if (e.getMessage().contains("WWW-Authenticate header missing")) {
throw new RuntimeException("Java HttpClient was not authorized, and it saw no WWW-Authenticate header" +
" in the response, so this is probably Grafana telling you that the auth scheme failed. Normally " +
"this error would be thrown by Java HttpClient:" + e.getMessage());
}
throw new RuntimeException(e);
}
if (response.statusCode() < 200 || response.statusCode() >= 300) {
throw new RuntimeException("Creating annotation failed with status code " + response.statusCode() + " at " +
"baseuri " + config.getBaseUri() + ": " + response.body());
}
String body = response.body();
GAnnotation savedGAnnotation = gson.fromJson(body, GAnnotation.class);
return savedGAnnotation;
}
public List<GDashboardInfo> findDashboards() {
HttpClient client = config.newClient();
HttpRequest.Builder rqb = config.newRequest("api/search?type=dash-db");
rqb = rqb.GET();
HttpResponse<String> response = null;
try {
response = client.send(rqb.build(), HttpResponse.BodyHandlers.ofString());
} catch (Exception e) {
throw new RuntimeException(e);
}
if (response.statusCode() < 200 || response.statusCode() >= 300) {
throw new RuntimeException("Getting list of dashboards failed with status code " + response.statusCode() +
" at baseuri " + config.getBaseUri() + ": " + response.body());
}
String body = response.body();
Type dblist = new TypeToken<List<GDashboardInfo>>() {
}.getType();
List<GDashboardInfo> results = gson.fromJson(body, dblist);
return results;
}
public GSnapshot findSnapshotBykey(String snapshotKey) {
HttpClient client = config.newClient();
HttpRequest.Builder rqb = config.newRequest("api/snapshots/" + snapshotKey);
rqb = rqb.GET();
HttpResponse<String> response = null;
try {
response = client.send(rqb.build(), HttpResponse.BodyHandlers.ofString());
} catch (Exception e) {
throw new RuntimeException(e);
}
if (response.statusCode() < 200 || response.statusCode() >= 300) {
throw new RuntimeException("Getting dashboard snapshot for key '" + snapshotKey + "' failed with status " +
"code " + response.statusCode() +
" at baseuri " + config.getBaseUri() + ": " + response.body());
}
String body = response.body();
GSnapshot snapshot = gson.fromJson(body, GSnapshot.class);
return snapshot;
}
public Optional<GSnapshot> findSnapshotBykeyOptionally(String snapshotKey) {
try {
GSnapshot snapshotBykey = findSnapshotBykey(snapshotKey);
return Optional.of(snapshotBykey);
} catch (Exception e) {
return Optional.empty();
}
}
public List<GSnapshotInfo> findSnapshots() {
HttpClient client = config.newClient();
HttpRequest.Builder rqb = config.newRequest("api/dashboard/snapshots");
rqb = rqb.GET();
HttpResponse<String> response = null;
try {
response = client.send(rqb.build(), HttpResponse.BodyHandlers.ofString());
} catch (Exception e) {
throw new RuntimeException(e);
}
if (response.statusCode() < 200 || response.statusCode() >= 300) {
throw new RuntimeException("Getting dashboard snapshots failed with status code " + response.statusCode() +
" at baseuri " + config.getBaseUri() + ": " + response.body());
}
String body = response.body();
Type t = new TypeToken<List<GSnapshotInfo>>() {
}.getType();
List<GSnapshotInfo> snapshotsInfo = gson.fromJson(body, t);
return snapshotsInfo;
}
public GDashboardResponse getDashboardByUid(String uid) {
HttpClient client = config.newClient();
HttpRequest.Builder rqb = config.newRequest("api/dashboards/uid/" + uid);
rqb = rqb.GET();
HttpResponse<String> response = null;
try {
response = client.send(rqb.build(), HttpResponse.BodyHandlers.ofString());
} catch (Exception e) {
throw new RuntimeException(e);
}
if (response.statusCode() < 200 || response.statusCode() >= 300) {
throw new RuntimeException("Getting dashboard by uid (" + uid + ") failed with status code " + response.statusCode() +
" at baseuri " + config.getBaseUri() + ": " + response.body());
}
String body = response.body();
GDashboardResponse dashboardMeta = gson.fromJson(body, GDashboardResponse.class);
return dashboardMeta;
}
public GSnapshotInfo createSnapshot(GDashboard dashboard, String snid) {
HttpClient client = config.newClient();
HttpRequest.Builder rqb = config.newRequest("api/snapshots");
rqb = rqb.setHeader("Content-Type", "application/json");
String rqBody = gson.toJson(new CreateSnapshotRequest(dashboard, null, snid));
rqb = rqb.POST(HttpRequest.BodyPublishers.ofString(rqBody));
HttpResponse<String> response = null;
try {
response = client.send(rqb.build(), HttpResponse.BodyHandlers.ofString());
} catch (Exception e) {
throw new RuntimeException(e);
}
if (response.statusCode() < 200 || response.statusCode() >= 300) {
throw new RuntimeException("Creating snapshot for snid (" + snid + ") failed with status code " + response.statusCode() +
" at baseuri " + config.getBaseUri() + ": " + response.body());
}
String body = response.body();
GSnapshotInfo snapshotInfo = gson.fromJson(body, GSnapshotInfo.class);
return snapshotInfo;
}
/**
* <pre>{@code
* POST /api/annotations/graphite
*
* Example Request:
*
* POST /api/annotations/graphite HTTP/1.1
* Accept: application/json
* Content-Type: application/json
*
* {
* "what": "Event - deploy",
* "tags": ["deploy", "production"],
* "when": 1467844481,
* "data": "deploy of master branch happened at Wed Jul 6 22:34:41 UTC 2016"
* }
* Example Response:
*
* HTTP/1.1 200
* Content-Type: application/json
*
* {
* "message":"Graphite annotation added",
* "id": 1
* }
* }</pre>
*
* @return
*/
public GAnnotation createGraphiteAnnotation() {
throw new RuntimeException("unimplemented");
}
/**
* <pre>{@code
* PUT /api/annotations/:id
*
* Updates all properties of an annotation that matches the specified id. To only update certain property, consider using the Patch Annotation operation.
*
* Example Request:
*
* PUT /api/annotations/1141 HTTP/1.1
* Accept: application/json
* Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
* Content-Type: application/json
*
* {
* "time":1507037197339,
* "timeEnd":1507180805056,
* "text":"Annotation Description",
* "tags":["tag3","tag4","tag5"]
* }
* Example Response:
*
* HTTP/1.1 200
* Content-Type: application/json
*
* {
* "message":"Annotation updated"
* }
* }</pre>
*/
public void updateAnnotation() {
throw new RuntimeException("unimplemented");
}
/**
* <pre>{@code
* PATCH /api/annotations/:id
*
* Updates one or more properties of an annotation that matches the specified id.
*
* This operation currently supports updating of the text, tags, time and timeEnd properties.
*
* Example Request:
*
* PATCH /api/annotations/1145 HTTP/1.1
* Accept: application/json
* Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
* Content-Type: application/json
*
* {
* "text":"New Annotation Description",
* "tags":["tag6","tag7","tag8"]
* }
* Example Response:
*
* HTTP/1.1 200
* Content-Type: application/json
*
* {
* "message":"Annotation patched"
* }
* }</pre>
*/
public void patchAnnotation() {
throw new RuntimeException("unimplemented");
}
/**
* <pre>{@code
* Example Request:
*
* DELETE /api/annotations/1 HTTP/1.1
* Accept: application/json
* Content-Type: application/json
* Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
* Example Response:
*
* HTTP/1.1 200
* Content-Type: application/json
*
* {
* "message":"Annotation deleted"
* }
* }</pre>
*
* @param id
*/
public void deleteAnnotation(long id) {
throw new RuntimeException("unimplemented");
}
/**
* This can be called to create an api token and store it for later use as long as you
* have the admin credentials for basic auth. This is preferred to continuing to
* passing basic auth for admin privileges. The permissions can now be narrowed or managed
* in a modular way.
*
* @param namer the principal name for the privelege
* @param role the Grafana role
* @param ttl Length of validity for the granted api token
* @param keyfilePath The path of the token. If it is present it will simply be used.
* @param un The basic auth username for the Admin role
* @param pw The basic auth password for the Admin role
*/
public void cacheApiToken(Supplier<String> namer, String role, long ttl, Path keyfilePath, String un, String pw) {
if (!Files.exists(keyfilePath)) {
GrafanaClientConfig basicClientConfig = config.copy();
basicClientConfig = basicClientConfig.basicAuth(un, pw);
GrafanaClient apiClient = new GrafanaClient(basicClientConfig);
String keyName = namer.get();
ApiToken apiToken = apiClient.createApiToken(keyName, role, ttl);
try {
if (keyfilePath.toString().contains(File.separator)) {
Files.createDirectories(keyfilePath.getParent(),
PosixFilePermissions.asFileAttribute(PosixFilePermissions.fromString("rwxrwx---")));
}
Files.writeString(keyfilePath, apiToken.getKey());
} catch (Exception e) {
throw new RuntimeException(e);
}
}
GrafanaMetricsAnnotator.AuthWrapper authHeaderSupplier = new GrafanaMetricsAnnotator.AuthWrapper(
"Authorization",
new GrafanaKeyFileReader(keyfilePath),
s -> "Bearer " + s
);
config.addHeaderSource(authHeaderSupplier);
}
public ApiToken createApiToken(String name, String role, long ttl) {
ApiTokenRequest r = new ApiTokenRequest(name, role, ttl);
Optional<ApiToken> token = postApiTokenRequest(r, "gen api token");
if (token.isPresent()) {
logger.info("authorized grafana client via built-in authorizer");
return token.get();
} else {
Path path = NBEnvironment.INSTANCE
.interpolate("$NBSTATEDIR/grafana/grafana.db")
.or(() -> Optional.of("~/.nosqlbench/grafana/grafana_apikey"))
.map(Path::of).orElseThrow();
throw new RuntimeException("Unable to authorize local grafana client with any" +
" built in local authorizer. Please store a grafana API key at:\n" + path
+ "\n and start again.");
}
}
private Optional<ApiToken> postApiTokenRequest(Object request, String desc) {
HttpRequest rq = config.newJsonPOST("api/auth/keys", request);
HttpClient client = config.newClient();
HttpResponse<String> response = null;
try {
response = client.send(rq, HttpResponse.BodyHandlers.ofString());
} catch (Exception e) {
if (e.getMessage() != null && e.getMessage().contains("WWW-Authenticate header missing")) {
try {
Optional<ApiToken> token = Optional.empty();
if (request instanceof ApiTokenRequest apirq) {
token = RawSocketInjector.submit(apirq, rq);
if (token.isPresent()) {
return token;
}
}
} catch (Exception e2) {
logger.error("Error while using secondary method to init grafana client key after auth failure: " + e2, e2);
throw e2;
}
}
}
if (response.statusCode() < 200 || response.statusCode() >= 300) {
throw new RuntimeException("Request to grafana failed with status code " + response.statusCode() + "\n" +
" while trying to '" + desc + "'\n at baseuri " + config.getBaseUri() + ": " + response.body());
}
String body = response.body();
ApiToken result = gson.fromJson(body, ApiToken.class);
return Optional.ofNullable(result);
}
public <T> T doProxyQuery(String dsname, String path, String query, TypeToken<? extends T> asType) {
GDataSource datasource = getCachedDatasource(dsname);
long dsid = datasource.getId();
String composedQuery = path;
if (query != null && !query.isBlank()) {
composedQuery = composedQuery + "?" + query;
}
HttpClient client = config.newClient();
HttpRequest.Builder rqb =
config.newRequest("api/datasources/proxy/" + dsid + "/" + composedQuery);
rqb.setHeader("Accept", "application/json");
rqb = rqb.GET();
HttpResponse<String> response = null;
try {
response = client.send(rqb.build(), HttpResponse.BodyHandlers.ofString());
} catch (Exception e) {
throw new RuntimeException(e);
}
if (response.statusCode() < 200 || response.statusCode() >= 300) {
throw new RuntimeException("Executing proxy query failed with status code " + response.statusCode() +
" at baseuri " + config.getBaseUri() + ": " + response.body() + " for datasource '" + dsid + "' and query '" + query + "'");
}
String body = response.body();
T result = gson.fromJson(body, asType.getType());
return result;
}
private GDataSource getCachedDatasource(String dsname) {
return getCachedDatasources().stream()
.filter(gd -> gd.getName().equals(dsname))
.findFirst()
.orElseThrow();
}
public Map<String, Set<String>> resolveAllTplValues(List<GTemplate> tpls, String timeStart, String timeEnd) {
Map<String, Set<String>> allTplValues = new HashMap<>();
for (GTemplate gTemplate : tpls) {
Set<String> strings = resolveTplValues(gTemplate, timeStart, timeEnd, getCachedDatasources());
allTplValues.put(gTemplate.getName(), strings);
}
return allTplValues;
}
public Set<String> resolveTplValues(GTemplate tpl, String timeStart, String timeEnd, List<GDataSource> dss) {
Set<String> resolved = new HashSet<>();
List<String> values = tpl.getCurrent().getValues();
if (values.size() == 1 && values.get(0).equals("$__all")) {
if (tpl.getAllValue() != null && !tpl.getAllValue().isBlank()) {
resolved.add(tpl.getAllValue());
} else {
String dsname = tpl.getDatasource();
Optional<GDataSource> dso = dss.stream().filter(n -> n.getName().equals(dsname)).findFirst();
GDataSource ds = dso.orElseThrow();
String query = tpl.getQuery();
String formatted = formatSeriesQuery(ds.getType(), query, timeStart, timeEnd);
if (ds.getType().equals("prometheus")) {
long startSpec = GTimeUnit.epochSecondsFor(timeStart);
long endSpec = GTimeUnit.epochSecondsFor(timeEnd);
String q = "api/v1/series?match[]=" + URLEncoder.encode(tpl.getQuery()) +
"&start=" + startSpec +
"&end=" + endSpec;
PromSeriesLookupResult psr = doProxyQuery("prometheus", "api/v1/series", q, new TypeToken<PromSeriesLookupResult>() {
});
for (PromSeriesLookupResult.Element elem : psr.getData()) {
String elementSpec = elem.toString();
String regex = tpl.getRegex();
if (regex != null && !regex.isBlank()) {
if (regex.startsWith("/")) {
regex = regex.substring(1, regex.length() - 2);
}
Pattern p = Pattern.compile(regex);
Matcher m = p.matcher(elementSpec);
if (m.find()) {
String group = m.group(1);
resolved.add(group);
}
} else {
resolved.add(elem.toString());
}
}
} else {
throw new RuntimeException("datasource type not supported yet for template values: '" + ds.getType() + "'");
}
}
} else {
for (String value : tpl.getCurrent().getValues()) {
resolved.add(value);
}
}
return resolved;
}
private String formatSeriesQuery(String type, String query, String startTime, String endTime) {
if (type.equals("prometheus")) {
long startSpec = GTimeUnit.epochSecondsFor(startTime);
long endSpec = GTimeUnit.epochSecondsFor(endTime);
return "api/v1/series?match[]=" + URLEncoder.encode(query, StandardCharsets.UTF_8) + "&start=" + startSpec +
"&end=" + endSpec;
} else {
throw new RuntimeException("Unknown query target type '" + type + "'");
}
}
public List<GDataSource> getDatasources() {
HttpClient client = config.newClient();
HttpRequest.Builder rqb = config.newRequest("api/datasources");
rqb = rqb.GET();
HttpResponse<String> response = null;
try {
response = client.send(rqb.build(), HttpResponse.BodyHandlers.ofString());
} catch (Exception e) {
throw new RuntimeException(e);
}
if (response.statusCode() < 200 || response.statusCode() >= 300) {
throw new RuntimeException("Getting datasources failed with status code " + response.statusCode() +
" at baseuri " + config.getBaseUri() + ": " + response.body());
}
String body = response.body();
Type dsListType = new TypeToken<List<GDataSource>>() {
}.getType();
List<GDataSource> dataSourcesList = gson.fromJson(body, dsListType);
return dataSourcesList;
}
private List<GDataSource> getCachedDatasources() {
if (datasources == null) {
datasources = getDatasources();
}
return datasources;
}
public GRangeResult doRangeQuery(String datasource, String expr, String startSpec, String endSpec) {
GDataSource ds = getCachedDatasource(datasource);
if (ds.getType().equals("prometheus")) {
long start = GTimeUnit.epochSecondsFor(startSpec);
long end = GTimeUnit.epochSecondsFor(endSpec);
// http://44.242.139.57:3000/api/datasources/proxy/1/api/v1/query_range?query=result%7Btype%3D%22avg_rate%22%2Cavg_of%3D%221m%22%2Calias%3D~%22keyvalue_main_001%22%7D&start=1608534000&end=1608620400&step=300
// http://44.242.139.57:3000/api/datasources/proxy/1/api/v1/query_range?query=result%7Btype%3D%22avg_rate%22%2Cavg_of%3D%221m%22%2Calias%3D%7E%22%28.*%29%22%7D&start=1608611971&end=1608622771&step=300
String path = "api/v1/query_range?query=" +
URLEncoder.encode(expr) + "&start=" + start + "&end=" + end + "&step=300";
PromQueryResult<PMatrixData> vectorData = doProxyQuery(
datasource,
"api/v1/query_range",
"query=" + URLEncoder.encode(expr) + "&start=" + start + "&end=" + end + "&step=300",
new TypeToken<PromQueryResult<PMatrixData>>() {
});
logger.info(vectorData);
return null;
} else {
throw new RuntimeException("data source " + datasource + " is not yet supported.");
}
// TODO: Distinguish between datasources named "prometheus" and data source types "prometheus"
// TODO: Figure out how to set step equivalently
}
}

View File

@ -0,0 +1,191 @@
/*
* Copyright (c) 2022 nosqlbench
*
* 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 io.nosqlbench.engine.clients.grafana;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import java.net.Authenticator;
import java.net.PasswordAuthentication;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.time.Duration;
import java.util.*;
import java.util.function.Supplier;
public class GrafanaClientConfig {
private static final Gson gson = new GsonBuilder().setPrettyPrinting().create();
@JsonProperty("baseuri")
private URI baseUrl;
@JsonProperty("timeoutms" )
private int timeoutms;
private final List<Authenticator> authenticators = new ArrayList<>();
private final List<Supplier<Map<String, String>>> headerSources = new ArrayList<>();
public GrafanaClientConfig() {
}
private GrafanaClientConfig(URI baseUrl, int timeoutms, List<Authenticator> authenticators,
List<Supplier<Map<String, String>>> headerSources) {
this.baseUrl = baseUrl;
this.timeoutms = timeoutms;
this.authenticators.addAll(authenticators);
this.headerSources.addAll(headerSources);
}
public GrafanaClientConfig copy() {
return new GrafanaClientConfig(baseUrl, timeoutms, authenticators, headerSources);
}
public GrafanaClientConfig basicAuth(String username, String pw) {
Objects.requireNonNull(username);
String authPw = pw != null ? pw : "";
Authenticator basicAuth = new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(username, authPw.toCharArray());
}
};
addAuthenticator(basicAuth);
addHeader("Authorization", encodeBasicAuth(username, authPw));
return this;
}
public GrafanaClientConfig addAuthenticator(Authenticator authenticator) {
authenticators.add(authenticator);
return this;
}
public GrafanaClientConfig addHeader(String headername, String... headervals) {
String headerVal = String.join(";", Arrays.asList(headervals));
addHeaderSource(() -> Map.of(headername, headerVal));
return this;
}
/**
* Add a dynamic header source to be used for every new request.
* Each source provides a map of new headers. If key or value of any
* entry is null or empty, that entry is skipped. Otherwise, they are
* computed and added to every request anew.
*
* @param headerSource A source of new headers
* @return this GrafanaClientConfig, for method chaining
*/
public GrafanaClientConfig addHeaderSource(Supplier<Map<String, String>> headerSource) {
this.headerSources.add(headerSource);
return this;
}
public LinkedHashMap<String, String> getHeaders() {
LinkedHashMap<String, String> headers = new LinkedHashMap<>();
this.headerSources.forEach(hs -> {
Map<String, String> entries = hs.get();
entries.forEach((k, v) -> {
if (k != null && v != null && !k.isEmpty() && !v.isEmpty()) {
headers.put(k, v);
}
});
});
return headers;
}
public HttpClient newClient() {
HttpClient.Builder cb = HttpClient.newBuilder();
if (timeoutms > 0) {
cb.connectTimeout(Duration.ofMillis(timeoutms));
}
for (Authenticator authenticator : authenticators) {
cb.authenticator(authenticator);
}
HttpClient client = cb.build();
return client;
}
private URI makeUri(String pathAndQuery) {
try {
return new URI(getBaseUri().toString() + pathAndQuery);
} catch (URISyntaxException e) {
throw new RuntimeException(e);
}
}
public HttpRequest.Builder newRequest(String path) {
URI requestUri = makeUri(path);
HttpRequest.Builder rqb = HttpRequest.newBuilder(requestUri);
if (timeoutms > 0) {
rqb.timeout(Duration.ofMillis(timeoutms));
}
getHeaders().forEach(rqb::setHeader);
return rqb;
}
public GrafanaClientConfig setBaseUri(String baseuri) {
try {
URI uri = new URI(baseuri);
String userinfo = uri.getRawUserInfo();
if (userinfo != null) {
String[] unpw = userinfo.split(":");
basicAuth(unpw[0], unpw.length == 2 ? unpw[1] : "");
uri = new URI(baseuri.replace(userinfo + "@", ""));
}
this.baseUrl = uri;
} catch (URISyntaxException e) {
throw new RuntimeException(e);
}
return this;
}
private static String encodeBasicAuth(String username, String password) {
return "Basic " + Base64.getEncoder().encodeToString((username + ":" + password).getBytes());
}
public static GrafanaClientConfig fromJson(CharSequence json) {
GrafanaClientConfig grafanaClientConfig = gson.fromJson(json.toString(), GrafanaClientConfig.class);
return grafanaClientConfig;
}
public URI getBaseUri() {
return baseUrl;
}
public HttpRequest newJsonPOST(String pathAndParams, Object rq) {
HttpRequest.Builder rqb = newRequest(pathAndParams);
String body = gson.toJson(rq);
rqb = rqb.POST(HttpRequest.BodyPublishers.ofString(body));
rqb = rqb.setHeader("Content-Type", "application/json");
return rqb.build();
}
public int getTimeoutms() {
return timeoutms;
}
public void setTimeoutms(int timeoutms) {
this.timeoutms = timeoutms;
}
}

View File

@ -0,0 +1,57 @@
/*
* Copyright (c) 2022 nosqlbench
*
* 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 io.nosqlbench.engine.clients.grafana;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.function.Supplier;
public class GrafanaKeyFileReader implements Supplier<String> {
private final static Logger logger = LogManager.getLogger("ANNOTATORS" );
private final Path keyfilePath;
public GrafanaKeyFileReader(Path path) {
this.keyfilePath = path;
}
public GrafanaKeyFileReader(String sourcePath) {
this.keyfilePath = Path.of(sourcePath);
}
@Override
public String get() {
if (!Files.exists(keyfilePath)) {
logger.warn("apikeyfile does not exist at '" + keyfilePath);
return null;
} else {
try {
String apikey = Files.readString(keyfilePath, StandardCharsets.UTF_8);
apikey = apikey.trim();
return apikey;
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}

View File

@ -0,0 +1,22 @@
/*
* Copyright (c) 2022 nosqlbench
*
* 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 io.nosqlbench.engine.clients.grafana.analyzer;
import java.util.LinkedHashMap;
public class GQueryResult extends LinkedHashMap<String, Object> {
}

View File

@ -0,0 +1,196 @@
/*
* Copyright (c) 2022 nosqlbench
*
* 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 io.nosqlbench.engine.clients.grafana.analyzer;
import io.nosqlbench.api.metadata.SystemId;
import io.nosqlbench.engine.clients.grafana.GRangeResult;
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 org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.nio.file.Path;
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Supplier;
import java.util.stream.Collectors;
public class GrafanaRegionAnalyzer implements Runnable {
private final Logger logger = LogManager.getLogger(GrafanaRegionAnalyzer.class);
private String baseUrl = "";
// private String dashboardId = "";
private GrafanaClient gclient;
public static void main(String[] args) {
GrafanaRegionAnalyzer analyzer = new GrafanaRegionAnalyzer();
analyzer.setBaseUrl("http://<host>:<port>/");
// analyzer.setDashboardId("<<dashboard_id>>");
analyzer.run();
}
private GrafanaClient getClient() {
if (this.gclient == null) {
GrafanaClientConfig ccfg = new GrafanaClientConfig();
ccfg.setBaseUri(baseUrl);
GrafanaClient newclient = new GrafanaClient(ccfg);
Supplier<String> namer = () -> "nosqlbench-" + SystemId.getNodeId() + "-" + System.currentTimeMillis();
newclient.cacheApiToken(namer, "Admin", Long.MAX_VALUE, Path.of("grafana_apikey"), "admin", "admin");
this.gclient = newclient;
}
return this.gclient;
}
public void setBaseUrl(String url) {
this.baseUrl = url;
}
public GSnapshotInfo createSnapshotForAnnotation(GAnnotation anno, GDashboard dashboard, String snid) {
//session: scenario_20201215_050435_240
//[2020-12-15T05:04:37.232Z[GMT] - 2020-12-15T05:04:37.232Z[GMT]]
//span:interval
//details:
// params: ActivityDef:(4)/{keycount=5000000000L, hosts=node1, main-cycles=500, threads=1, workload=./keyvalue.yaml, cycles=2, stride=2, tags=phase:schema, password=cassandra, rf=3, pooling=16:16:500, driver=cql, rampup-cycles=5000000000, alias=keyvalue_default_schema, valuecount=5000000000L, errors=count, username=cassandra}
//labels:
// layer: Activity
// alias: keyvalue_default_schema
// driver: cql
// workload: ./keyvalue.yaml
// session: scenario_20201215_050435_240
// span: interval
// appname: nosqlbench
// "time": {
// "from": "2020-12-15T04:19:54.943Z",
// "raw": {
// "from": "2020-12-15T04:19:54.943Z",
// "to": "2020-12-15T15:25:11.743Z"
// },
// "to": "2020-12-15T15:25:11.743Z"
// },
ZoneId zoneid = ZoneId.of("GMT");
ZonedDateTime startTime = ZonedDateTime.ofInstant(Instant.ofEpochMilli(anno.getTime()), zoneid);
ZonedDateTime endTime = ZonedDateTime.ofInstant(Instant.ofEpochMilli(anno.getTimeEnd()), zoneid);
String startIsoInstant = startTime.format(DateTimeFormatter.ISO_INSTANT);
String endIsoInstant = endTime.format(DateTimeFormatter.ISO_INSTANT);
dashboard.getTime().setFrom(startIsoInstant);
dashboard.getTime().setTo(endIsoInstant);
GSnapshotInfo snapshotInfo = gclient.createSnapshot(dashboard, snid);
return snapshotInfo;
}
public void getQueries(GDashboard db) {
List<GDataSource> datasources = getClient().getDatasources();
List<GPanelDef> mainpanels = db.getPanels().stream()
.filter(p -> p.getType().equals("graph"))
.collect(Collectors.toList());
Map<String, Set<String>> tplValues = getClient().resolveAllTplValues(
db.getTemplating().getList(),
db.getTime().getFrom(),
db.getTime().getTo());
GStitcher stitcher = new GStitcher(tplValues);
for (GPanelDef mainpanel : mainpanels) {
long id = mainpanel.getId();
String title = mainpanel.getTitle();
String description = mainpanel.getDescription();
List<GPanelDef> panels = mainpanel.getPanels();
String datasource = mainpanel.getDatasource();
Map<String, Object> fieldConfig = mainpanel.getFieldConfig();
Map<String, String> options = mainpanel.getOptions();
List<GPanelDef.GTarget> targets = mainpanel.getTargets();
logger.info("targets:\n" + targets);
for (GPanelDef.GTarget target : targets) {
String expr = target.getExpr();
expr = stitcher.stitchRegex(expr);
// expr = GStitcher.resolve(expr,tplValues,GStitcher.Regex);
logger.info("expr now:" + expr);
GRangeResult result = getClient().doRangeQuery(mainpanel.getDatasource(), expr, db.getTime().getFrom(), db.getTime().getTo());
// GQueryResult gqr = getClient().doProxyQuery(mainpanel.getDatasource(), expr, new TypeToken<GQueryResult>() {});
logger.info(result);
}
//logger.info(mainpanel);
}
logger.info(mainpanels.size() + " graphs...");
//http://44.242.139.57:3000/api/datasources/proxy/1/
// api/v1/query_range?query= result{
// type="avg_rate",
// avg_of="1m",
// alias=~"(keyvalue_default_main|keyvalue_default_rampup|keyvalue_default_schema|keyvalue_main_001
// |keyvalue_rampup_001|keyvalue_schema_001)"}&start=1607996100&end=1608600900&step=300
}
@Override
public void run() {
GrafanaClient client = getClient();
GDashboard.fromFile("db.json");
//List<DashboardInfo> dashboards = gclient.findDashboards();
// GDashboardMeta dashboardMeta = client.getDashboardByUid(dashboardId);
// List<GSnapshotInfo> snapshots = client.findSnapshots();
//
// List<GAnnotation> annotations = client.findAnnotations(By.tag("appname:nosqlbench,layer:Activity"));
// List<GAnnotation> mainActivityAnno = annotations.stream()
// .filter(s -> s.getTagMap().getOrDefault("alias", "").contains("main"))
// .filter(s -> s.getTagMap().getOrDefault("span", "").contains("interval"))
// .sorted(Comparator.comparing(t -> t.getTime()))
// .collect(Collectors.toList());
//
//
// for (GAnnotation anno : mainActivityAnno) {
// logger.info("creating data snapshot for " + anno);
// long start = anno.getTime();
// long end = anno.getTimeEnd();
// String snapshotKey = "ss-" + dashboardId + "-" + anno.getId();
// Optional<GSnapshot> snapshot = client.findSnapshotBykeyOptionally(snapshotKey);
// if (!snapshot.isPresent()) {
// GSnapshotInfo info = createSnapshotForAnnotation(anno, dashboardMeta.getDashboard(), snapshotKey);
// GSnapshot newsnapshot = client.findSnapshotBykey(info.getKey());
// }
// }
logger.info("end");
}
public GDashboard getDashboard(String dbUid) {
return getClient().getDashboardByUid(dbUid).getDashboard();
}
}

View File

@ -0,0 +1,859 @@
{
"meta": {
"isSnapshot": true,
"type": "snapshot",
"canSave": false,
"canEdit": false,
"canAdmin": false,
"canStar": false,
"slug": "",
"url": "",
"expires": "2070-12-05T06:28:42Z",
"created": "2020-12-17T06:28:42Z",
"updated": "0001-01-01T00:00:00Z",
"updatedBy": "",
"createdBy": "",
"version": 0,
"hasAcl": false,
"isFolder": false,
"folderId": 0,
"folderTitle": "",
"folderUrl": "",
"provisioned": false,
"provisionedExternalId": ""
},
"dashboard": {
"annotations": {
"list": [
{
"datasource": "-- Grafana --",
"enable": true,
"hide": false,
"iconColor": "#96D98D",
"limit": 1000.0,
"name": "ShowMatching",
"showIn": 0.0,
"tags": [
"appname:nosqlbench",
"span:$span",
"layer:$layer"
],
"type": "tags"
},
{
"builtIn": 1.0,
"datasource": "-- Grafana --",
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
"limit": 100.0,
"name": "Annotations \u0026 Alerts",
"showIn": 0.0,
"type": "dashboard"
},
{
"datasource": "-- Grafana --",
"enable": false,
"hide": false,
"iconColor": "rgba(255, 96, 96, 1)",
"limit": 1000.0,
"matchAny": true,
"name": "ShowAll",
"showIn": 0.0,
"tags": [
"appname:nosqlbench"
],
"type": "tags"
}
]
},
"description": "Basic Dashboard with Annotations for NoSQLBench 4",
"editable": true,
"graphToolTip": 0,
"id": 1,
"iteration": 1607542677976,
"links": [],
"panels": [
{
"collapsed": true,
"gridPos": {
"h": "1",
"w": "24",
"x": "0",
"y": "0"
},
"id": 130,
"panels": [
{
"collapsed": false,
"description": "",
"fieldConfig": {
"defaults": {
"custom": {}
},
"overrides": []
},
"gridPos": {
"h": "4",
"w": "4",
"x": "0",
"y": "1"
},
"id": 132,
"options": {
"content": "This selects the specific NoSQLBench alias, which includes workload, named scenario, and step components.\n",
"mode": "markdown"
},
"pluginVersion": "7.3.4",
"targets": [
{
"queryType": "randomWalk",
"refId": "A"
}
],
"title": "Alias",
"type": "text"
},
{
"collapsed": false,
"fieldConfig": {
"defaults": {
"custom": {}
},
"overrides": []
},
"gridPos": {
"h": "4",
"w": "4",
"x": "4",
"y": "1"
},
"id": 133,
"options": {
"content": "This chooses an Annotation Layer to focus on. Each layer is a different shell of execution within NoSQLBench. \n",
"mode": "markdown"
},
"pluginVersion": "7.3.4",
"targets": [
{
"queryType": "randomWalk",
"refId": "A"
}
],
"title": "AnLayer",
"type": "text"
},
{
"collapsed": false,
"fieldConfig": {
"defaults": {
"custom": {}
},
"overrides": []
},
"gridPos": {
"h": "4",
"w": "4",
"x": "8",
"y": "1"
},
"id": 134,
"options": {
"content": "This chooses an Annotation Layer to focus on. An instant is a point in time. An Interval is defined by two instants representing start and end times.",
"mode": "markdown"
},
"pluginVersion": "7.3.4",
"targets": [
{
"queryType": "randomWalk",
"refId": "A"
}
],
"title": "AnSpan",
"type": "text"
}
],
"title": "NoSQLBench Controls Guide",
"type": "row"
},
{
"collapsed": true,
"gridPos": {
"h": "1",
"w": "24",
"x": "0",
"y": "1"
},
"id": 115,
"panels": [
{
"collapsed": false,
"fieldConfig": {
"defaults": {
"custom": {}
},
"overrides": []
},
"gridPos": {
"h": "2",
"w": "24",
"x": "0",
"y": "2"
},
"id": 128,
"options": {
"content": "The descriptions below are in the same position as the metric they desscribe.\nYou can collapse this row when you no longer need the descriptions.\nSome of the descriptions have additional details if you scroll with\nyour mouse wheel.\n\n\n",
"mode": "markdown"
},
"pluginVersion": "7.3.4",
"title": "",
"type": "text"
},
{
"collapsed": false,
"description": "",
"fieldConfig": {
"defaults": {
"custom": {}
},
"overrides": []
},
"gridPos": {
"h": "3",
"w": "6",
"x": "0",
"y": "4"
},
"id": 117,
"options": {
"content": "These two metrics show the 1-minute averaged ops per second for all ops (successes and errors) and succesful ops (no errors during execution) separately.\n\nWhen there are no errors, these metrics should be the same. When there are errors, there will be a difference. In that case, you can look at the error metrics to learn more about what is happening.\n\nBy comparing these two metrics for any activity, you have a quick first-glance sanity check that your tests are configured properly and\nthat there are no serious configuration or resource issues. In addition, this is the primary throughput metric.\n\n\n\n\n\n",
"mode": "markdown"
},
"pluginVersion": "7.3.4",
"title": "Ops and Successful Ops",
"type": "text"
},
{
"collapsed": false,
"description": "",
"fieldConfig": {
"defaults": {
"custom": {}
},
"overrides": []
},
"gridPos": {
"h": "3",
"w": "6",
"x": "6",
"y": "4"
},
"id": 118,
"options": {
"content": "When an exception is caught and counted in a scenario, a specially named metric is emited which includes a simplified name of the error type.\nEach of these metrics is created as needed in the NoSQLBench runtime.\n\nThese metrics are formated with a name pattern of `errorcounts.NAME` where NAME is the simple name\nof the exception class which was caught during the operation. For example, For an activity\nnamed foo (with alias\u003dfoo), you would expect an exception named \u0027FooTimeoutException\u0027 to be shown\nwith a metric name of ...`foo.errorcounts.FooTimeoutException`\n\n\n\n\n",
"mode": "markdown"
},
"pluginVersion": "7.3.4",
"title": "Error Counts",
"type": "text"
},
{
"collapsed": false,
"description": "",
"fieldConfig": {
"defaults": {
"custom": {}
},
"overrides": []
},
"gridPos": {
"h": "3",
"w": "6",
"x": "12",
"y": "4"
},
"id": 119,
"options": {
"content": "The service time distribution is an HDR histogram that represents the time elapsed from when an operation is submitted for execution to when its result has been received.\n\nFrom the vantage point of the NoSQLBench runtime, this is a client-side metric. Thus, the service time captures the processing time that an application would see, including driver logic, wire time (transport), and server-side processing. \n\nThis metric does not include the waittime or the responsetime metrics. These metrics are only meaningful (and provided) when a `cyclerate\u003d` is provided to an activity. When computing the responsetime metric, the servicetime is added to the waittime for a given operation.\n",
"mode": "markdown"
},
"pluginVersion": "7.3.4",
"title": "Service Time Distribution",
"type": "text"
},
{
"collapsed": false,
"description": "",
"fieldConfig": {
"defaults": {
"custom": {}
},
"overrides": []
},
"gridPos": {
"h": "3",
"w": "6",
"x": "18",
"y": "4"
},
"id": 120,
"options": {
"content": "The tries distribution is an HDR histogram which represents how many times an operation was submitted. In a well balanced system,\ntries should be 1 across the board.\n\nEvery operation which is executed within NoSQLBench should have a `tries` metric. For example, with CQL, the number of times an operation is submitted\nbefore it is succesful is ideally 1. If you are overloading your target system, thus forcing resource contention, you may see operations timeout.\n\nBy default, NoSQLBench will try up to 10 times to submit operations before giving up and throwing an error for an operation.\n\nThe tries metric can be used as a low-noise indicator of system saturation. For example, if you are running a system marginally\nbeyond its capacity, the tries for operations will go above 1 at the higher percentiles such as P99. If you increase the load even further, more retries\nwill be needed and less work will be completed, thus showing higher retries at even lower percentiles, like P95, for example.\n\nAs such, you can use the tries metric as an indicator of relative saturation.",
"mode": "markdown"
},
"pluginVersion": "7.3.4",
"title": "Op Tries Distribution",
"type": "text"
},
{
"collapsed": false,
"description": "",
"fieldConfig": {
"defaults": {
"custom": {}
},
"overrides": []
},
"gridPos": {
"h": "3",
"w": "6",
"x": "0",
"y": "7"
},
"id": 121,
"options": {
"content": "This panel shows the P75 service times for the internal stages of NoSQLBench processing. It provides a sanity check to ensure that the client processing time is low and predictable. \n\nIf there are spikes in this data, then you are likely trying to run your workloads on insufficient client hardware. In order to ensure high fidelity results in the other metrics, the client\nneeds to be capable of driving the workload without saturating or introducing signifcant local resource contention.\n\n- **read-input** - the time it takes for a worker thread to acquire a stride (range of cycles) for execution.\n- **bind** - the time it takes to convert a cycle value into a set of fields for us in an operation.\n- **execute** - the time it takes to submit work to a protocol-specific driver.",
"mode": "markdown"
},
"pluginVersion": "7.3.4",
"title": "P75 NB Internals",
"type": "text"
},
{
"collapsed": false,
"description": "",
"fieldConfig": {
"defaults": {
"custom": {}
},
"overrides": []
},
"gridPos": {
"h": "3",
"w": "6",
"x": "6",
"y": "7"
},
"id": 122,
"options": {
"content": "This panel shows the P99 service times for the internal stages of NoSQLBench processing. It provides a sanity check to ensure that the client processing time is low and predictable. \n\nIf there are spikes in this data, then you are likely trying to run your workloads on insufficient client hardware. In order to ensure high fidelity results in the other metrics, the client\nneeds to be capable of driving the workload without saturating or introducing signifcant local resource contention.\n\n- **read-input** - the time it takes for a worker thread to acquire a stride (range of cycles) for execution.\n- **bind** - the time it takes to convert a cycle value into a set of fields for us in an operation.\n- **execute** - the time it takes to submit work to a protocol-specific driver.",
"mode": "markdown"
},
"pluginVersion": "7.3.4",
"title": "P99 NB Internals",
"type": "text"
},
{
"collapsed": false,
"description": "",
"fieldConfig": {
"defaults": {
"custom": {}
},
"overrides": []
},
"gridPos": {
"h": "3",
"w": "6",
"x": "12",
"y": "7"
},
"id": 123,
"options": {
"content": "This panel simply breaks out the service time range in a simpler view. This is a good metric to look at when you want to know what the best and worse case value is for any given histogram interval.\n\nThese values come from a discrete HDR histogram reservoir. They are the actual best and worst service times, unaffected by time-decaying reservoir logic.\n",
"mode": "markdown"
},
"pluginVersion": "7.3.4",
"title": "Service Time Range",
"type": "text"
},
{
"collapsed": false,
"description": "",
"fieldConfig": {
"defaults": {
"custom": {}
},
"overrides": []
},
"gridPos": {
"h": "3",
"w": "6",
"x": "18",
"y": "7"
},
"id": 124,
"options": {
"content": "This is a simple visual reference for the cycle count within each activity. This makes it easy to see the relative progress of an activity over time.",
"mode": "markdown"
},
"pluginVersion": "7.3.4",
"title": "Cycle Count",
"type": "text"
}
],
"title": "NoSQLBench Metrics Guide",
"type": "row"
},
{
"collapsed": false,
"gridPos": {
"h": "1",
"w": "24",
"x": "0",
"y": "2"
},
"id": 91,
"panels": [],
"title": "NoSQLBench Metrics",
"type": "row"
},
{
"collapsed": false,
"datasource": "prometheus",
"description": "",
"fieldConfig": {
"defaults": {
"custom": {},
"links": []
},
"overrides": []
},
"gridPos": {
"h": "6",
"w": "6",
"x": "0",
"y": "3"
},
"id": 95,
"options": {
"alertThreshold": "true"
},
"pluginVersion": "7.3.4",
"targets": [
{
"expr": "result{type\u003d\"avg_rate\",avg_of\u003d\"1m\",alias\u003d~\"$alias\"}",
"interval": "",
"legendFormat": "{{alias}}-allops",
"refId": "C"
},
{
"expr": "result{type\u003d\"avg_rate\",avg_of\u003d\"1m\",alias\u003d~\"$alias\"}",
"interval": "",
"legendFormat": "{{alias}}-allops",
"refId": "B"
}
],
"title": "Ops and Successful Ops",
"type": "graph"
},
{
"collapsed": false,
"datasource": "prometheus",
"fieldConfig": {
"defaults": {
"custom": {},
"links": []
},
"overrides": []
},
"gridPos": {
"h": "6",
"w": "6",
"x": "6",
"y": "3"
},
"id": 93,
"options": {
"alertThreshold": "true"
},
"pluginVersion": "7.3.4",
"targets": [
{
"expr": "{__name__\u003d~\"errorcounts.*\",alias\u003d~\"$alias\"}",
"interval": "",
"legendFormat": "{{alias}}-{{error}}",
"refId": "B"
}
],
"title": "Error Counts",
"type": "graph"
},
{
"collapsed": false,
"datasource": "prometheus",
"fieldConfig": {
"defaults": {
"custom": {},
"links": []
},
"overrides": []
},
"gridPos": {
"h": "6",
"w": "6",
"x": "12",
"y": "3"
},
"id": 97,
"options": {
"alertThreshold": "true"
},
"pluginVersion": "7.3.4",
"targets": [
{
"expr": "result_success{type\u003d\"pctile\",alias\u003d~\"$Alias\"}",
"hide": "false",
"interval": "",
"legendFormat": "{{alias}}-p{{pctile}}",
"refId": "B"
},
{
"expr": "result_success{type\u003d\"pctile\",alias\u003d~\"$alias\"}",
"hide": "false",
"interval": "",
"legendFormat": "{{alias}}-p{{pctile}}",
"refId": "C"
}
],
"title": "service time distribution",
"type": "graph"
},
{
"collapsed": false,
"datasource": "prometheus",
"fieldConfig": {
"defaults": {
"custom": {},
"links": []
},
"overrides": []
},
"gridPos": {
"h": "6",
"w": "6",
"x": "18",
"y": "3"
},
"id": 98,
"options": {
"alertThreshold": "true"
},
"pluginVersion": "7.3.4",
"targets": [
{
"expr": "tries{type\u003d\"pctile\",alias\u003d~\"$alias\"}",
"interval": "",
"legendFormat": "{{alias}}-p{{pctile}}",
"refId": "A"
}
],
"title": "op tries distribution",
"type": "graph"
},
{
"collapsed": false,
"datasource": "prometheus",
"fieldConfig": {
"defaults": {
"custom": {},
"links": []
},
"overrides": []
},
"gridPos": {
"h": "6",
"w": "6",
"x": "0",
"y": "9"
},
"id": 99,
"options": {
"alertThreshold": "true"
},
"pluginVersion": "7.3.4",
"targets": [
{
"expr": "{__name__\u003d~\"read_input|bind|execute\",type\u003d\"pctile\",pctile\u003d\"75\",alias\u003d~\"$alias\"}",
"interval": "",
"legendFormat": "{{alias}}-{{__name__}}-p{{pctile}}",
"refId": "B"
}
],
"title": "p75 client overhead",
"type": "graph"
},
{
"collapsed": false,
"datasource": "prometheus",
"fieldConfig": {
"defaults": {
"custom": {},
"links": []
},
"overrides": []
},
"gridPos": {
"h": "6",
"w": "6",
"x": "6",
"y": "9"
},
"id": 111,
"options": {
"alertThreshold": "true"
},
"pluginVersion": "7.3.4",
"targets": [
{
"expr": "{__name__\u003d~\"read_input|bind|execute\",type\u003d\"pctile\",pctile\u003d\"99\",alias\u003d~\"$alias\"}",
"format": "time_series",
"hide": "false",
"instant": "false",
"interval": "",
"intervalFactor": "1",
"legendFormat": "{{alias}}-{{__name__}}-p{{pctile}}",
"refId": "C"
}
],
"title": "p99 client overhead",
"type": "graph"
},
{
"collapsed": false,
"datasource": "prometheus",
"description": "",
"fieldConfig": {
"defaults": {
"custom": {},
"links": []
},
"overrides": []
},
"gridPos": {
"h": "6",
"w": "6",
"x": "12",
"y": "9"
},
"id": 109,
"options": {
"alertThreshold": "true"
},
"pluginVersion": "7.3.4",
"targets": [
{
"expr": "result_success{type\u003d\"pctile\",pctile\u003d\"0\",alias\u003d~\"$alias\"}",
"interval": "",
"legendFormat": "{{alias}}-min",
"refId": "B"
},
{
"expr": "result_success{type\u003d\"pctile\",pctile\u003d\"100\",alias\u003d~\"$alias\"}",
"interval": "",
"legendFormat": "{{alias}}-max",
"refId": "A"
}
],
"title": "service time range",
"type": "graph"
},
{
"collapsed": false,
"datasource": "prometheus",
"fieldConfig": {
"defaults": {
"custom": {},
"links": []
},
"overrides": []
},
"gridPos": {
"h": "6",
"w": "6",
"x": "18",
"y": "9"
},
"id": 113,
"options": {
"alertThreshold": "true"
},
"pluginVersion": "7.3.4",
"targets": [
{
"expr": "cycles_servicetime{type\u003d\"counter\",alias\u003d~\"$alias\"}",
"interval": "",
"legendFormat": "{{alias}}-count",
"refId": "C"
}
],
"title": "Cycle Count",
"type": "graph"
}
],
"refresh": "1m",
"schemaVersion": 26,
"style": "dark",
"tags": [
"NoSQLBench"
],
"templating": {
"list": [
{
"allValue": ".*",
"current": {
"selected": true,
"text": [
"All"
],
"value": [
"$__all"
]
},
"datasource": "prometheus",
"definition": "{appname\u003d\"nosqlbench\"}",
"hide": 0.0,
"includeAll": true,
"label": "Alias",
"multi": true,
"name": "alias",
"options": [],
"query": "{appname\u003d\"nosqlbench\"}",
"refresh": 2.0,
"regex": "/.*alias\u003d\"([^\"]+)\".*/",
"skipUrlSync": false,
"sort": 1.0,
"tagValuesQuery": "",
"tags": [],
"tagsQuery": "",
"type": "query",
"useTags": false
},
{
"allValue": " ",
"current": {
"selected": true,
"text": "Activity",
"value": "Activity"
},
"hide": 0.0,
"includeAll": true,
"label": "AnLayer",
"multi": false,
"name": "layer",
"options": [
{
"selected": false,
"text": "All",
"value": "$__all"
},
{
"selected": false,
"text": "NONE",
"value": "NONE"
},
{
"selected": false,
"text": "CLI",
"value": "CLI"
},
{
"selected": true,
"text": "Scenario",
"value": "Scenario"
},
{
"selected": false,
"text": "Script",
"value": "Script"
},
{
"selected": false,
"text": "Activity",
"value": "Activity"
}
],
"query": "NONE,CLI,Scenario,Script,Activity",
"queryValue": "",
"skipUrlSync": false,
"type": "custom"
},
{
"current": {
"selected": false,
"text": "interval",
"value": "interval"
},
"hide": 0.0,
"includeAll": false,
"label": "AnSpan",
"multi": false,
"name": "span",
"options": [
{
"selected": false,
"text": "NONE",
"value": "NONE"
},
{
"selected": false,
"text": "interval",
"value": "interval"
},
{
"selected": true,
"text": "instant",
"value": "instant"
}
],
"query": "NONE,interval,instant",
"queryValue": "",
"skipUrlSync": false,
"type": "custom"
}
]
},
"time": {
"from": "2020-12-16T23:19:50.623Z",
"to": "2020-12-16T23:35:45.691Z"
},
"timepicker": {
"refresh_intervals": [
"10s",
"30s",
"1m",
"5m",
"15m",
"30m",
"1h",
"2h",
"1d"
],
"time_options": [
"5m",
"15m",
"1h",
"6h",
"12h",
"24h",
"2d",
"7d",
"30d"
]
},
"timezone": "",
"title": "NB4 Dashboard",
"uid": "aIIX1f6Wz",
"version": 1
}
}

View File

@ -0,0 +1,219 @@
/*
* Copyright (c) 2022 nosqlbench
*
* 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 io.nosqlbench.engine.clients.grafana.annotator;
import io.nosqlbench.api.annotations.Annotation;
import io.nosqlbench.api.annotations.Annotator;
import io.nosqlbench.api.config.params.ParamsParser;
import io.nosqlbench.api.config.standard.*;
import io.nosqlbench.api.errors.BasicError;
import io.nosqlbench.api.metadata.SystemId;
import io.nosqlbench.engine.clients.grafana.GrafanaClient;
import io.nosqlbench.engine.clients.grafana.GrafanaClientConfig;
import io.nosqlbench.engine.clients.grafana.transfer.GAnnotation;
import io.nosqlbench.nb.annotations.Service;
import io.nosqlbench.api.errors.OnError;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.nio.file.Path;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;
import java.util.function.Supplier;
@Service(value = Annotator.class, selector = "grafana")
public class GrafanaMetricsAnnotator implements Annotator, NBConfigurable {
private final static Logger logger = LogManager.getLogger("ANNOTATORS");
//private final static Logger annotationsLog = LogManager.getLogger("ANNOTATIONS" );
private OnError onError = OnError.Warn;
private GrafanaClient client;
private Map<String, String> tags = new LinkedHashMap<>();
public GrafanaMetricsAnnotator() {
}
@Override
public void recordAnnotation(Annotation annotation) {
try {
GAnnotation ga = new GAnnotation();
ga.setTime(annotation.getStart());
ga.setTimeEnd(annotation.getEnd());
annotation.getLabels().forEach((k, v) -> {
ga.getTags().add(k + ":" + v);
});
ga.getTags().add("layer:" + annotation.getLayer().toString());
if (annotation.getStart() == annotation.getEnd()) {
ga.getTags().add("span:instant");
} else {
ga.getTags().add("span:interval");
}
Map<String, String> labels = annotation.getLabels();
Optional.ofNullable(labels.get("alertId"))
.map(Integer::parseInt).ifPresent(ga::setAlertId);
ga.setText(annotation.toString());
annotation.getSession();
// Target
Optional.ofNullable(labels.get("type"))
.ifPresent(ga::setType);
Optional.ofNullable(labels.get("id")).map(Integer::valueOf)
.ifPresent(ga::setId);
Optional.ofNullable(labels.get("alertId")).map(Integer::valueOf)
.ifPresent(ga::setAlertId);
Optional.ofNullable(labels.get("dashboardId")).map(Integer::valueOf)
.ifPresent(ga::setDashboardId);
Optional.ofNullable(labels.get("panelId")).map(Integer::valueOf)
.ifPresent(ga::setPanelId);
Optional.ofNullable(labels.get("userId")).map(Integer::valueOf)
.ifPresent(ga::setUserId);
Optional.ofNullable(labels.get("userName"))
.ifPresent(ga::setUserName);
Optional.ofNullable(labels.get("metric"))
.ifPresent(ga::setMetric);
// Details
GAnnotation created = this.client.createAnnotation(ga);
} catch (Exception e) {
switch (onError) {
case Warn:
logger.warn("Error while reporting annotation: " + e.getMessage(), e);
break;
case Throw:
throw e;
}
}
}
@Override
public void applyConfig(NBConfiguration cfg) {
GrafanaClientConfig gc = new GrafanaClientConfig();
gc.setBaseUri(cfg.get("baseurl"));
cfg.getOptional("tags")
.map(t -> ParamsParser.parse(t, false))
.ifPresent(this::setTags);
cfg.getOptional("username")
.ifPresent(
username ->
gc.basicAuth(
username,
cfg.getOptional("password").orElse("")
)
);
Path keyfilePath = null;
Optional<String> optionalApikeyfile = cfg.getEnvOptional("apikeyfile");
Optional<String> optionalApikey = cfg.getOptional("apikey");
if (optionalApikeyfile.isPresent()) {
keyfilePath = optionalApikeyfile.map(Path::of).orElseThrow();
} else if (optionalApikey.isPresent()) {
gc.addHeaderSource(() -> Map.of("Authorization", "Bearer " + optionalApikey.get()));
} else {
throw new BasicError("Undefined keyfile parameters.");
}
cfg.getOptional("onerror").map(OnError::valueOfName).ifPresent(this::setOnError);
this.client = new GrafanaClient(gc);
String keyName = "nosqlbench-" + SystemId.getNodeId() + "-" + System.currentTimeMillis();
Supplier<String> namer = () -> "nosqlbench-" + SystemId.getNodeId() + "-" + System.currentTimeMillis();
this.client.cacheApiToken(namer, "Admin", Long.MAX_VALUE, keyfilePath, "admin", "admin");
}
private void setOnError(OnError onError) {
this.onError = onError;
}
private void setTags(Map<String, String> tags) {
this.tags = tags;
}
@Override
public NBConfigModel getConfigModel() {
return ConfigModel.of(this.getClass())
.add(Param.required("baseurl", String.class)
.setDescription("The base url of the grafana node, like http://localhost:3000/"))
.add(Param.defaultTo("apikeyfile", "$NBSTATEDIR/grafana/grafana_apikey")
.setDescription("The file that contains the api key, supersedes apikey"))
.add(Param.optional("apikey", String.class)
.setDescription("The api key to use, supersedes basic username and password"))
.add(Param.optional("username", String.class)
.setDescription("The username to use for basic auth"))
.add(Param.optional("password", String.class)
.setDescription("The password to use for basic auth"))
.add(Param.defaultTo("tags", "source:nosqlbench")
.setDescription("The tags that identify the annotations, in k:v,... form"))
.add(Param.defaultTo("onerror", "warn")
.setDescription("What to do when an error occurs while posting an annotation"))
.add(Param.defaultTo("timeoutms", 5000)
.setDescription("connect and transport timeout for the HTTP client"))
.asReadOnly();
}
public static class AuthWrapper implements Supplier<Map<String, String>> {
private final Function<String, String> valueMapper;
private final String headerName;
private final Supplier<String> valueSupplier;
public AuthWrapper(String headerName, Supplier<String> valueSupplier, Function<String, String> valueMapper) {
this.headerName = headerName;
this.valueSupplier = valueSupplier;
this.valueMapper = valueMapper;
}
@Override
public Map<String, String> get() {
String value = valueSupplier.get();
if (value != null) {
value = valueMapper.apply(value);
return Map.of(headerName, value);
}
return Map.of();
}
}
}

View File

@ -0,0 +1,138 @@
/*
* Copyright (c) 2022 nosqlbench
*
* 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 io.nosqlbench.engine.clients.grafana.authorizers;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import io.nosqlbench.engine.clients.grafana.ApiToken;
import io.nosqlbench.engine.clients.grafana.transfer.ApiTokenRequest;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import javax.net.SocketFactory;
import java.io.*;
import java.net.Socket;
import java.net.http.HttpRequest;
import java.nio.CharBuffer;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.Optional;
public class RawSocketInjector {
private final static Logger logger = LogManager.getLogger("GRAFANA-SOCKET-AUTH");
public static Optional<ApiToken> submit(ApiTokenRequest apirq, HttpRequest rq) {
Socket clientSocket = null;
InputStream fromServer = null;
OutputStream toServer = null;
try {
SocketFactory socketFactory = SocketFactory.getDefault();
if (!rq.uri().getScheme().equals("http")) {
throw new RuntimeException("URI scheme " + rq.uri().getScheme() + " not supported for auto-key yet.");
}
clientSocket = socketFactory.createSocket(rq.uri().getHost(), rq.uri().getPort());
fromServer = clientSocket.getInputStream();
toServer = clientSocket.getOutputStream();
String rqbody = """
{
"Name": "_NAME_",
"Role": "_ROLE_"
}
"""
.replace("_NAME_", apirq.getName() + "_" + System.currentTimeMillis())
.replace("_ROLE_", apirq.getRole());
String basicAuthDigest = "Basic " + Base64.getEncoder().encodeToString("admin:admin".getBytes(StandardCharsets.UTF_8));
String rqProtocol = """
POST __PATH__ HTTP/1.1
Host: __HOST__:__PORT__
Connection: close
Authorization: __AUTHORIZATION__
User-Agent: **nb**
Accept: */*
Content-Type: application/json
Content-Length: __CONTENT_LENGTH__
""".replace("__PATH__", rq.uri().getPath())
.replace("__CONTENT_LENGTH__", String.valueOf(rqbody.getBytes(StandardCharsets.UTF_8).length))
.replace("__AUTHORIZATION__", basicAuthDigest)
.replace("__HOST__", rq.uri().getHost())
.replace("__PORT__", String.valueOf(rq.uri().getPort()));
CharBuffer rqbuf = CharBuffer.allocate(1000000);
rqbuf.put(rqProtocol)
.put("\r\n")
.put(rqbody)
.put("\r\n");
rqbuf.flip();
String requestContent = rqbuf.toString();
logger.trace(() -> "authorizer request:\n" + requestContent + "\n");
toServer.write(requestContent.getBytes(StandardCharsets.UTF_8));
toServer.flush();
BufferedReader reader = new BufferedReader(new InputStreamReader(fromServer));
CharBuffer inbuf = CharBuffer.allocate(1000000);
while (reader.read(inbuf) >= 0) {
}
inbuf.flip();
String response = inbuf.toString();
logger.trace(() -> "authorizer response:\n" + response + "\n");
String[] headersAndBody = response.split("\r\n\r\n", 2);
String[] statusAndHeaders = headersAndBody[0].split("\r\n", 2);
if (!statusAndHeaders[0].contains("200 OK")) {
logger.error("Status was unexpected: '" + statusAndHeaders[0] + "'");
logger.error("response was:\n" + response);
return Optional.empty();
}
// toServer.close();
// fromServer.close();
// clientSocket.close();
// toServer=null;
// fromServer=null;
// clientSocket=null;
Gson gson = new GsonBuilder().setPrettyPrinting().create();
ApiToken apiToken = gson.fromJson(headersAndBody[1], ApiToken.class);
logger.info(() -> "Authorized local grafana client with Socket client: " + apiToken.toString());
return Optional.of(apiToken);
} catch (Exception e) {
logger.error("error while authorizing grafana client over raw socket: " + e, e);
return Optional.empty();
} finally {
try {
if (toServer != null) {
toServer.close();
}
if (fromServer != null) {
fromServer.close();
}
if (clientSocket != null) {
clientSocket.close();
}
} catch (IOException e) {
logger.error(e);
}
}
}
}

View File

@ -0,0 +1,62 @@
/*
* Copyright (c) 2022 nosqlbench
*
* 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 io.nosqlbench.engine.clients.grafana.transfer;
import java.util.Set;
public class ApiTokenRequest {
public static final Set<String> VALID_ROLES = Set.of("Admin", "Editor", "Viewer");
private final String name;
private final String role;
private final long ttl;
private final int id;
public ApiTokenRequest(String name, String role, long ttl, int id) {
this.name = name;
this.role = checkRole(role);
this.ttl = ttl;
this.id = id;
}
public ApiTokenRequest(String name, String role, long ttl) {
this(name, role, ttl, 1);
}
private String checkRole(String role) {
if (!VALID_ROLES.contains(role)) {
throw new RuntimeException("Role '" + role + "' must be one of " + VALID_ROLES);
}
return role;
}
public int getId() {
return id;
}
public String getName() {
return name;
}
public String getRole() {
return role;
}
public long getTtl() {
return ttl;
}
}

View File

@ -0,0 +1,29 @@
/*
* Copyright (c) 2022 nosqlbench
*
* 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 io.nosqlbench.engine.clients.grafana.transfer;
public class CreateSnapshotRequest {
GDashboard dashboard;
Long expires;
String key;
public CreateSnapshotRequest(GDashboard dashboard, Long expires, String key) {
this.dashboard = dashboard;
this.expires = expires;
this.key = key;
}
}

View File

@ -0,0 +1,199 @@
/*
* Copyright (c) 2022 nosqlbench
*
* 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 io.nosqlbench.engine.clients.grafana.transfer;
import java.util.*;
public class GAnnotation {
private Integer id;
private Integer alertId;
private Integer dashboardId;
private Integer panelId;
private Integer userId;
private String userName;
private String newState;
private String prevState;
private Long time;
private Long timeEnd;
private String text;
private String metric;
private String type;
private final List<String> tags = new ArrayList<>();
// private Map<String, String> tags = new LinkedHashMap<>();
private Object data;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Integer getAlertId() {
return alertId;
}
public void setAlertId(Integer alertId) {
this.alertId = alertId;
}
public Integer getDashboardId() {
return dashboardId;
}
public void setDashboardId(Integer dashboardId) {
this.dashboardId = dashboardId;
}
public Integer getPanelId() {
return panelId;
}
public void setPanelId(Integer panelId) {
this.panelId = panelId;
}
public Integer getUserId() {
return userId;
}
public void setUserId(Integer userId) {
this.userId = userId;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getNewState() {
return newState;
}
public void setNewState(String newState) {
this.newState = newState;
}
public String getPrevState() {
return prevState;
}
public void setPrevState(String prevState) {
this.prevState = prevState;
}
public Long getTime() {
return time;
}
public void setTime(Long time) {
this.time = time;
}
public Long getTimeEnd() {
return timeEnd;
}
public void setTimeEnd(Long timeEnd) {
this.timeEnd = timeEnd;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
public String getMetric() {
return metric;
}
public void setMetric(String metric) {
this.metric = metric;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public List<String> getTags() {
return tags;
}
public void addTag(String tag) {
this.tags.add(tag);
}
public void setTags(List<String> tags) {
tags.forEach(this::addTag);
}
public void setTags(Map<String, String> tags) {
tags.forEach((k, v) -> {
this.addTag(k + ":" + v);
});
}
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
@Override
public String toString() {
return "Annotation{" +
"id=" + id +
", alertId=" + alertId +
", dashboardId=" + dashboardId +
", panelId=" + panelId +
", userId=" + userId +
", userName='" + userName + '\'' +
", newState='" + newState + '\'' +
", prevState='" + prevState + '\'' +
", time=" + time +
", timeEnd=" + timeEnd +
", text='" + text + '\'' +
", metric='" + metric + '\'' +
", type='" + type + '\'' +
", tags=" + tags +
", data=" + data +
'}';
}
public LinkedHashMap<String, String> getTagMap() {
LinkedHashMap<String, String> map = new LinkedHashMap<>();
for (String tag : this.getTags()) {
String[] split = tag.split(":", 2);
map.put(split[0], (split.length == 2 ? split[1] : null));
}
return map;
}
}

View File

@ -0,0 +1,246 @@
/*
* Copyright (c) 2022 nosqlbench
*
* 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 io.nosqlbench.engine.clients.grafana.transfer;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class GDashboard {
Map<String, Object> annotations = new HashMap<>();
String description;
boolean editable;
long graphToolTip;
long id;
long iteration;
List<Object> links;
List<GPanelDef> panels;
String refresh;
long schemaVersion;
String style;
List<Object> tags;
GTemplating templating;
Time time;
Map<String, List<String>> timepicker;
String timezone;
String title;
String uid;
long version;
public Map<String, Object> getAnnotations() {
return annotations;
}
public void setAnnotations(Map<String, Object> annotations) {
this.annotations = annotations;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public boolean isEditable() {
return editable;
}
public void setEditable(boolean editable) {
this.editable = editable;
}
public long getGraphToolTip() {
return graphToolTip;
}
public void setGraphToolTip(long graphToolTip) {
this.graphToolTip = graphToolTip;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public long getIteration() {
return iteration;
}
public void setIteration(long iteration) {
this.iteration = iteration;
}
public List<Object> getLinks() {
return links;
}
public void setLinks(List<Object> links) {
this.links = links;
}
public List<GPanelDef> getPanels() {
return panels;
}
public void setPanels(List<GPanelDef> panels) {
this.panels = panels;
}
public String getRefresh() {
return refresh;
}
public void setRefresh(String refresh) {
this.refresh = refresh;
}
public long getSchemaVersion() {
return schemaVersion;
}
public void setSchemaVersion(long schemaVersion) {
this.schemaVersion = schemaVersion;
}
public String getStyle() {
return style;
}
public void setStyle(String style) {
this.style = style;
}
public List<Object> getTags() {
return tags;
}
public void setTags(List<Object> tags) {
this.tags = tags;
}
public GTemplating getTemplating() {
return templating;
}
public void setTemplating(GTemplating templating) {
this.templating = templating;
}
public void setTime(Time time) {
this.time = time;
}
public Map<String, List<String>> getTimepicker() {
return timepicker;
}
public void setTimepicker(Map<String, List<String>> timepicker) {
this.timepicker = timepicker;
}
public String getTimezone() {
return timezone;
}
public void setTimezone(String timezone) {
this.timezone = timezone;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getUid() {
return uid;
}
public void setUid(String uid) {
this.uid = uid;
}
public long getVersion() {
return version;
}
public void setVersion(long version) {
this.version = version;
}
public static GDashboard fromFile(String path) {
// URL resource = GDashboard.class.getClassLoader().getResource(path);
String json = null;
try {
URL url = GDashboard.class.getClassLoader().getResource(path);
if (url == null) {
throw new RuntimeException("path not found:" + path);
}
Path found = Paths.get(url.toURI());
byte[] bytes = Files.readAllBytes(found);
json = new String(bytes, StandardCharsets.UTF_8);
Gson gson = new GsonBuilder().setPrettyPrinting().create();
GDashboard db = gson.fromJson(json, GDashboard.class);
return db;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public Time getTime() {
return time;
}
public static class Time {
String from;
String to;
public String getFrom() {
return from;
}
public void setFrom(String from) {
this.from = from;
}
public String getTo() {
return to;
}
public void setTo(String to) {
this.to = to;
}
}
}

View File

@ -0,0 +1,33 @@
/*
* Copyright (c) 2022 nosqlbench
*
* 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 io.nosqlbench.engine.clients.grafana.transfer;
import java.util.List;
public class GDashboardInfo {
long id;
String uid;
String title;
String url;
String type;
List<String> tags;
boolean isStarred;
// deprecated
String uri;
}

View File

@ -0,0 +1,39 @@
/*
* Copyright (c) 2022 nosqlbench
*
* 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 io.nosqlbench.engine.clients.grafana.transfer;
public class GDashboardResponse {
GMeta meta;
GDashboard dashboard;
public GMeta getMeta() {
return meta;
}
public void setMeta(GMeta meta) {
this.meta = meta;
}
public GDashboard getDashboard() {
return dashboard;
}
public void setDashboard(GDashboard dashboard) {
this.dashboard = dashboard;
}
}

View File

@ -0,0 +1,149 @@
/*
* Copyright (c) 2022 nosqlbench
*
* 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 io.nosqlbench.engine.clients.grafana.transfer;
import java.util.Map;
public class GDataSource {
long id;
long orgId;
String name;
String type;
String typeLogoUrl;
String access;
String url;
String password;
String user;
String database;
boolean basicAuth;
boolean isDefault;
Map<String, Object> jsonData;
boolean readOnly;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public long getOrgId() {
return orgId;
}
public void setOrgId(long orgId) {
this.orgId = orgId;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getTypeLogoUrl() {
return typeLogoUrl;
}
public void setTypeLogoUrl(String typeLogoUrl) {
this.typeLogoUrl = typeLogoUrl;
}
public String getAccess() {
return access;
}
public void setAccess(String access) {
this.access = access;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getUser() {
return user;
}
public void setUser(String user) {
this.user = user;
}
public String getDatabase() {
return database;
}
public void setDatabase(String database) {
this.database = database;
}
public boolean isBasicAuth() {
return basicAuth;
}
public void setBasicAuth(boolean basicAuth) {
this.basicAuth = basicAuth;
}
public boolean isDefault() {
return isDefault;
}
public void setDefault(boolean aDefault) {
isDefault = aDefault;
}
public Map<String, Object> getJsonData() {
return jsonData;
}
public void setJsonData(Map<String, Object> jsonData) {
this.jsonData = jsonData;
}
public boolean isReadOnly() {
return readOnly;
}
public void setReadOnly(boolean readOnly) {
this.readOnly = readOnly;
}
}

View File

@ -0,0 +1,22 @@
/*
* Copyright (c) 2022 nosqlbench
*
* 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 io.nosqlbench.engine.clients.grafana.transfer;
import java.util.LinkedHashMap;
public class GMeta extends LinkedHashMap<String, Object> {
}

View File

@ -0,0 +1,205 @@
/*
* Copyright (c) 2022 nosqlbench
*
* 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 io.nosqlbench.engine.clients.grafana.transfer;
import java.util.List;
import java.util.Map;
public class GPanelDef {
boolean collapsed;
Map<String, String> gridPos;
long id;
List<GPanelDef> panels;
String description;
Map<String, Object> fieldConfig;
Map<String, String> options;
String pluginVersion;
List<GTarget> targets;
String title;
String type;
String datasource;
public boolean isCollapsed() {
return collapsed;
}
public void setCollapsed(boolean collapsed) {
this.collapsed = collapsed;
}
public Map<String, String> getGridPos() {
return gridPos;
}
public void setGridPos(Map<String, String> gridPos) {
this.gridPos = gridPos;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public List<GPanelDef> getPanels() {
return panels;
}
public void setPanels(List<GPanelDef> panels) {
this.panels = panels;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public Map<String, Object> getFieldConfig() {
return fieldConfig;
}
public void setFieldConfig(Map<String, Object> fieldConfig) {
this.fieldConfig = fieldConfig;
}
public Map<String, String> getOptions() {
return options;
}
public void setOptions(Map<String, String> options) {
this.options = options;
}
public String getPluginVersion() {
return pluginVersion;
}
public void setPluginVersion(String pluginVersion) {
this.pluginVersion = pluginVersion;
}
public List<GTarget> getTargets() {
return targets;
}
public void setTargets(List<GTarget> targets) {
this.targets = targets;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getDatasource() {
return datasource;
}
public void setDatasource(String datasource) {
this.datasource = datasource;
}
public String toString() {
return id + ":'" + title + "'";
}
public static class GTarget {
String queryType;
String refId;
String expr;
String interval;
String legendFormat;
boolean hide;
public String getQueryType() {
return queryType;
}
public void setQueryType(String queryType) {
this.queryType = queryType;
}
public String getRefId() {
return refId;
}
public void setRefId(String refId) {
this.refId = refId;
}
public String getExpr() {
return expr;
}
public void setExpr(String expr) {
this.expr = expr;
}
public String getInterval() {
return interval;
}
public void setInterval(String interval) {
this.interval = interval;
}
public String getLegendFormat() {
return legendFormat;
}
public void setLegendFormat(String legendFormat) {
this.legendFormat = legendFormat;
}
public boolean isHide() {
return hide;
}
public void setHide(boolean hide) {
this.hide = hide;
}
@Override
public String toString() {
return "GTarget{" +
"queryType='" + queryType + '\'' +
", refId='" + refId + '\'' +
", expr='" + expr + '\'' +
", interval='" + interval + '\'' +
", legendFormat='" + legendFormat + '\'' +
", hide=" + hide +
'}';
}
}
}

View File

@ -0,0 +1,38 @@
/*
* Copyright (c) 2022 nosqlbench
*
* 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 io.nosqlbench.engine.clients.grafana.transfer;
public class GSnapshot {
GMeta meta;
GDashboard dashboard;
public GMeta getMeta() {
return meta;
}
public void setMeta(GMeta meta) {
this.meta = meta;
}
public GDashboard getDashboard() {
return dashboard;
}
public void setDashboard(GDashboard dashboard) {
this.dashboard = dashboard;
}
}

View File

@ -0,0 +1,110 @@
/*
* Copyright (c) 2022 nosqlbench
*
* 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 io.nosqlbench.engine.clients.grafana.transfer;
public class GSnapshotInfo {
long id;
String name;
String key;
long orgId;
long userId;
boolean external;
String externalUrl;
String expires;
String created;
String updated;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public long getOrgId() {
return orgId;
}
public void setOrgId(long orgId) {
this.orgId = orgId;
}
public long getUserId() {
return userId;
}
public void setUserId(long userId) {
this.userId = userId;
}
public boolean isExternal() {
return external;
}
public void setExternal(boolean external) {
this.external = external;
}
public String getExternalUrl() {
return externalUrl;
}
public void setExternalUrl(String externalUrl) {
this.externalUrl = externalUrl;
}
public String getExpires() {
return expires;
}
public void setExpires(String expires) {
this.expires = expires;
}
public String getCreated() {
return created;
}
public void setCreated(String created) {
this.created = created;
}
public String getUpdated() {
return updated;
}
public void setUpdated(String updated) {
this.updated = updated;
}
}

View File

@ -0,0 +1,262 @@
/*
* Copyright (c) 2022 nosqlbench
*
* 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 io.nosqlbench.engine.clients.grafana.transfer;
import java.util.List;
public class GTemplate {
String allValue;
String name;
GCurrentValue current;
String datasource;
String definition;
Object error;
long hide;
boolean includeAll;
String label;
boolean multi;
List<Object> options;
String query;
long refresh;
String regex;
boolean skipUrlSync;
long sort;
String tagValuesQuery;
List<String> tags;
String tagsQuery;
String type;
boolean useTags;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAllValue() {
return allValue;
}
public void setAllValue(String allValue) {
this.allValue = allValue;
}
public GCurrentValue getCurrent() {
return current;
}
public void setCurrent(GCurrentValue current) {
this.current = current;
}
public String getDatasource() {
return datasource;
}
public void setDatasource(String datasource) {
this.datasource = datasource;
}
public String getDefinition() {
return definition;
}
public void setDefinition(String definition) {
this.definition = definition;
}
public Object getError() {
return error;
}
public void setError(Object error) {
this.error = error;
}
public long getHide() {
return hide;
}
public void setHide(long hide) {
this.hide = hide;
}
public boolean isIncludeAll() {
return includeAll;
}
public void setIncludeAll(boolean includeAll) {
this.includeAll = includeAll;
}
public String getLabel() {
return label;
}
public void setLabel(String label) {
this.label = label;
}
public boolean isMulti() {
return multi;
}
public void setMulti(boolean multi) {
this.multi = multi;
}
public List<Object> getOptions() {
return options;
}
public void setOptions(List<Object> options) {
this.options = options;
}
public String getQuery() {
return query;
}
public void setQuery(String query) {
this.query = query;
}
public long getRefresh() {
return refresh;
}
public void setRefresh(long refresh) {
this.refresh = refresh;
}
public String getRegex() {
return regex;
}
public void setRegex(String regex) {
this.regex = regex;
}
public boolean isSkipUrlSync() {
return skipUrlSync;
}
public void setSkipUrlSync(boolean skipUrlSync) {
this.skipUrlSync = skipUrlSync;
}
public long getSort() {
return sort;
}
public void setSort(long sort) {
this.sort = sort;
}
public String getTagValuesQuery() {
return tagValuesQuery;
}
public void setTagValuesQuery(String tagValuesQuery) {
this.tagValuesQuery = tagValuesQuery;
}
public List<String> getTags() {
return tags;
}
public void setTags(List<String> tags) {
this.tags = tags;
}
public String getTagsQuery() {
return tagsQuery;
}
public void setTagsQuery(String tagsQuery) {
this.tagsQuery = tagsQuery;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public boolean isUseTags() {
return useTags;
}
public void setUseTags(boolean useTags) {
this.useTags = useTags;
}
public static class GCurrentValue {
boolean selected;
Object text;
Object value;
public boolean isSelected() {
return selected;
}
public void setSelected(boolean selected) {
this.selected = selected;
}
public Object getText() {
return text;
}
public void setText(Object text) {
this.text = text;
}
public Object getValue() {
return value;
}
public void setValue(Object value) {
this.value = value;
}
public List<String> getValues() {
if (value instanceof String) {
return List.of((String) value);
} else if (value instanceof List) {
return (List<String>) value;
} else {
throw new RuntimeException("Unrecognized form of value:" + value.getClass().getSimpleName());
}
}
public List<String> getTexts() {
if (text instanceof String) {
return List.of((String) text);
} else if (text instanceof List) {
return (List<String>) text;
} else {
throw new RuntimeException("Unrecognized form of text:" + text.getClass().getSimpleName());
}
}
}
}

View File

@ -0,0 +1,31 @@
/*
* Copyright (c) 2022 nosqlbench
*
* 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 io.nosqlbench.engine.clients.grafana.transfer;
import java.util.List;
public class GTemplating {
List<GTemplate> list;
public List<GTemplate> getList() {
return list;
}
public void setList(List<GTemplate> list) {
this.list = list;
}
}

View File

@ -0,0 +1,48 @@
/*
* Copyright (c) 2022 nosqlbench
*
* 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 io.nosqlbench.engine.clients.prometheus;
import java.util.List;
public class PMatrixData {
String resultType;
List<PMatrixElem> result;
public String getResultType() {
return resultType;
}
public void setResultType(String resultType) {
this.resultType = resultType;
}
public List<PMatrixElem> getResult() {
return result;
}
public void setResult(List<PMatrixElem> result) {
this.result = result;
}
@Override
public String toString() {
return "PMatrixData{" +
"resultType='" + resultType + '\'' +
", result=" + result +
'}';
}
}

View File

@ -0,0 +1,30 @@
/*
* Copyright (c) 2022 nosqlbench
*
* 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 io.nosqlbench.engine.clients.prometheus;
public class PMatrixElem {
PMetric metric;
PValues values;
@Override
public String toString() {
return "PMatrixElem{" +
"metric=" + metric +
", values=" + values +
'}';
}
}

View File

@ -0,0 +1,33 @@
/*
* Copyright (c) 2022 nosqlbench
*
* 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 io.nosqlbench.engine.clients.prometheus;
import java.util.LinkedHashMap;
public class PMetric extends LinkedHashMap<String, String> {
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("{");
forEach((k, v) -> {
sb.append(k).append("=");
sb.append("\"").append(v).append("\",");
});
sb.setLength(sb.length() - 1);
sb.append("}");
return sb.toString();
}
}

View File

@ -0,0 +1,29 @@
/*
* Copyright (c) 2022 nosqlbench
*
* 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 io.nosqlbench.engine.clients.prometheus;
import java.util.ArrayList;
public class PValue extends ArrayList<Object> {
public double getInstant() {
return (double) get(0);
}
public String getValue() {
return (String) get(1);
}
}

View File

@ -0,0 +1,30 @@
/*
* Copyright (c) 2022 nosqlbench
*
* 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 io.nosqlbench.engine.clients.prometheus;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import java.util.ArrayList;
public class PValues extends ArrayList<PValue> {
private final static Gson gson = new GsonBuilder().create();
public String toString() {
return gson.toJson(this);
}
}

View File

@ -0,0 +1,46 @@
/*
* Copyright (c) 2022 nosqlbench
*
* 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 io.nosqlbench.engine.clients.prometheus;
public class PromQueryResult<T> {
String status;
T data;
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
@Override
public String toString() {
return "PromQueryResult{" +
"status='" + status + '\'' +
", data=" + data +
'}';
}
}

View File

@ -0,0 +1,104 @@
/*
* Copyright (c) 2022 nosqlbench
*
* 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 io.nosqlbench.engine.clients.prometheus;
import java.util.List;
import java.util.Map;
public class PromSeriesDataResult {
String status;
Data data;
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
public Data getData() {
return data;
}
public void setData(Data data) {
this.data = data;
}
public static class Data {
String resultType;
List<Result> result;
public String getResultType() {
return resultType;
}
public void setResultType(String resultType) {
this.resultType = resultType;
}
public List<Result> getResult() {
return result;
}
public void setResult(List<Result> result) {
this.result = result;
}
private static class Result {
Map<String, String> metric;
Value[] values;
public Map<String, String> getMetric() {
return metric;
}
public void setMetric(Map<String, String> metric) {
this.metric = metric;
}
public Value[] getValues() {
return values;
}
public void setValues(Value[] values) {
this.values = values;
}
public static class Value {
long instant;
String value;
public long getInstant() {
return instant;
}
public void setInstant(long instant) {
this.instant = instant;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
}
}
}

View File

@ -0,0 +1,56 @@
/*
* Copyright (c) 2022 nosqlbench
*
* 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 io.nosqlbench.engine.clients.prometheus;
import java.util.LinkedHashMap;
import java.util.List;
public class PromSeriesLookupResult {
String status;
List<Element> data;
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
public List<Element> getData() {
return data;
}
public void setData(List<Element> data) {
this.data = data;
}
public static class Element extends LinkedHashMap<String, String> {
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("{");
forEach((k, v) -> {
sb.append(k).append("=");
sb.append("\"").append(v).append("\",");
});
sb.setLength(sb.length() - 1);
sb.append("}");
return sb.toString();
}
}
}

View File

@ -0,0 +1,37 @@
/*
* Copyright (c) 2022 nosqlbench
*
* 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 io.nosqlbench.engine.clients.grafana;
import org.assertj.core.data.Offset;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
public class GTimeUnitTest {
@Test
public void testParseBasic() {
long result = GTimeUnit.epochSecondsFor("now");
}
@Test
public void testParseRelative() {
long result = GTimeUnit.epochSecondsFor("now-1w");
assertThat(result).isCloseTo((System.currentTimeMillis() / 1000) - (86400L * 7L), Offset.offset(60L));
}
}

View File

@ -0,0 +1,61 @@
/*
* Copyright (c) 2022 nosqlbench
*
* 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 io.nosqlbench.engine.clients.grafana;
import io.nosqlbench.engine.clients.grafana.transfer.GAnnotation;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import java.util.List;
public class GrafanaClientTest {
private final static Logger logger = LogManager.getLogger(GrafanaClientTest.class);
private static final String testurl = "http://localhost:3000/";
@Test
@Disabled
public void testCreateAnnotation() {
GrafanaClient client = new GrafanaClient(testurl);
client.getConfig().basicAuth("admin", "admin");
GAnnotation a = new GAnnotation();
a.setDashboardId(2);
a.setText("testingAnnotation");
GAnnotation created = client.createAnnotation(a);
logger.info(created);
}
@Test
@Disabled
public void testFindAnnotations() {
GrafanaClient client = new GrafanaClient(testurl);
client.getConfig().basicAuth("admin", "admin");
List<GAnnotation> annotations = client.findAnnotations(By.id(1));
logger.info(annotations);
}
@Test
@Disabled
public void testGetApiToken() {
GrafanaClient client = new GrafanaClient(testurl);
client.getConfig().basicAuth("admin", "admin");
ApiToken token = client.createApiToken("nosqlbench", "Admin", Long.MAX_VALUE);
logger.info(token);
}
}

View File

@ -0,0 +1,37 @@
/*
* Copyright (c) 2022 nosqlbench
*
* 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 io.nosqlbench.engine.clients.grafana.analyzer;
import io.nosqlbench.engine.clients.grafana.transfer.GDashboard;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
public class GrafanaRegionAnalyzerTest {
@Test
@Disabled
public void testGetQueries() {
GrafanaRegionAnalyzer gra = new GrafanaRegionAnalyzer();
gra.setBaseUrl("http://44.242.139.57:3000/");
GDashboard db = gra.getDashboard("aIIX1f6Wz");
//GDashboard db = GDashboard.fromFile("examples/db.json");
// gra.getQueries(db);
}
}

View File

@ -0,0 +1,43 @@
/*
* Copyright (c) 2022 nosqlbench
*
* 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 io.nosqlbench.engine.clients.prometheus;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;
import io.nosqlbench.api.content.NBIO;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import java.lang.reflect.Type;
import static org.assertj.core.api.Assertions.assertThat;
public class PMatrixElemTest {
@Test
@Disabled
public void testMatrixElem() {
Gson gson = new GsonBuilder().create();
String json = NBIO.classpath().name("test.json").one().asString();
Type type = new TypeToken<PromQueryResult<PMatrixData>>() {
}.getType();
Object result = gson.fromJson(json, type);
assertThat(result).isOfAnyClassIn(PromQueryResult.class);
}
}

View File

@ -0,0 +1,50 @@
{
"status": "success",
"data": {
"resultType": "matrix",
"result": [
{
"metric": {
"__name__": "up",
"job": "prometheus",
"instance": "localhost:9090"
},
"values": [
[
1435781430.781,
"1"
],
[
1435781445.781,
"1"
],
[
1435781460.781,
"1"
]
]
},
{
"metric": {
"__name__": "up",
"job": "node",
"instance": "localhost:9091"
},
"values": [
[
1435781430.781,
"0"
],
[
1435781445.781,
"0"
],
[
1435781460.781,
"1"
]
]
}
]
}
}

File diff suppressed because it is too large Load Diff

View File

@ -67,6 +67,12 @@
<version>5.17.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>io.nosqlbench</groupId>
<artifactId>engine-clients</artifactId>
<version>5.17.1-SNAPSHOT</version>
</dependency>
</dependencies>
<build>

16
pom.xml
View File

@ -36,19 +36,22 @@
<properties>
<!-- CORE MODULES -->
<module.mvn-defaults>mvn-defaults</module.mvn-defaults>
<module.nb5>nb5</module.nb5>
<module.nbr>nbr</module.nbr>
<module.nbr-examples>nbr-examples</module.nbr-examples>
<module.nb-api>nb-api</module.nb-api>
<module.nb-annotations>nb-annotations</module.nb-annotations>
<module.nb-spectest>nb-spectest</module.nb-spectest>
<module.adapters-api>adapters-api</module.adapters-api>
<module.engine-api>engine-api</module.engine-api>
<module.engine-core>engine-core</module.engine-core>
<module.engine-extensions>engine-extensions</module.engine-extensions>
<module.engine-docker>engine-docker</module.engine-docker>
<module.engine-docs>engine-docs</module.engine-docs>
<module.engine-cli>engine-cli</module.engine-cli>
<module.adapters-api>adapters-api</module.adapters-api>
<module.engine-docs>engine-docs</module.engine-docs>
<module.nb5>nb5</module.nb5>
<module.nbr>nbr</module.nbr>
<module.nb-spectest>nb-spectest</module.nb-spectest>
<module.nbr-examples>nbr-examples</module.nbr-examples>
<!-- driver modules -->
<module.adapter-diag>adapter-diag</module.adapter-diag>
@ -88,6 +91,7 @@
<module>engine-extensions</module>
<module>engine-docker</module>
<module>engine-docs</module>
<module>engine-clients</module>
<module>engine-cli</module>
<module>adapters-api</module>