Clean up, renaming etc

This commit is contained in:
rick.parker 2017-11-14 13:48:35 +00:00
parent fa7c67f34b
commit 08577510af
8 changed files with 23 additions and 433 deletions

View File

@ -1,4 +1,4 @@
package net.corda.jmeter
package com.r3.corda.jmeter
import net.corda.client.rpc.CordaRPCClient
import net.corda.client.rpc.CordaRPCConnection
@ -12,25 +12,30 @@ import org.apache.jmeter.protocol.java.sampler.JavaSamplerContext
import org.apache.jmeter.samplers.SampleResult
class CordaRPCSampler() : AbstractJavaSamplerClient() {
abstract class FlowSampler() : AbstractJavaSamplerClient() {
companion object {
val host = Argument("host", "localhost", "<meta>", "The remote network address (hostname or IP address) to connect to for RPC.")
val port = Argument("port", "10000", "<meta>", "The remote port to connect to for RPC.")
val username = Argument("username", "corda", "<meta>", "The RPC user to connect to connect as.")
val password = Argument("password", "corda_is_awesome", "<meta>", "The password for the RPC user.")
val className = Argument("pluginClassName", "", "<meta>", "The class name of the implementation of ${CordaRPCSampler.Plugin::class.java}.")
val allArgs = setOf(host, port, username, password, className)
val allArgs = setOf(host, port, username, password)
}
var rpcClient: CordaRPCClient? = null
var rpcConnection: CordaRPCConnection? = null
var rpcProxy: CordaRPCOps? = null
var plugin: Plugin? = null
override fun getDefaultParameters(): Arguments {
// Add copies of all args, since they seem to be mutable.
return Arguments().apply { for(arg in allArgs) { addArgument(arg.clone() as Argument) } }
return Arguments().apply {
for (arg in allArgs) {
addArgument(arg.clone() as Argument)
}
for (arg in additionalArgs) {
addArgument(arg.clone() as Argument)
}
}
}
override fun setupTest(context: JavaSamplerContext) {
@ -38,12 +43,11 @@ class CordaRPCSampler() : AbstractJavaSamplerClient() {
rpcClient = CordaRPCClient(NetworkHostAndPort(context.getParameter(host.name), context.getIntParameter(port.name)))
rpcConnection = rpcClient!!.start(context.getParameter(username.name), context.getParameter(password.name))
rpcProxy = rpcConnection!!.proxy
plugin = Class.forName(context.getParameter(className.name)).newInstance() as Plugin
plugin!!.setupTest(rpcProxy!!, context)
setupTest(rpcProxy!!, context)
}
override fun runTest(context: JavaSamplerContext): SampleResult {
val flowInvoke = plugin!!.createFlowInvoke(rpcProxy!!, context)
val flowInvoke = createFlowInvoke(rpcProxy!!, context)
val result = SampleResult()
result.sampleStart()
val handle = rpcProxy!!.startFlowDynamic(flowInvoke!!.flowLogicClass, *(flowInvoke!!.args))
@ -64,8 +68,7 @@ class CordaRPCSampler() : AbstractJavaSamplerClient() {
}
override fun teardownTest(context: JavaSamplerContext) {
plugin!!.teardownTest(rpcProxy!!, context)
plugin = null
teardownTest(rpcProxy!!, context)
rpcProxy = null
rpcConnection!!.close()
rpcConnection = null
@ -73,11 +76,10 @@ class CordaRPCSampler() : AbstractJavaSamplerClient() {
super.teardownTest(context)
}
interface Plugin {
fun setupTest(rpcProxy: CordaRPCOps, testContext: JavaSamplerContext)
fun createFlowInvoke(rpcProxy: CordaRPCOps, testContext: JavaSamplerContext): FlowInvoke<*>
fun teardownTest(rpcProxy: CordaRPCOps, testContext: JavaSamplerContext)
}
abstract val additionalArgs: Set<Argument>
abstract fun setupTest(rpcProxy: CordaRPCOps, testContext: JavaSamplerContext)
abstract fun createFlowInvoke(rpcProxy: CordaRPCOps, testContext: JavaSamplerContext): FlowInvoke<*>
abstract fun teardownTest(rpcProxy: CordaRPCOps, testContext: JavaSamplerContext)
class FlowInvoke<T : FlowLogic<*>>(val flowLogicClass: Class<out T>, val args: Array<Any?>)
}

View File

@ -8,11 +8,6 @@ import java.nio.file.Files
import java.nio.file.Paths
import kotlin.streams.asSequence
/**
* A wrapper around JMeter to make it run without having a JMeter download installed locally. One mode is used for
* running on a remote cluster using an all-in-one bundle JAR using Capsule. The other is just used to run based on current
* classpath, but with optional SSH tunnelling logic automatically invoked.
*/
class Launcher {
companion object {
@JvmStatic
@ -50,16 +45,11 @@ class Launcher {
}
jmeter.start(arrayOf("-s", "-p", (capsuleDirPath / "jmeter.properties").toString()) + extraArgs + args)
} else {
val searchPath = Files.readAllLines(Paths.get(System.getProperty("search_paths_file"))).first()
logger.info("search_paths = $searchPath")
System.setProperty("search_paths", searchPath)
jmeter.start(maybeOpenSshTunnels(args))
}
}
private fun maybeOpenSshTunnels(args: Array<String>): Array<String> {
// We trim the args at the point "-Xssh" appears in the array of args. Anything after that is a host to
// SSH tunnel to.
var index = 0
for (arg in args) {
if (arg == "-Xssh") {

View File

@ -10,9 +10,7 @@ import java.io.BufferedReader
import java.io.InputStreamReader
import java.util.*
/**
* Creates SSH tunnels for remote controlling SSH servers/agents from the local host (via UI or headless).
*/
class Ssh {
companion object {
val log = LoggerFactory.getLogger(this::class.java)
@ -30,7 +28,7 @@ class Ssh {
val jmeterProps = loadProps("/jmeter.properties")
// The port the JMeter remote agents call back to on this client host.
val clientRmiLocalPort = jmeterProps.getProperty("client.rmi.localport").toInt()
// Remote RMI registry port.
// TODO: Where is this value used? Just on the remote agent to set up the RMI registry?
val serverRmiPort = jmeterProps.getProperty("server.rmi.port", "1099").toInt()
// Where JMeter driver will try to connect for remote agents (should all be localhost so can ssh tunnel).
@ -49,6 +47,7 @@ class Ssh {
val session = connectToHost(jsch, remoteHost, userName)
sessions += session
// TODO: maybe check the local host is actually "localhost"?
// For tunnelling the RMI registry on the remote agent
// ssh ${remoteHostAndPort.host} -L 0.0.0.0:${localHostAndPort.port}:localhost:$serverRmiPort -N
createOutboundTunnel(session, NetworkHostAndPort("0.0.0.0", localHostAndPort.port), NetworkHostAndPort("localhost", serverRmiPort))

View File

@ -1,65 +0,0 @@
package net.corda.jmeter
import net.corda.core.internal.div
import org.apache.jmeter.JMeter
import org.slf4j.LoggerFactory
import java.net.InetAddress
import java.nio.file.Files
import java.nio.file.Paths
import kotlin.streams.asSequence
class Launcher {
companion object {
@JvmStatic
fun main(args: Array<String>) {
val logger = LoggerFactory.getLogger(this::class.java)
logger.info("Launcher called with ${args.toList()}")
val jmeter = JMeter()
val capsuleDir = System.getProperty("capsule.dir")
if (capsuleDir != null) {
// We are running under Capsule, so assume we want a JMeter slave server to be controlled from
// elsewhere.
logger.info("Starting JMeter in server mode from $capsuleDir")
val capsuleDirPath = Paths.get(capsuleDir)
// Add all JMeter and Corda jars onto the JMeter search_paths
val searchPath = Files.list(capsuleDirPath).asSequence().filter {
val filename = it.fileName.toString()
filename.endsWith(".jar") && (filename.contains("corda") || filename.contains("jmeter", true))
}.joinToString(";")
logger.info("search_paths = $searchPath")
System.setProperty("search_paths", searchPath)
// Set the JMeter home as a property rather than command line arg, due to inconsistent code in JMeter.
System.setProperty("jmeter.home", capsuleDir)
// Create two dirs that JMeter expects, if they don't already exist.
Files.createDirectories(capsuleDirPath / "lib" / "ext")
Files.createDirectories(capsuleDirPath / "lib" / "junit")
// Now see if we have a hostname specific property file, and if so, add it.
val hostName = InetAddress.getLocalHost().hostName
val hostSpecificConfigFile = capsuleDirPath / "$hostName.properties"
logger.info("Attempting to use host-specific properties file $hostSpecificConfigFile")
val extraArgs = if (Files.exists(hostSpecificConfigFile)) {
logger.info("Found host-specific properties file")
arrayOf("-q", hostSpecificConfigFile.toString())
} else {
emptyArray()
}
jmeter.start(arrayOf("-s", "-p", (capsuleDirPath / "jmeter.properties").toString()) + extraArgs + args)
} else {
jmeter.start(maybeOpenSshTunnels(args))
}
}
private fun maybeOpenSshTunnels(args: Array<String>): Array<String> {
var index = 0
for (arg in args) {
if (arg == "-Xssh") {
// start ssh
Ssh.main(args.copyOfRange(index + 1, args.size), false)
return if (index == 0) emptyArray() else args.copyOfRange(0, index)
}
index++
}
return args
}
}
}

View File

@ -1,113 +0,0 @@
package net.corda.jmeter
import com.jcraft.jsch.JSch
import com.jcraft.jsch.Session
import net.corda.core.utilities.NetworkHostAndPort
import net.corda.loadtest.setupJSchWithSshAgent
import net.corda.nodeapi.internal.addShutdownHook
import org.slf4j.LoggerFactory
import java.io.BufferedReader
import java.io.InputStreamReader
import java.util.*
class Ssh {
companion object {
val log = LoggerFactory.getLogger(this::class.java)
@JvmStatic
@JvmOverloads
fun main(args: Array<String>, wait: Boolean = true) {
val userName = System.getProperty("user.name")
val jsch = setupJSchWithSshAgent()
val sessions = mutableListOf<Session>()
// Read jmeter.properties
// For each host:port combo, map them to hosts from command line
val jmeterProps = loadProps("/jmeter.properties")
// The port the JMeter remote agents call back to on this client host.
val clientRmiLocalPort = jmeterProps.getProperty("client.rmi.localport").toInt()
// TODO: Where is this value used? Just on the remote agent to set up the RMI registry?
val serverRmiPort = jmeterProps.getProperty("server.rmi.port", "1099").toInt()
// Where JMeter driver will try to connect for remote agents (should all be localhost so can ssh tunnel).
val localHostsAndPorts = jmeterProps.getProperty("remote_hosts", "").split(',').map { it.trim() }
args.zip(localHostsAndPorts) { remoteHost, localHostAndPortString ->
// Actual remote host and port we will tunnel to.
log.info("Creating tunnels for $remoteHost")
val localHostAndPort = NetworkHostAndPort.parse(localHostAndPortString)
// For the remote host, load their specific property file, since it specifies remote RMI server port
val unqualifiedHostName = remoteHost.substringBefore('.')
val hostProps = loadProps("/$unqualifiedHostName.properties")
val serverRmiLocalPort = hostProps.getProperty("server.rmi.localport", jmeterProps.getProperty("server.rmi.localport")).toInt()
val session = connectToHost(jsch, remoteHost, userName)
sessions += session
// TODO: maybe check the local host is actually "localhost"?
// For tunnelling the RMI registry on the remote agent
// ssh ${remoteHostAndPort.host} -L 0.0.0.0:${localHostAndPort.port}:localhost:$serverRmiPort -N
createOutboundTunnel(session, NetworkHostAndPort("0.0.0.0", localHostAndPort.port), NetworkHostAndPort("localhost", serverRmiPort))
// For tunnelling the actual connection to the remote agent
// ssh ${remoteHostAndPort.host} -L 0.0.0.0:$serverRmiLocalPort:localhost:$serverRmiLocalPort -N
createOutboundTunnel(session, NetworkHostAndPort("0.0.0.0", serverRmiLocalPort), NetworkHostAndPort("localhost", serverRmiLocalPort))
// For returning results to the client
// ssh ${remoteHostAndPort.host} -R 0.0.0.0:clientRmiLocalPort:localhost:clientRmiLocalPort -N
createInboundTunnel(session, NetworkHostAndPort("0.0.0.0", clientRmiLocalPort), NetworkHostAndPort("localhost", clientRmiLocalPort))
}
if (wait) {
val input = BufferedReader(InputStreamReader(System.`in`))
do {
log.info("Type 'quit' to exit cleanly.")
} while (input.readLine() != "quit")
sessions.forEach {
log.info("Closing tunnels for ${it.host}")
it.disconnect()
}
} else {
addShutdownHook {
sessions.forEach {
log.info("Closing tunnels for ${it.host}")
it.disconnect()
}
}
}
}
private fun loadProps(filename: String): Properties {
val props = Properties()
this::class.java.getResourceAsStream(filename).use {
props.load(it)
}
return props
}
fun connectToHost(jSch: JSch, remoteHost: String, remoteUserName: String): Session {
val session = jSch.getSession(remoteUserName, remoteHost, 22)
// We don't check the host fingerprints because they may change often
session.setConfig("StrictHostKeyChecking", "no")
log.info("Connecting to $remoteHost...")
session.connect()
log.info("Connected to $remoteHost!")
return session
}
fun createOutboundTunnel(session: Session, local: NetworkHostAndPort, remote: NetworkHostAndPort) {
log.info("Creating outbound tunnel from $local to $remote with ${session.host}...")
session.setPortForwardingL(local.host, local.port, remote.host, remote.port)
log.info("Tunnel created!")
}
fun createInboundTunnel(session: Session, local: NetworkHostAndPort, remote: NetworkHostAndPort) {
log.info("Creating inbound tunnel from $remote to $local on ${session.host}...")
session.setPortForwardingR(remote.host, remote.port, local.host, local.port)
log.info("Tunnel created!")
}
}
}

View File

@ -1,46 +0,0 @@
package net.corda.jmeter
import net.corda.core.identity.CordaX500Name
import net.corda.core.identity.Party
import net.corda.core.messaging.CordaRPCOps
import net.corda.core.utilities.OpaqueBytes
import net.corda.finance.DOLLARS
import net.corda.finance.flows.CashIssueFlow
import net.corda.jmeter.CordaRPCSampler.FlowInvoke
import org.apache.jmeter.protocol.java.sampler.JavaSamplerContext
abstract class AsbtractTraderDemoPlugin : CordaRPCSampler.Plugin {
//lateinit var buyer: Party
//lateinit var seller: Party
lateinit var notary: Party
//val bankA = CordaX500Name(organisation = "Bank A", locality = "London", country = "GB")
//val bankB = CordaX500Name(organisation = "Bank B", locality = "New York", country = "US")
val node1 = CordaX500Name(commonName = null, state = null, organisation = "Perf-10.155.0.4", organisationUnit = "Corda", locality = "London", country = "GB")
protected fun getIdentities(rpc: CordaRPCOps) {
//buyer = rpc.wellKnownPartyFromX500Name(bankA) ?: throw IllegalStateException("Don't know $bankA")
//seller = rpc.wellKnownPartyFromX500Name(bankB) ?: throw IllegalStateException("Don't know $bankB")
//notary = rpc.notaryIdentities().first()
notary = rpc.wellKnownPartyFromX500Name(node1) ?: throw IllegalStateException("Don't know $node1")
}
}
class CashIssuerPlugin : AsbtractTraderDemoPlugin() {
override fun setupTest(rpcProxy: CordaRPCOps, testContext: JavaSamplerContext) {
getIdentities(rpcProxy)
}
override fun teardownTest(rpcProxy: CordaRPCOps, testContext: JavaSamplerContext) {
}
override fun createFlowInvoke(rpcProxy: CordaRPCOps, testContext: JavaSamplerContext): FlowInvoke<CashIssueFlow> {
val amount = 1_100_000_000_000.DOLLARS
//val amounts = calculateRandomlySizedAmounts(amount, 3, 10, Random())
//rpc.startFlow(net.corda.finance.flows::CashIssueFlow, amount, OpaqueBytes.of(1), notary).returnValue.getOrThrow()
return FlowInvoke<CashIssueFlow>(CashIssueFlow::class.java, arrayOf(amount, OpaqueBytes.of(1), notary))
}
}

View File

@ -46,7 +46,7 @@
</elementProp>
<elementProp name="password" elementType="Argument">
<stringProp name="Argument.name">password</stringProp>
<stringProp name="Argument.value">Password Here</stringProp>
<stringProp name="Argument.value">corda_is_awesome</stringProp>
<stringProp name="Argument.metadata">=</stringProp>
</elementProp>
<elementProp name="notaryName" elementType="Argument">

View File

@ -1,177 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<jmeterTestPlan version="1.2" properties="3.2" jmeter="3.3 r1808647">
<hashTree>
<TestPlan guiclass="TestPlanGui" testclass="TestPlan" testname="Test Plan" enabled="true">
<stringProp name="TestPlan.comments"></stringProp>
<boolProp name="TestPlan.functional_mode">false</boolProp>
<boolProp name="TestPlan.serialize_threadgroups">false</boolProp>
<elementProp name="TestPlan.user_defined_variables" elementType="Arguments" guiclass="ArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true">
<collectionProp name="Arguments.arguments"/>
</elementProp>
<stringProp name="TestPlan.user_define_classpath"></stringProp>
</TestPlan>
<hashTree>
<ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="Thread Group" enabled="true">
<stringProp name="ThreadGroup.on_sample_error">continue</stringProp>
<elementProp name="ThreadGroup.main_controller" elementType="LoopController" guiclass="LoopControlPanel" testclass="LoopController" testname="Loop Controller" enabled="true">
<boolProp name="LoopController.continue_forever">false</boolProp>
<stringProp name="LoopController.loops">10</stringProp>
</elementProp>
<stringProp name="ThreadGroup.num_threads">3</stringProp>
<stringProp name="ThreadGroup.ramp_time"></stringProp>
<longProp name="ThreadGroup.start_time">1509455820000</longProp>
<longProp name="ThreadGroup.end_time">1509455820000</longProp>
<boolProp name="ThreadGroup.scheduler">false</boolProp>
<stringProp name="ThreadGroup.duration"></stringProp>
<stringProp name="ThreadGroup.delay"></stringProp>
</ThreadGroup>
<hashTree>
<JavaSampler guiclass="JavaTestSamplerGui" testclass="JavaSampler" testname="Java Request" enabled="true">
<elementProp name="arguments" elementType="Arguments" guiclass="ArgumentsPanel" testclass="Arguments" enabled="true">
<collectionProp name="Arguments.arguments">
<elementProp name="host" elementType="Argument">
<stringProp name="Argument.name">host</stringProp>
<stringProp name="Argument.value">localhost</stringProp>
<stringProp name="Argument.metadata">=</stringProp>
</elementProp>
<elementProp name="port" elementType="Argument">
<stringProp name="Argument.name">port</stringProp>
<stringProp name="Argument.value">10012</stringProp>
<stringProp name="Argument.metadata">=</stringProp>
</elementProp>
<elementProp name="username" elementType="Argument">
<stringProp name="Argument.name">username</stringProp>
<stringProp name="Argument.value">demo</stringProp>
<stringProp name="Argument.metadata">=</stringProp>
</elementProp>
<elementProp name="password" elementType="Argument">
<stringProp name="Argument.name">password</stringProp>
<stringProp name="Argument.value">demo</stringProp>
<stringProp name="Argument.metadata">=</stringProp>
</elementProp>
<elementProp name="pluginClassName" elementType="Argument">
<stringProp name="Argument.name">pluginClassName</stringProp>
<stringProp name="Argument.value">net.corda.jmeter.CashIssuerPlugin</stringProp>
<stringProp name="Argument.metadata">=</stringProp>
</elementProp>
</collectionProp>
</elementProp>
<stringProp name="classname">net.corda.jmeter.CordaRPCSampler</stringProp>
</JavaSampler>
<hashTree/>
</hashTree>
<ResultCollector guiclass="TableVisualizer" testclass="ResultCollector" testname="View Results in Table" enabled="true">
<boolProp name="ResultCollector.error_logging">false</boolProp>
<objProp>
<name>saveConfig</name>
<value class="SampleSaveConfiguration">
<time>true</time>
<latency>true</latency>
<timestamp>true</timestamp>
<success>true</success>
<label>true</label>
<code>true</code>
<message>true</message>
<threadName>true</threadName>
<dataType>true</dataType>
<encoding>false</encoding>
<assertions>true</assertions>
<subresults>true</subresults>
<responseData>false</responseData>
<samplerData>false</samplerData>
<xml>false</xml>
<fieldNames>true</fieldNames>
<responseHeaders>false</responseHeaders>
<requestHeaders>false</requestHeaders>
<responseDataOnError>false</responseDataOnError>
<saveAssertionResultsFailureMessage>true</saveAssertionResultsFailureMessage>
<assertionsResultsToSave>0</assertionsResultsToSave>
<bytes>true</bytes>
<sentBytes>true</sentBytes>
<threadCounts>true</threadCounts>
<idleTime>true</idleTime>
<connectTime>true</connectTime>
</value>
</objProp>
<stringProp name="filename"></stringProp>
</ResultCollector>
<hashTree/>
<ResultCollector guiclass="StatGraphVisualizer" testclass="ResultCollector" testname="Aggregate Graph" enabled="true">
<boolProp name="ResultCollector.error_logging">false</boolProp>
<objProp>
<name>saveConfig</name>
<value class="SampleSaveConfiguration">
<time>true</time>
<latency>true</latency>
<timestamp>true</timestamp>
<success>true</success>
<label>true</label>
<code>true</code>
<message>true</message>
<threadName>true</threadName>
<dataType>true</dataType>
<encoding>false</encoding>
<assertions>true</assertions>
<subresults>true</subresults>
<responseData>false</responseData>
<samplerData>false</samplerData>
<xml>false</xml>
<fieldNames>true</fieldNames>
<responseHeaders>false</responseHeaders>
<requestHeaders>false</requestHeaders>
<responseDataOnError>false</responseDataOnError>
<saveAssertionResultsFailureMessage>true</saveAssertionResultsFailureMessage>
<assertionsResultsToSave>0</assertionsResultsToSave>
<bytes>true</bytes>
<sentBytes>true</sentBytes>
<threadCounts>true</threadCounts>
<idleTime>true</idleTime>
<connectTime>true</connectTime>
</value>
</objProp>
<stringProp name="filename"></stringProp>
</ResultCollector>
<hashTree/>
<ResultCollector guiclass="GraphVisualizer" testclass="ResultCollector" testname="Graph Results" enabled="true">
<boolProp name="ResultCollector.error_logging">false</boolProp>
<objProp>
<name>saveConfig</name>
<value class="SampleSaveConfiguration">
<time>true</time>
<latency>true</latency>
<timestamp>true</timestamp>
<success>true</success>
<label>true</label>
<code>true</code>
<message>true</message>
<threadName>true</threadName>
<dataType>true</dataType>
<encoding>false</encoding>
<assertions>true</assertions>
<subresults>true</subresults>
<responseData>false</responseData>
<samplerData>false</samplerData>
<xml>false</xml>
<fieldNames>true</fieldNames>
<responseHeaders>false</responseHeaders>
<requestHeaders>false</requestHeaders>
<responseDataOnError>false</responseDataOnError>
<saveAssertionResultsFailureMessage>true</saveAssertionResultsFailureMessage>
<assertionsResultsToSave>0</assertionsResultsToSave>
<bytes>true</bytes>
<sentBytes>true</sentBytes>
<threadCounts>true</threadCounts>
<idleTime>true</idleTime>
<connectTime>true</connectTime>
</value>
</objProp>
<stringProp name="filename"></stringProp>
</ResultCollector>
<hashTree/>
</hashTree>
<WorkBench guiclass="WorkBenchGui" testclass="WorkBench" testname="WorkBench" enabled="true">
<boolProp name="WorkBench.save">true</boolProp>
</WorkBench>
<hashTree/>
</hashTree>
</jmeterTestPlan>