diff --git a/README.md b/README.md index 999b28c514df4ca1f9b107a33c388d9033a7d1aa..4baf807a4a5015d53fb381a2df4be89a89c61002 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,36 @@ |Authors|Organisation| |-|-| |[Michael Boniface](mailto:mjb@it-innovation.soton.ac.uk)|[University of Southampton, IT Innovation Centre](http://www.it-innovation.soton.ac.uk)| +|[Simon Crowle](mailto:sgc@it-innovation.soton.ac.uk)|[University of Southampton, IT Innovation Centre](http://www.it-innovation.soton.ac.uk)| +### Contents -#### Contents \ No newline at end of file +#### CLMC Monitoring Specification Test Framework +A Java/JUnit test framework has been developed to provide concrete examples of the CLMC monitoring specification. To build and run this test framework you will need: + +1. The CLMC TICK stack installed and running (provided as a Vagrant solution in this project) + +2. Java JDK 1.8+ installed +3. Maven 3+ installed + - Optionally a Java IDE installed, such as NetBeans + + +##### Building the test framework + +1. Clone this project (obviously) + +2. Open the Maven project (\<flame-clmc root\>\src\clmc-spec) in your Java IDE or navigate to POM.xml file in command line + +3. Check the monSpecTestConfig.properties file (src/main/resources) matches your TICK stack set-up. It's likely to. + +4. Build the project (this should automatically build and run the tests) + > From the command line: mvn test + + +##### Extending the test framework +This test framework is easily extendible. There are two simple tests already ready for you to explore: + +* BasicInputTest.java - This tries sending some basic metrics about a host to InfluxDB +* BasicQueryTest.java - This tries querying InfluxDB about host metrics + +Each test case uses resources in the project to send test data or execute queries. In the first case the resource '/src/main/resources/inputs/host_resource_input' is a file with example InfluxDB Line Protocol statements. In the second test case, the file '/src/main/resources/host_resource_query' contains queries that are executed against InfluxDB. The results of these are currently just output to the console. \ No newline at end of file diff --git a/src/clmc-spec/.classpath b/src/clmc-spec/.classpath new file mode 100644 index 0000000000000000000000000000000000000000..6d7587a819e638a7f25352b31dcd0b4e876e42da --- /dev/null +++ b/src/clmc-spec/.classpath @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="UTF-8"?> +<classpath> + <classpathentry kind="src" output="target/classes" path="src/main/java"> + <attributes> + <attribute name="optional" value="true"/> + <attribute name="maven.pomderived" value="true"/> + </attributes> + </classpathentry> + <classpathentry excluding="**" kind="src" output="target/classes" path="src/main/resources"> + <attributes> + <attribute name="maven.pomderived" value="true"/> + </attributes> + </classpathentry> + <classpathentry kind="src" output="target/test-classes" path="src/test/java"> + <attributes> + <attribute name="optional" value="true"/> + <attribute name="maven.pomderived" value="true"/> + </attributes> + </classpathentry> + <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"> + <attributes> + <attribute name="maven.pomderived" value="true"/> + </attributes> + </classpathentry> + <classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER"> + <attributes> + <attribute name="maven.pomderived" value="true"/> + </attributes> + </classpathentry> + <classpathentry kind="output" path="target/classes"/> +</classpath> diff --git a/src/clmc-spec/.project b/src/clmc-spec/.project new file mode 100644 index 0000000000000000000000000000000000000000..061590a24bdd8b6ea607dbd0d3090242f5a9ac00 --- /dev/null +++ b/src/clmc-spec/.project @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<projectDescription> + <name>clmc-spec</name> + <comment></comment> + <projects> + </projects> + <buildSpec> + <buildCommand> + <name>org.eclipse.jdt.core.javabuilder</name> + <arguments> + </arguments> + </buildCommand> + <buildCommand> + <name>org.eclipse.m2e.core.maven2Builder</name> + <arguments> + </arguments> + </buildCommand> + </buildSpec> + <natures> + <nature>org.eclipse.jdt.core.javanature</nature> + <nature>org.eclipse.m2e.core.maven2Nature</nature> + </natures> +</projectDescription> diff --git a/src/clmc-spec/.settings/org.eclipse.core.resources.prefs b/src/clmc-spec/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 0000000000000000000000000000000000000000..839d647eef851c560a9854ff81d9caa1df594ced --- /dev/null +++ b/src/clmc-spec/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,5 @@ +eclipse.preferences.version=1 +encoding//src/main/java=UTF-8 +encoding//src/main/resources=UTF-8 +encoding//src/test/java=UTF-8 +encoding/<project>=UTF-8 diff --git a/src/clmc-spec/.settings/org.eclipse.jdt.core.prefs b/src/clmc-spec/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 0000000000000000000000000000000000000000..714351aec195a9a572640e6844dcafd51565a2a5 --- /dev/null +++ b/src/clmc-spec/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,5 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 +org.eclipse.jdt.core.compiler.compliance=1.8 +org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning +org.eclipse.jdt.core.compiler.source=1.8 diff --git a/src/clmc-spec/.settings/org.eclipse.m2e.core.prefs b/src/clmc-spec/.settings/org.eclipse.m2e.core.prefs new file mode 100644 index 0000000000000000000000000000000000000000..f897a7f1cb2389f85fe6381425d29f0a9866fb65 --- /dev/null +++ b/src/clmc-spec/.settings/org.eclipse.m2e.core.prefs @@ -0,0 +1,4 @@ +activeProfiles= +eclipse.preferences.version=1 +resolveWorkspaceProjects=true +version=1 diff --git a/src/clmc-spec/.vscode/settings.json b/src/clmc-spec/.vscode/settings.json new file mode 100644 index 0000000000000000000000000000000000000000..e0f15db2eb22b5d618150277e48b741f8fdd277a --- /dev/null +++ b/src/clmc-spec/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "java.configuration.updateBuildConfiguration": "automatic" +} \ No newline at end of file diff --git a/src/clmc-spec/pom.xml b/src/clmc-spec/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..fa2828ef27c6176ba2ff74793d3acb9e65ef8965 --- /dev/null +++ b/src/clmc-spec/pom.xml @@ -0,0 +1,51 @@ +<?xml version="1.0" encoding="UTF-8"?> +<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> + <groupId>uk.ac.soton.itinnovation.flame</groupId> + <artifactId>clmc-spec</artifactId> + <version>0.1-SNAPSHOT</version> + <packaging>jar</packaging> + + <properties> + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> + <maven.compiler.source>1.8</maven.compiler.source> + <maven.compiler.target>1.8</maven.compiler.target> + </properties> + + <dependencies> + + <dependency> + <groupId>org.influxdb</groupId> + <artifactId>influxdb-java</artifactId> + <version>2.8</version> + </dependency> + + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <version>4.12</version> + <scope>test</scope> + </dependency> + + <dependency> + <groupId>org.hamcrest</groupId> + <artifactId>hamcrest-core</artifactId> + <version>1.3</version> + <scope>test</scope> + </dependency> + + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-api</artifactId> + <version>1.7.21</version> + </dependency> + + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-simple</artifactId> + <version>1.7.21</version> + </dependency> + + </dependencies> + +</project> \ No newline at end of file diff --git a/src/clmc-spec/src/main/resources/inputs/host_resource_input b/src/clmc-spec/src/main/resources/inputs/host_resource_input new file mode 100644 index 0000000000000000000000000000000000000000..39441c9cc0cd3f182b070ea5cd5710bcee4993e8 --- /dev/null +++ b/src/clmc-spec/src/main/resources/inputs/host_resource_input @@ -0,0 +1,9 @@ +# Host Resource metrics +# +# host_resource node_id,sf_inst_id,sf_id,sfc_inst_id,sfc_id,server_id,location cpus,memory,storage <timestamp> +# +# NOTE: commas and spaces are significant in the line protocol + +host_resource,node_id=1,sf_inst_id=1,sf_id=1,sfc_inst_id=1,sfc_id=1,server_id=1,location="Bristol_DC" cpus=16,memory=256,storage=1024 1513778835385000000 +host_resource,node_id=1,sf_inst_id=2,sf_id=1,sfc_inst_id=1,sfc_id=1,server_id=1,location="Bristol_DC" cpus=16,memory=256,storage=1024 1513778836385000000 +host_resource,node_id=1,sf_inst_id=3,sf_id=1,sfc_inst_id=1,sfc_id=1,server_id=2,location="Bristol_DC" cpus=8,memory=128,storage=1024 1513778837385000000 \ No newline at end of file diff --git a/src/clmc-spec/src/main/resources/monSpecTestConfig.properties b/src/clmc-spec/src/main/resources/monSpecTestConfig.properties new file mode 100644 index 0000000000000000000000000000000000000000..54042b375a276101bd225752f105d5dfcd54ac88 --- /dev/null +++ b/src/clmc-spec/src/main/resources/monSpecTestConfig.properties @@ -0,0 +1,8 @@ +# Test settings +clearDBOnExit=false + +# InfluxDB +influxDB_EP=http://localhost:8086 +influxDB_UN=root +influxDB_PW=root +influxDB_DB=clmcTestDB diff --git a/src/clmc-spec/src/main/resources/queries/host_resource_query b/src/clmc-spec/src/main/resources/queries/host_resource_query new file mode 100644 index 0000000000000000000000000000000000000000..45c8c16ee14a6b8ac1a20ea4a5e1a4d032da6585 --- /dev/null +++ b/src/clmc-spec/src/main/resources/queries/host_resource_query @@ -0,0 +1,4 @@ +# Host Resource Basic queries + +SELECT "node_id", "cpus", "memory", "storage" FROM "clmcTestDB"."autogen"."host_resource" + diff --git a/src/clmc-spec/src/test/java/uk/ac/soton/innovation/flame/clmc/monspec/test/AlphaTestSuite.java b/src/clmc-spec/src/test/java/uk/ac/soton/innovation/flame/clmc/monspec/test/AlphaTestSuite.java new file mode 100644 index 0000000000000000000000000000000000000000..26888bc7314bbbef5c3909284f5664b551a62806 --- /dev/null +++ b/src/clmc-spec/src/test/java/uk/ac/soton/innovation/flame/clmc/monspec/test/AlphaTestSuite.java @@ -0,0 +1,72 @@ +///////////////////////////////////////////////////////////////////////// +// +// © University of Southampton IT Innovation Centre, 2017 +// +// Copyright in this software belongs to University of Southampton +// IT Innovation Centre of Gamma House, Enterprise Road, +// Chilworth Science Park, Southampton, SO16 7NS, UK. +// +// This software may not be used, sold, licensed, transferred, copied +// or reproduced in whole or in part in any manner or form or in or +// on any media by any person other than in accordance with the terms +// of the Licence Agreement supplied with the software, or otherwise +// without the prior written consent of the copyright owners. +// +// This software is distributed WITHOUT ANY WARRANTY, without even the +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +// PURPOSE, except where stated in the Licence Agreement supplied with +// the software. +// +// Created By : Simon Crowle +// Created Date : 19/12/2017 +// Created for Project : FLAME +// +///////////////////////////////////////////////////////////////////////// +package uk.ac.soton.innovation.flame.clmc.monspec.test; + +import uk.ac.soton.innovation.flame.clmc.monspec.test.base.TestSuiteBase; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.runner.RunWith; +import org.junit.runners.Suite; + + + + +@RunWith(Suite.class) +@Suite.SuiteClasses({ + uk.ac.soton.innovation.flame.clmc.monspec.test.BasicInputTest.class, + uk.ac.soton.innovation.flame.clmc.monspec.test.BasicQueryTest.class +}) +public class AlphaTestSuite extends TestSuiteBase +{ + + @BeforeClass + public static void setUpClass() throws Exception + { + TestSuiteBase.setUpClass(); + } + + @AfterClass + public static void tearDownClass() throws Exception + { + TestSuiteBase.tearDownClass(); + } + + @Before + @Override + public void setUp() throws Exception + { + super.setUp(); + } + + @After + @Override + public void tearDown() throws Exception + { + super.setUp(); + } + +} diff --git a/src/clmc-spec/src/test/java/uk/ac/soton/innovation/flame/clmc/monspec/test/BasicInputTest.java b/src/clmc-spec/src/test/java/uk/ac/soton/innovation/flame/clmc/monspec/test/BasicInputTest.java new file mode 100644 index 0000000000000000000000000000000000000000..662b0be38f67a4cfa4d52b798e9e441836c59dbd --- /dev/null +++ b/src/clmc-spec/src/test/java/uk/ac/soton/innovation/flame/clmc/monspec/test/BasicInputTest.java @@ -0,0 +1,77 @@ +///////////////////////////////////////////////////////////////////////// +// +// © University of Southampton IT Innovation Centre, 2017 +// +// Copyright in this software belongs to University of Southampton +// IT Innovation Centre of Gamma House, Enterprise Road, +// Chilworth Science Park, Southampton, SO16 7NS, UK. +// +// This software may not be used, sold, licensed, transferred, copied +// or reproduced in whole or in part in any manner or form or in or +// on any media by any person other than in accordance with the terms +// of the Licence Agreement supplied with the software, or otherwise +// without the prior written consent of the copyright owners. +// +// This software is distributed WITHOUT ANY WARRANTY, without even the +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +// PURPOSE, except where stated in the Licence Agreement supplied with +// the software. +// +// Created By : Simon Crowle +// Created Date : 19/12/2017 +// Created for Project : FLAME +// +///////////////////////////////////////////////////////////////////////// +package uk.ac.soton.innovation.flame.clmc.monspec.test; + +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import uk.ac.soton.innovation.flame.clmc.monspec.test.base.BaseTest; + + + +/** + * BasicInputTest class tests inputting data series into InfluxDB + * + * IMPORTANT NOTE: for test automation under Maven, ensure your classes are labelled either + * Test* or *Test. Not doing this means Maven will miss this test class. + */ +public class BasicInputTest extends BaseTest +{ + + public BasicInputTest() + {} + + @BeforeClass + public static void setUpClass() + {} + + @AfterClass + public static void tearDownClass() + {} + + @Before + @Override + public void setUp() + { + super.setUp(); + } + + @After + @Override + public void tearDown() + { + super.tearDown(); + } + + @Test + public void testInputSeries() + { + writeProtocolLines( "host_resource_input" ); + + logger.info( "Completed Test Input Series" ); + } +} diff --git a/src/clmc-spec/src/test/java/uk/ac/soton/innovation/flame/clmc/monspec/test/BasicQueryTest.java b/src/clmc-spec/src/test/java/uk/ac/soton/innovation/flame/clmc/monspec/test/BasicQueryTest.java new file mode 100644 index 0000000000000000000000000000000000000000..862dffa80bd087187dba6d86f012de3ab6210bc9 --- /dev/null +++ b/src/clmc-spec/src/test/java/uk/ac/soton/innovation/flame/clmc/monspec/test/BasicQueryTest.java @@ -0,0 +1,75 @@ +///////////////////////////////////////////////////////////////////////// +// +// © University of Southampton IT Innovation Centre, 2017 +// +// Copyright in this software belongs to University of Southampton +// IT Innovation Centre of Gamma House, Enterprise Road, +// Chilworth Science Park, Southampton, SO16 7NS, UK. +// +// This software may not be used, sold, licensed, transferred, copied +// or reproduced in whole or in part in any manner or form or in or +// on any media by any person other than in accordance with the terms +// of the Licence Agreement supplied with the software, or otherwise +// without the prior written consent of the copyright owners. +// +// This software is distributed WITHOUT ANY WARRANTY, without even the +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +// PURPOSE, except where stated in the Licence Agreement supplied with +// the software. +// +// Created By : Simon Crowle +// Created Date : 20-Dec-2017 +// Created for Project : FLAME +// +///////////////////////////////////////////////////////////////////////// + +package uk.ac.soton.innovation.flame.clmc.monspec.test; + +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import uk.ac.soton.innovation.flame.clmc.monspec.test.base.BaseTest; + + + +/** + * BasicQueryTest class tests querying InfluxDB using data from BasicInputTest + * + * IMPORTANT NOTE: for test automation under Maven, ensure your classes are labelled either + * Test* or *Test. Not doing this means Maven will miss this test class. + */ +public class BasicQueryTest extends BaseTest +{ + public BasicQueryTest() + {} + + @BeforeClass + public static void setUpClass() + {} + + @AfterClass + public static void tearDownClass() + {} + + @Before + @Override + public void setUp() + { + super.setUp(); + } + + @After + @Override + public void tearDown() + { + super.tearDown(); + } + + @Test + public void querySoloSeries() + { + executeQueries( "host_resource_query", true ); + } +} diff --git a/src/clmc-spec/src/test/java/uk/ac/soton/innovation/flame/clmc/monspec/test/base/BaseTest.java b/src/clmc-spec/src/test/java/uk/ac/soton/innovation/flame/clmc/monspec/test/base/BaseTest.java new file mode 100644 index 0000000000000000000000000000000000000000..4b3c724ece094c47ff3432a774dd2a8a88169de4 --- /dev/null +++ b/src/clmc-spec/src/test/java/uk/ac/soton/innovation/flame/clmc/monspec/test/base/BaseTest.java @@ -0,0 +1,137 @@ +///////////////////////////////////////////////////////////////////////// +// +// © University of Southampton IT Innovation Centre, 2017 +// +// Copyright in this software belongs to University of Southampton +// IT Innovation Centre of Gamma House, Enterprise Road, +// Chilworth Science Park, Southampton, SO16 7NS, UK. +// +// This software may not be used, sold, licensed, transferred, copied +// or reproduced in whole or in part in any manner or form or in or +// on any media by any person other than in accordance with the terms +// of the Licence Agreement supplied with the software, or otherwise +// without the prior written consent of the copyright owners. +// +// This software is distributed WITHOUT ANY WARRANTY, without even the +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +// PURPOSE, except where stated in the Licence Agreement supplied with +// the software. +// +// Created By : Simon Crowle +// Created Date : 19/12/2017 +// Created for Project : FLAME +// +///////////////////////////////////////////////////////////////////////// +package uk.ac.soton.innovation.flame.clmc.monspec.test.base; + +import java.util.ArrayList; +import java.util.List; +import org.influxdb.InfluxDB; +import org.junit.After; +import org.junit.AfterClass; +import static org.junit.Assert.assertTrue; +import org.junit.Before; +import org.junit.BeforeClass; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * BaseTest is an abstract class used to set up InfluxDB resource before + * executing tests proper. Derive from this class and use protected methods. + */ +public abstract class BaseTest +{ + protected Logger logger = LoggerFactory.getLogger( BaseTest.class ); + protected InfluxDB influxDB; + + public BaseTest() + {} + + @BeforeClass + public static void setUpClass() + {} + + @AfterClass + public static void tearDownClass() + {} + + @Before + public void setUp() + { + try + { + // Create test database if it does not already exist + influxDB = InfluxUtility.getInfluxDBConnection(); + String dbName = InfluxUtility.getTestConfig().getProperty( "influxDB_DB" ); + + if ( !influxDB.databaseExists(dbName) ) + influxDB.createDatabase( dbName ); + + influxDB.setDatabase( dbName ); + } + catch ( Exception ex ) + { + logger.error( "Failed to get InfluxDB connection: " + ex.getMessage() ); + assertTrue( false ); + } + } + + @After + public void tearDown() + {} + + // Protected methods ------------------------------------------------------- + /** + * Attempts to write InfluxDB Line Protocol lines to InfluxDB + * + * @param seriesLabel - label of input resource (see /src/main/resources/inputs + */ + protected void writeProtocolLines( String seriesLabel ) + { + try + { + // Get line protocols (will throw if non-existent or empty) + List<String> protocols = InfluxUtility.getInputSeries( seriesLabel ); + + for ( String lp : protocols ) + influxDB.write( lp ); + } + catch ( Exception ex ) + { + String err = "Failed writing protocol lines: " + ex.getMessage(); + logger.error( err ); + + assertTrue( false ); + } + } + + /** + * Attempts to execute one or more queries with InfluxDB + * + * @param queryLabel - name of query input file resource (see /src/main/resources/queries) + * @param printOut - boolean to indicate whether results are logged out to console + * @return - Array of query results (per series set) + */ + protected List<List<String>> executeQueries( String queryLabel, boolean printOut ) + { + List<List<String>> uberResult = new ArrayList<>(); + + try + { + // Get test query set (will throw if non-existent or empty) + List<String> queries = InfluxUtility.getQueries( queryLabel ); + + for ( String q : queries ) + uberResult.add(InfluxUtility.executeQuery(q,printOut) ); + } + catch ( Exception ex ) + { + String err = "Failed to execute queries: " + queryLabel; + logger.error( err ); + + assertTrue( false ); + } + + return uberResult; + } +} diff --git a/src/clmc-spec/src/test/java/uk/ac/soton/innovation/flame/clmc/monspec/test/base/InfluxUtility.java b/src/clmc-spec/src/test/java/uk/ac/soton/innovation/flame/clmc/monspec/test/base/InfluxUtility.java new file mode 100644 index 0000000000000000000000000000000000000000..0204b09cffec76f1596efee12b65aabb641b9ca7 --- /dev/null +++ b/src/clmc-spec/src/test/java/uk/ac/soton/innovation/flame/clmc/monspec/test/base/InfluxUtility.java @@ -0,0 +1,309 @@ +///////////////////////////////////////////////////////////////////////// +// +// © University of Southampton IT Innovation Centre, 2017 +// +// Copyright in this software belongs to University of Southampton +// IT Innovation Centre of Gamma House, Enterprise Road, +// Chilworth Science Park, Southampton, SO16 7NS, UK. +// +// This software may not be used, sold, licensed, transferred, copied +// or reproduced in whole or in part in any manner or form or in or +// on any media by any person other than in accordance with the terms +// of the Licence Agreement supplied with the software, or otherwise +// without the prior written consent of the copyright owners. +// +// This software is distributed WITHOUT ANY WARRANTY, without even the +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +// PURPOSE, except where stated in the Licence Agreement supplied with +// the software. +// +// Created By : Simon Crowle +// Created Date : 19-Dec-2017 +// Created for Project : FLAME +// +///////////////////////////////////////////////////////////////////////// + +package uk.ac.soton.innovation.flame.clmc.monspec.test.base; + +import java.io.BufferedReader; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.List; +import java.util.Properties; +import java.util.concurrent.TimeUnit; +import org.influxdb.InfluxDB; +import org.influxdb.InfluxDBFactory; +import org.influxdb.dto.Query; +import org.influxdb.dto.QueryResult; +import org.influxdb.dto.QueryResult.Result; +import org.influxdb.dto.QueryResult.Series; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + + + +/** + * Helper class for accessing InfluxDB and processing results + */ +public class InfluxUtility +{ + private static Logger logger = LoggerFactory.getLogger(InfluxUtility.class ); + private static Properties testConfig; + private static InfluxDB influxDB; + + + public static Properties getTestConfig() + { + if ( testConfig == null ) + { + InputStream is = null; + try + { + testConfig = new Properties(); + + is = ClassLoader.class.getResourceAsStream( "/monSpecTestConfig.properties" ); + + if ( is != null ) + { + testConfig.load( is ); + logger.info( "Found test configuration OK" ); + } + else + throw new Exception( "Failed to find test configuration" ); + } + catch ( Exception ex ) + { + String err = "Could not find test configuration: " + ex.getMessage(); + logger.error( err ); + } + finally + { + try + { + if ( is != null ) + is.close(); + } + catch ( Exception ex ) + { logger.warn( "Had problems closing resource input stream" ); } + } + } + + return testConfig; + } + + public static InfluxDB getInfluxDBConnection() throws Exception + { + InfluxDB result = null; + + try + { + if ( influxDB == null ) + { + Properties config = getTestConfig(); + logger.info( "Trying to connect to Influx: " + config.getProperty("influxDB_EP") ); + + influxDB = InfluxDBFactory.connect( config.getProperty("influxDB_EP"), + config.getProperty("influxDB_UN"), + config.getProperty("influxDB_PW") ); + + result = influxDB; + logger.info( "Influx connection OK" ); + } + else + result = influxDB; + } + catch ( Exception ex ) + { + String err = "Failed to load InfluxDB configuration: " + ex.getMessage(); + logger.error( err, ex ); + + throw new Exception( err ); + } + + return result; + } + + public static List<String> getInputSeries( String label ) throws Exception + { + // Safety first + if ( label == null || label.isEmpty() ) + throw new Exception( "Could not get input series: label invalid" ); + + return tryGetTestFile( "/inputs/" + label ); + } + + public static List<String> getQueries( String label ) throws Exception + { + // Safety first + if ( label == null || label.isEmpty() ) + throw new Exception( "Could not get queries: label invalid" ); + + return tryGetTestFile( "/queries/" + label ); + } + + public static List<String> executeQuery( String query, boolean printOut ) throws Exception + { + List<String> result = null; + + // Safety first + if ( query == null || query.isEmpty() ) + throw new Exception( "Could not execute query: invalid input param" ); + + if ( influxDB == null ) + throw new Exception( "Could not execute query: no InfluxDB connection" ); + + // Query it + Query fluxQuery = new Query( query, testConfig.getProperty("influxDB_DB") ); + QueryResult qResult = influxDB.query( fluxQuery, TimeUnit.NANOSECONDS ); + + // Safety + if ( qResult == null ) + throw new Exception( "Got null InfluxDB query result for: " + query ); + else + result = parseFluxQueryResult( qResult ); + + if ( printOut ) + { + String rOut = ""; + for ( String line : result ) + rOut += line + "\n"; + + logger.info( rOut ); + } + + return result; + } + + // Private methods --------------------------------------------------------- + private static List<String> tryGetTestFile( String path ) throws Exception + { + List<String> result = new ArrayList<>(); + + // Safety first + if ( path == null || path.isEmpty() ) + throw new Exception( "Could not get test file: path invalid" ); + + InputStream is = null; + BufferedReader br = null; + + try + { + // Load resource as stream + is = ClassLoader.class.getResourceAsStream( path ); + + if ( is != null ) + { + // Read into list + br = new BufferedReader( new InputStreamReader(is) ); + String nextLine; + + while( (nextLine = br.readLine()) != null ) + { + if ( !nextLine.startsWith("#") && !nextLine.isEmpty() ) + result.add( nextLine ); + } + } + else + throw new Exception( "Failed to find test file" ); + } + catch ( Exception ex ) + { + String err = "Could not find test file: " + ex.getMessage(); + logger.error( err ); + } + // Clean up + finally + { + try + { + if ( br != null ) + br.close(); + + if ( is != null ) + is.close(); + } + catch ( Exception ex ) + { logger.warn( "Had problems closing resource input stream: " + ex.getMessage() ); } + } + + // Final safety check + if ( result.isEmpty() ) + throw new Exception( "Found no lines in test file" ); + + return result; + } + + /** + * This method simply parses the result of the query and formats as CSV (tab delimited) + * + * @param queryResult - The structured query result returned by InfluxDB + * @return - A list of rows in Line Protocol format + */ + private static List<String> parseFluxQueryResult( QueryResult queryResult ) + { + List<String> result = new ArrayList<>(); + + for ( Result res : queryResult.getResults() ) + { + for ( Series series : res.getSeries() ) + { + // Result series name + result.add( series.getName() ); + + // TODO: Unclear what the tags collection is for at the moment + + // Column titles + String colNames = ""; + int volInd = 0; + for ( String colName : series.getColumns() ) + { + // Special formatting for time value + if ( volInd == 0 ) + colNames += colName + "\t\t\t"; + else + colNames += colName + "\t"; + + ++volInd; + } + + + colNames = colNames.substring( 0, colNames.length() -1 ); // Trim last comma + result.add( colNames ); + + // Results + for ( List<Object> rowList : series.getValues() ) + { + String valList = ""; + volInd = 0; + + for ( Object val : rowList ) + { + // Special formatting for time value + // Note: some nasty rounding errors in nanosecond number component + // for now round down to millisecond + if ( volInd == 0 ) + { + Double tD = (Double) val; + Double msFrac = tD / 100000; + Long tL = msFrac.longValue(); + tL *= 100000; + + valList += tL.toString() + "\t"; + + } + else + valList += val.toString() + "\t"; + + ++volInd; + } + + valList = valList.substring( 0, valList.length() -1 ); // Trim last comma + result.add( valList ); + } + } + } + + return result; + } +} diff --git a/src/clmc-spec/src/test/java/uk/ac/soton/innovation/flame/clmc/monspec/test/base/TestSuiteBase.java b/src/clmc-spec/src/test/java/uk/ac/soton/innovation/flame/clmc/monspec/test/base/TestSuiteBase.java new file mode 100644 index 0000000000000000000000000000000000000000..965f11170ed312d2ab21ddd83866e96731382fe2 --- /dev/null +++ b/src/clmc-spec/src/test/java/uk/ac/soton/innovation/flame/clmc/monspec/test/base/TestSuiteBase.java @@ -0,0 +1,94 @@ +///////////////////////////////////////////////////////////////////////// +// +// © University of Southampton IT Innovation Centre, 2017 +// +// Copyright in this software belongs to University of Southampton +// IT Innovation Centre of Gamma House, Enterprise Road, +// Chilworth Science Park, Southampton, SO16 7NS, UK. +// +// This software may not be used, sold, licensed, transferred, copied +// or reproduced in whole or in part in any manner or form or in or +// on any media by any person other than in accordance with the terms +// of the Licence Agreement supplied with the software, or otherwise +// without the prior written consent of the copyright owners. +// +// This software is distributed WITHOUT ANY WARRANTY, without even the +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +// PURPOSE, except where stated in the Licence Agreement supplied with +// the software. +// +// Created By : Simon Crowle +// Created Date : 19/12/2017 +// Created for Project : FLAME +// +///////////////////////////////////////////////////////////////////////// +package uk.ac.soton.innovation.flame.clmc.monspec.test.base; + +import java.util.Properties; +import org.influxdb.InfluxDB; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.runner.RunWith; +import org.junit.runners.Suite; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + + +@RunWith(Suite.class) +@Suite.SuiteClasses({}) +/** + * TestSuiteBase is an abstract class used to set up Influx test suites. Derive + * from this class to create InfluxDB test suites + */ +public abstract class TestSuiteBase +{ + private static Logger logger = LoggerFactory.getLogger( TestSuiteBase.class ); + + @BeforeClass + public static void setUpClass() throws Exception + { + InfluxUtility.getInfluxDBConnection(); // throws if connection fails + + destroyTestDatabase(); + } + + @AfterClass + public static void tearDownClass() throws Exception + { + Properties config = InfluxUtility.getTestConfig(); + + if ( "true".equals( config.getProperty("clearDBOnExit")) ) + destroyTestDatabase(); + } + + @Before + public void setUp() throws Exception + { + + } + + @After + public void tearDown() throws Exception + { + + } + + // Private methods --------------------------------------------------------- + private static void destroyTestDatabase() + { + try + { + String dbName = InfluxUtility.getTestConfig().getProperty( "influxDB_DB" ); + InfluxDB influxDB = InfluxUtility.getInfluxDBConnection(); + + if ( influxDB.databaseExists( dbName ) ) + influxDB.deleteDatabase( dbName ); + } + catch ( Exception ex ) + { + logger.error( ex.getMessage() ); + } + } +}