All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.loadcoder.cluster.clients.influxdb.InfluxDBClient Maven / Gradle / Ivy

There is a newer version: 3.1.1
Show newest version
/*******************************************************************************
 * Copyright (C) 2020 Team Loadcoder
 * 
 * This file is part of Loadcoder.
 * 
 * Loadcoder is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * Loadcoder is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see .
 ******************************************************************************/
package com.loadcoder.cluster.clients.influxdb;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.jayway.jsonpath.JsonPath;
import com.jayway.jsonpath.PathNotFoundException;
import com.loadcoder.cluster.clients.HttpClient;
import com.loadcoder.cluster.clients.HttpResponse;
import com.loadcoder.cluster.clients.docker.LoadcoderCluster;
import com.loadcoder.load.scenario.RuntimeResultConsumer;
import com.loadcoder.network.CodeGeneratable;
import com.loadcoder.result.TransactionExecutionResult;
import com.loadcoder.statics.Configuration;
import com.loadcoder.utils.DateTimeUtil;
import com.loadcoder.utils.FileUtil;

import net.minidev.json.JSONArray;

public class InfluxDBClient extends HttpClient {

	static Logger log = LoggerFactory.getLogger(InfluxDBClient.class);
	private final String dbName;

	private final String testGroup;
	private final String testName;

	private final static String WRITE_URL_TEMPLATE = "%s://%s:%s/write?db=";
	private final static String QUERY_URL_TEMPLATE = "%s://%s:%s/query";
	private final static String DB_NAME_TEMPLATE = "%s__%s";

	public static CodeGeneratable influxReporter(String groupName, String testName) {
		return codeTemplate -> InfluxDBClient.generateCodeCallStoreAndConsumeResultRuntime(codeTemplate, groupName,
				testName);
	}

	private final static String WRITE_ENTRY_BODY_TEMPLATE = "%s,transaction=%s,status=%s value=%s %s00";

	private final String WRITE_URL;
	private final String QUERY_URL;

	/**
	 * Constructor for the InfluxDBClient
	 * 
	 * @param host      is the host for where the influx DB is hosted
	 * @param port      is the port where the influx DB exposes its API
	 * @param https     is boolean for whether or not the communication is encrypted
	 *                  or not.
	 * @param testGroup is the group name of the test
	 * @param testName  is the name of the test
	 */
	public InfluxDBClient(String host, int port, boolean https, String testGroup, String testName) {
		String protocol = protocolAsString(https);
		WRITE_URL = String.format(WRITE_URL_TEMPLATE, protocol, host, port);
		QUERY_URL = String.format(QUERY_URL_TEMPLATE, protocol, host, port);

		this.dbName = String.format(DB_NAME_TEMPLATE, testGroup, testName);
		this.testGroup = testGroup;
		this.testName = testName;
	}

	public static RuntimeResultConsumer setupInfluxDataConsumer(LoadcoderCluster dockerClusterClient, String testGroup,
			String testName) {
		InfluxDBClient client = dockerClusterClient.getInfluxDBClient(testGroup, testName);

		return client.setupInfluxDataConsumer(testGroup, testName, dockerClusterClient.getConfiguration());
	}

	public RuntimeResultConsumer setupInfluxDataConsumer(String testGroup, String testName, Configuration config) {

		List databases = listDatabases();
		if (!databases.contains(dbName)) {
			createDB();
		}

		String executionId = config.getConfiguration("LOADCODER_EXECUTION_ID");
		if (executionId == null) {
			executionId = DateTimeUtil.getDateTimeNowString();
		}
		InfluxDBTestExecution exe = createTestExecution(executionId);
		log.info("Using executionId:" + executionId);

		RuntimeResultConsumer influxConsumer = (res) -> exe.writeTransactions(res);
		return influxConsumer;
	}

	public String getTestGroup() {
		return testGroup;
	}

	public String getTestName() {
		return testName;
	}

	public String getDatabaseName() {
		return this.dbName;
	}

	public HttpResponse createDB() {
		log.info("Creating new InfluxDB database:" + dbName);
		String body = String.format("q=CREATE DATABASE %s", dbName);
		return sendPost(body, QUERY_URL, Arrays.asList());
	}

	public List showMeasurements() {
		List result = new ArrayList();
		String body = String.format("q=SHOW MEASUREMENTS ON %s", dbName);
		HttpResponse resp = sendPost(body, QUERY_URL, Arrays.asList());
		String respBody = resp.getBody();
		try {
			JSONArray array = JsonPath.read(respBody, "$['results'][0]['series'][0]['values'][*][*]");
			array.stream().forEach(db -> {
				result.add(db.toString());
			});

			return result;
		} catch (PathNotFoundException pnfe) {
			throw new RuntimeException("There are no measurements in the InfluxDB database " + dbName
					+ ". Make sure that there are results written to the database before trying to create a Grafana dashboard.");
		}

	}

