mirror of
https://github.com/corda/corda.git
synced 2024-12-29 09:18:58 +00:00
SSH tunnelling utility. Property files per remote host.
This commit is contained in:
parent
b58744b18a
commit
501f6c4a1a
@ -6,7 +6,8 @@ mainClassName = 'net.corda.jmeter.Launcher'
|
|||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
compile project(':client:rpc')
|
compile project(':client:rpc')
|
||||||
compile project(":finance")
|
compile project(':finance')
|
||||||
|
compile project(':tools:loadtest')
|
||||||
|
|
||||||
// JMeter
|
// JMeter
|
||||||
ext.jmVersion = "3.3"
|
ext.jmVersion = "3.3"
|
||||||
@ -42,8 +43,18 @@ task(runServer, dependsOn: 'classes', type: JavaExec) {
|
|||||||
"-s" ]
|
"-s" ]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
task(runSsh, dependsOn: 'classes', type: JavaExec) {
|
||||||
|
classpath = sourceSets.main.runtimeClasspath
|
||||||
|
main = 'net.corda.jmeter.Ssh'
|
||||||
|
if ( project.hasProperty("jmeterHosts") ) {
|
||||||
|
args Eval.me(jmeterHosts)
|
||||||
|
}
|
||||||
|
standardInput = System.in
|
||||||
|
}
|
||||||
|
|
||||||
run {
|
run {
|
||||||
systemProperty "search_paths", project(':tools:jmeter').configurations.runtime.files.join(";")
|
systemProperty "search_paths", project(':tools:jmeter').configurations.runtime.files.join(";")
|
||||||
|
systemProperty "java.rmi.server.hostname", "localhost"
|
||||||
systemProperty "jmeter.home", sourceSets.main.resources.getSrcDirs().first().getPath()
|
systemProperty "jmeter.home", sourceSets.main.resources.getSrcDirs().first().getPath()
|
||||||
// If you want to debug: jvmArgs += "-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5005"
|
// If you want to debug: jvmArgs += "-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5005"
|
||||||
args+= [ "-p", sourceSets.main.resources.getSrcDirs().first().getPath()+"/jmeter.properties",
|
args+= [ "-p", sourceSets.main.resources.getSrcDirs().first().getPath()+"/jmeter.properties",
|
||||||
@ -70,12 +81,14 @@ task buildJMeterJAR(type: FatCapsule, dependsOn: 'jar') {
|
|||||||
project(':tools:jmeter').jar
|
project(':tools:jmeter').jar
|
||||||
)
|
)
|
||||||
from 'NOTICE' // Copy CDDL notice
|
from 'NOTICE' // Copy CDDL notice
|
||||||
from { "$rootDir/tools/jmeter/build/resources/main/jmeter.properties" }
|
from("$rootDir/tools/jmeter/build/resources/main") {
|
||||||
from { "$rootDir/tools/jmeter/build/resources/main/log4j2.xml" }
|
include "log4j2.xml"
|
||||||
|
include "*.properties"
|
||||||
|
}
|
||||||
|
|
||||||
capsuleManifest {
|
capsuleManifest {
|
||||||
applicationVersion = corda_release_version
|
applicationVersion = corda_release_version
|
||||||
systemProperties['java.rmi.server.hostname'] = '0.0.0.0'
|
systemProperties['java.rmi.server.hostname'] = 'localhost'
|
||||||
minJavaVersion = '1.8.0'
|
minJavaVersion = '1.8.0'
|
||||||
minUpdateVersion['1.8'] = java8_minUpdateVersion
|
minUpdateVersion['1.8'] = java8_minUpdateVersion
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@ package net.corda.jmeter
|
|||||||
import net.corda.core.internal.div
|
import net.corda.core.internal.div
|
||||||
import org.apache.jmeter.JMeter
|
import org.apache.jmeter.JMeter
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
|
import java.net.InetAddress
|
||||||
import java.nio.file.Files
|
import java.nio.file.Files
|
||||||
import java.nio.file.Paths
|
import java.nio.file.Paths
|
||||||
import kotlin.streams.asSequence
|
import kotlin.streams.asSequence
|
||||||
@ -18,8 +19,9 @@ class Launcher {
|
|||||||
// We are running under Capsule, so assume we want a JMeter slave server to be controlled from
|
// We are running under Capsule, so assume we want a JMeter slave server to be controlled from
|
||||||
// elsewhere.
|
// elsewhere.
|
||||||
logger.info("Starting JMeter in server mode from $capsuleDir")
|
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
|
// Add all JMeter and Corda jars onto the JMeter search_paths
|
||||||
val searchPath = Files.list(Paths.get(capsuleDir)).asSequence().filter {
|
val searchPath = Files.list(capsuleDirPath).asSequence().filter {
|
||||||
val filename = it.fileName.toString()
|
val filename = it.fileName.toString()
|
||||||
filename.endsWith(".jar") && (filename.contains("corda") || filename.contains("jmeter", true))
|
filename.endsWith(".jar") && (filename.contains("corda") || filename.contains("jmeter", true))
|
||||||
}.joinToString(";")
|
}.joinToString(";")
|
||||||
@ -28,9 +30,19 @@ class Launcher {
|
|||||||
// Set the JMeter home as a property rather than command line arg, due to inconsistent code in JMeter.
|
// Set the JMeter home as a property rather than command line arg, due to inconsistent code in JMeter.
|
||||||
System.setProperty("jmeter.home", capsuleDir)
|
System.setProperty("jmeter.home", capsuleDir)
|
||||||
// Create two dirs that JMeter expects, if they don't already exist.
|
// Create two dirs that JMeter expects, if they don't already exist.
|
||||||
Files.createDirectories(Paths.get(capsuleDir) / "lib" / "ext")
|
Files.createDirectories(capsuleDirPath / "lib" / "ext")
|
||||||
Files.createDirectories(Paths.get(capsuleDir) / "lib" / "junit")
|
Files.createDirectories(capsuleDirPath / "lib" / "junit")
|
||||||
jmeter.start(arrayOf("-s", "-p", "$capsuleDir/jmeter.properties") + args)
|
// 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 {
|
} else {
|
||||||
jmeter.start(args)
|
jmeter.start(args)
|
||||||
}
|
}
|
||||||
|
102
tools/jmeter/src/main/kotlin/net/corda/jmeter/Ssh.kt
Normal file
102
tools/jmeter/src/main/kotlin/net/corda/jmeter/Ssh.kt
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
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 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
|
||||||
|
fun main(args: Array<String>) {
|
||||||
|
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))
|
||||||
|
}
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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!")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -15,9 +15,9 @@
|
|||||||
<stringProp name="ThreadGroup.on_sample_error">continue</stringProp>
|
<stringProp name="ThreadGroup.on_sample_error">continue</stringProp>
|
||||||
<elementProp name="ThreadGroup.main_controller" elementType="LoopController" guiclass="LoopControlPanel" testclass="LoopController" testname="Loop Controller" enabled="true">
|
<elementProp name="ThreadGroup.main_controller" elementType="LoopController" guiclass="LoopControlPanel" testclass="LoopController" testname="Loop Controller" enabled="true">
|
||||||
<boolProp name="LoopController.continue_forever">false</boolProp>
|
<boolProp name="LoopController.continue_forever">false</boolProp>
|
||||||
<stringProp name="LoopController.loops">1000</stringProp>
|
<stringProp name="LoopController.loops">10</stringProp>
|
||||||
</elementProp>
|
</elementProp>
|
||||||
<stringProp name="ThreadGroup.num_threads">2</stringProp>
|
<stringProp name="ThreadGroup.num_threads">3</stringProp>
|
||||||
<stringProp name="ThreadGroup.ramp_time"></stringProp>
|
<stringProp name="ThreadGroup.ramp_time"></stringProp>
|
||||||
<longProp name="ThreadGroup.start_time">1509455820000</longProp>
|
<longProp name="ThreadGroup.start_time">1509455820000</longProp>
|
||||||
<longProp name="ThreadGroup.end_time">1509455820000</longProp>
|
<longProp name="ThreadGroup.end_time">1509455820000</longProp>
|
||||||
|
@ -238,7 +238,7 @@ gui.quick_9=ViewResultsFullVisualizer
|
|||||||
#---------------------------------------------------------------------------
|
#---------------------------------------------------------------------------
|
||||||
|
|
||||||
# Remote Hosts - comma delimited
|
# Remote Hosts - comma delimited
|
||||||
remote_hosts=Rick-Parker.local
|
remote_hosts=127.0.0.1:20099
|
||||||
#remote_hosts=localhost:1099,localhost:2010
|
#remote_hosts=localhost:1099,localhost:2010
|
||||||
|
|
||||||
# RMI port to be used by the server (must start rmiregistry with same port)
|
# RMI port to be used by the server (must start rmiregistry with same port)
|
||||||
@ -261,7 +261,7 @@ remote_hosts=Rick-Parker.local
|
|||||||
# Parameter that controls the RMI port used by the RemoteSampleListenerImpl (The Controler)
|
# Parameter that controls the RMI port used by the RemoteSampleListenerImpl (The Controler)
|
||||||
# Default value is 0 which means port is randomly assigned
|
# Default value is 0 which means port is randomly assigned
|
||||||
# You may need to open Firewall port on the Controller machine
|
# You may need to open Firewall port on the Controller machine
|
||||||
#client.rmi.localport=0
|
client.rmi.localport=4001
|
||||||
|
|
||||||
# When distributed test is starting, there may be several attempts to initialize
|
# When distributed test is starting, there may be several attempts to initialize
|
||||||
# remote engines. By default, only single try is made. Increase following property
|
# remote engines. By default, only single try is made. Increase following property
|
||||||
@ -280,7 +280,7 @@ remote_hosts=Rick-Parker.local
|
|||||||
|
|
||||||
# To use a specific port for the JMeter server engine, define
|
# To use a specific port for the JMeter server engine, define
|
||||||
# the following property before starting the server:
|
# the following property before starting the server:
|
||||||
#server.rmi.localport=4000
|
server.rmi.localport=5000
|
||||||
|
|
||||||
# From JMeter 2.3.1, the jmeter server creates the RMI registry as part of the server process.
|
# From JMeter 2.3.1, the jmeter server creates the RMI registry as part of the server process.
|
||||||
# To stop the server creating the RMI registry:
|
# To stop the server creating the RMI registry:
|
||||||
|
1
tools/jmeter/src/main/resources/perf-node-1.properties
Normal file
1
tools/jmeter/src/main/resources/perf-node-1.properties
Normal file
@ -0,0 +1 @@
|
|||||||
|
server.rmi.localport=10101
|
1
tools/jmeter/src/main/resources/perf-node-2.properties
Normal file
1
tools/jmeter/src/main/resources/perf-node-2.properties
Normal file
@ -0,0 +1 @@
|
|||||||
|
server.rmi.localport=10102
|
1
tools/jmeter/src/main/resources/perf-node-3.properties
Normal file
1
tools/jmeter/src/main/resources/perf-node-3.properties
Normal file
@ -0,0 +1 @@
|
|||||||
|
server.rmi.localport=10103
|
1
tools/jmeter/src/main/resources/perf-node-4.properties
Normal file
1
tools/jmeter/src/main/resources/perf-node-4.properties
Normal file
@ -0,0 +1 @@
|
|||||||
|
server.rmi.localport=10104
|
1
tools/jmeter/src/main/resources/perf-notary.properties
Normal file
1
tools/jmeter/src/main/resources/perf-notary.properties
Normal file
@ -0,0 +1 @@
|
|||||||
|
server.rmi.localport=10100
|
Loading…
Reference in New Issue
Block a user