	public List listDatabases() {
		List result = new ArrayList();
		String body = String.format("q=SHOW DATABASES");
		HttpResponse resp = sendPost(body, QUERY_URL, Arrays.asList());
		String respBody = resp.getBody();

		JSONArray array = JsonPath.read(respBody, "$['results'][0]['series'][0]['values'][*][0]");
		array.stream().forEach(db -> {
			result.add(db.toString());
		});
		return result;
	}

	public List listDistinctTransactions(String measurement) {
		List result = new ArrayList();
		String body = String
				.format("q=	show tag values on " + dbName + " from \"" + measurement + "\" with key = transaction");
		HttpResponse resp = sendPost(body, QUERY_URL, Arrays.asList());
		String respBody = resp.getBody();

		JSONArray array = JsonPath.read(respBody, "$['results'][0]['series'][0]['values'][*][1]");
		array.stream().forEach(db -> {
			result.add(db.toString());
		});
		return result;
	}

	private HttpResponse writeEntries(String body) {
		return sendPost(body, WRITE_URL + dbName, Arrays.asList());
	}

	public InfluxDBTestExecution createTestExecution(String executionId) {
		return new InfluxDBTestExecution(executionId, this);
	}

	/**
	 * Class that keeps the influxDB connection together with the current execution
	 */
	public static class InfluxDBTestExecution {
		final String executionId;
		final InfluxDBClient client;

		private final Map uniqueNanos = new HashMap();

		private InfluxDBTestExecution(String executionId, InfluxDBClient client) {
			this.client = client;
			this.executionId = executionId;
		}

		private InfluxDBTestExecution(InfluxDBClient client) {
			this.client = client;
			this.executionId = null;
		}

		/**
		 * Write a list of transaction results into the influx DB
		 * 
		 * @param transactionResults that is going be be written into the influx DB
		 * @return the HTTP status code for the influx DB response
		 */
		public HttpResponse writeTransactions(Map> transactionResults) {
			String body = convertTransactionsToWriteBody(transactionResults, executionId);
			HttpResponse responseCode = client.writeEntries(body);
			return responseCode;
		}

		protected String convertTransactionsToWriteBody(
				Map> transactionResults, String executionId) {
			if (executionId == null || executionId.isEmpty()) {
				throw new RuntimeException("ExecutionId is null or empty");
			}
			StringBuilder builder = new StringBuilder();
			for (String key : transactionResults.keySet()) {
				StringBuilder builder2 = new StringBuilder();

				Integer nanoIterator = uniqueNanos.get(key);
				if (nanoIterator == null) {
					nanoIterator = 1000;
					uniqueNanos.put(key, nanoIterator);
				}

				List list = transactionResults.get(key);
				for (TransactionExecutionResult t : list) {

					if (nanoIterator > 9998) {
						nanoIterator = 1000;
					}
					String urlParameters = String.format(WRITE_ENTRY_BODY_TEMPLATE + (nanoIterator++), executionId, key,
							t.isStatus(), t.getValue(), t.getTs());

					builder2.append(urlParameters);
					builder2.append("\n");
				}
				uniqueNanos.put(key, nanoIterator);

				String transactionsString = builder2.toString();
				builder.append(transactionsString);
			}
			return builder.toString();
		}
	}

	/**
	 * Generates the code for the storeAndConsumeResultRuntime call where the
	 * results are sent to a InfluxDB
	 * 
	 * @param originalCode the code that will be modified into the new code
	 * @param groupName    name of the group of the test
	 * @param testName     the name of the test
	 * @return returns the new generated code
	 */
	public static String generateCodeCallStoreAndConsumeResultRuntime(String originalCode, String groupName,
			String testName) {
		String result = originalCode;
		String testContent = FileUtil.getResourceAsString("/cluster-codeTemplate/storeAndConsumeResultRuntime.tmp");
		testContent = testContent.replace("${groupName}", groupName);
		testContent = testContent.replace("${testName}", testName);

		result = result.replace("${storeAndConsumeResultRuntime}", testContent);

		result = result.replace("${importList}",
				"import com.loadcoder.cluster.clients.docker.LoadcoderCluster;\n${importList}");
		result = result.replace("${importList}",
				"import com.loadcoder.cluster.clients.influxdb.InfluxDBClient;\n${importList}");

		return result;
	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy