Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bug fix #2

Merged
merged 2 commits into from
Oct 20, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 18 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,19 +27,17 @@ Under tab "Available Plugins", select "SSHMon Samples Collector", then click "Ap

### Via Package from [JMeter-Plugins.org](https://jmeter-plugins.org/)

Extract the [zip package](https://jmeter-plugins.org/files/packages/tilln-sshmon-1.0.zip) into JMeter's lib directory, then restart JMeter.
Extract the [zip package](https://jmeter-plugins.org/files/packages/tilln-sshmon-1.1.zip) into JMeter's lib directory, then restart JMeter.

### Via Manual Download

1. Copy the [jmeter-sshmon jar file](https://github.com/tilln/jmeter-sshmon/releases/download/1.0/jmeter-sshmon-1.0.jar) into JMeter's lib/ext directory.
1. Copy the [jmeter-sshmon jar file](https://github.com/tilln/jmeter-sshmon/releases/download/1.1/jmeter-sshmon-1.1.jar) into JMeter's lib/ext directory.
2. Copy the following dependencies into JMeter's lib directory:
* [kg.apc / jmeter-plugins-cmn-jmeter](https://search.maven.org/remotecontent?filepath=kg/apc/jmeter-plugins-cmn-jmeter/0.5/jmeter-plugins-cmn-jmeter-0.5.jar)
* [commons-io / commons-io](https://search.maven.org/remotecontent?filepath=commons-io/commons-io/2.5/commons-io-2.5.jar)
* [org.apache.commons / commons-pool](https://search.maven.org/remotecontent?filepath=org/apache/commons/commons-pool2/2.4.2/commons-pool2-2.4.2.jar)
* [kg.apc / jmeter-plugins-cmn-jmeter](https://search.maven.org/remotecontent?filepath=kg/apc/jmeter-plugins-cmn-jmeter/0.6/jmeter-plugins-cmn-jmeter-0.6.jar)
* [com.jcraft.jsch / jsch](https://search.maven.org/remotecontent?filepath=com/jcraft/jsch/0.1.54/jsch-0.1.54.jar)
3. Restart JMeter.

**Important: Make sure to remove any older jar file version than `jmeter-plugins-cmn-jmeter-0.5.jar` from JMeter's lib directory!**
**Important: Make sure to remove any older jar file version than `jmeter-plugins-cmn-jmeter-0.6.jar` from JMeter's lib directory!**

Usage
-----
Expand Down Expand Up @@ -100,13 +98,21 @@ For details refer to the [JMeter-Plugins wiki](https://jmeter-plugins.org/wiki/S
### JMeter Properties

The following properties control the plugin behaviour:
* `jmeter.sshmon.knownHosts`: Filename of a known_hosts file containing public keys of trusted remote servers (in OpenSSH format). If not set, no validation will be performed.
* `jmeter.sshmon.interval`: Define the metrics collection interval in milliseconds (default=1 second).
* `jmeter.sshmon.forceOutputFile` - (true/false) makes sure JMeter writes metrics to CSV file in the current directory if no filename is specified (default=false).
* `jmeter.sshmon.knownHosts`: Filename of a known_hosts file containing public keys of trusted remote servers (in OpenSSH format).
If defined, connections to unknown hosts will be rejected (via `StrictHostKeyChecking=yes`).
If undefined, connections to unknown hosts will be established (via `StrictHostKeyChecking=no`).
Default: undefined.
* `jmeter.sshmon.interval`: Metrics collection interval in milliseconds.
This is inclusive of the execution time of the remote commands.
Default: 1 second.
* `jmeter.sshmon.forceOutputFile` (true/false): Makes sure JMeter writes metrics to CSV file in the current directory if no filename is specified.
Default: false.

Limitations
-----------

* Samples are collected by a single thread, so if the command takes more than an insignificant amount of time to run, the frequency of sample collection will be limited.
A separate monitor may be used in this case.
* The help link on the plugin GUI currently points to an incorrect location. This is to be resolved with the next release of `jmeter-plugins-cmn-jmeter` (see [this PR](https://github.com/undera/jmeter-plugins/pull/162)).
* Samples are collected by a single thread, so if a command takes more than an insignificant amount of time to run, the frequency of sample collection will be limited.
Even more so if more than one command is sampled. In this case, use a separate monitor for each sample command.
* When a JMeter test ends, this plugin will not interrupt the collector thread but let the current sample finish before stopping.
This may take longer than the JMeter engine [waits](https://jmeter.apache.org/usermanual/get-started.html#shutdown) in headless (non-GUI) mode.
In this case, increase the JMeter property `jmeter.exit.check.pause`.
16 changes: 6 additions & 10 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,13 @@
<groupId>co.nz.breakpoint.jmeter</groupId>
<artifactId>jmeter-sshmon</artifactId>
<packaging>jar</packaging>
<version>1.0</version>
<version>1.1</version>
<name>jmeter-sshmon</name>
<url>http://maven.apache.org</url>

<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>

<scm>
Expand Down Expand Up @@ -80,7 +81,6 @@
<goal>start</goal>
</goals>
<configuration>
<skip>true</skip>
<arguments>
<argument>java</argument>
<argument>-cp</argument>
Expand Down Expand Up @@ -130,7 +130,7 @@
<dependency>
<groupId>kg.apc</groupId>
<artifactId>jmeter-plugins-cmn-jmeter</artifactId>
<version>0.5</version>
<version>0.6</version>
</dependency>
<dependency>
<groupId>com.jcraft</groupId>
Expand All @@ -140,12 +140,8 @@
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.4.2</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.5</version>
<version>2.6.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>kg.apc</groupId>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package nz.co.breakpoint.jmeter.vizualizers.sshmon;

import java.util.HashMap;
import java.util.Map;
import com.jcraft.jsch.Logger;
import org.apache.jorphan.logging.LoggingManager;
import org.apache.log.Priority;

class JSchLogger implements Logger {

private static final org.apache.log.Logger log = LoggingManager.getLoggerForClass();

private static final Map<Integer, Priority> levels = new HashMap<Integer, Priority>();
static {
levels.put(Logger.DEBUG, Priority.DEBUG);
levels.put(Logger.INFO, Priority.INFO);
levels.put(Logger.WARN, Priority.WARN);
levels.put(Logger.ERROR, Priority.ERROR);
levels.put(Logger.FATAL, Priority.FATAL_ERROR);
}

@Override
public boolean isEnabled(int level) {
return log.isPriorityEnabled(levels.get(level));
}

@Override
public void log(int level, String message) {
Priority p = levels.get(level);
if (p != null) {
log.log(p, message);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -53,4 +53,10 @@ protected void initiateConnectors() {
samplers.add(new SSHMonSampler(label, connectionDetails, command, isDelta));
}
}

@Override
public void testEnded(String host) {
super.testEnded(host);
SSHMonSampler.clearConnectionPool();
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
package nz.co.breakpoint.jmeter.vizualizers.sshmon;

import java.io.InterruptedIOException;
import java.io.ByteArrayOutputStream;

import kg.apc.jmeter.vizualizers.MonitoringSampler;
import kg.apc.jmeter.vizualizers.MonitoringSampleGenerator;

import org.apache.commons.io.IOUtils;
import org.apache.commons.pool2.KeyedObjectPool;
import org.apache.commons.pool2.impl.GenericKeyedObjectPool;
import org.apache.commons.pool2.impl.GenericKeyedObjectPoolConfig;
Expand Down Expand Up @@ -35,12 +34,23 @@ public class SSHMonSampler
*/
private static KeyedObjectPool<ConnectionDetails, Session> pool;
static {
GenericKeyedObjectPoolConfig config = new GenericKeyedObjectPoolConfig();
GenericKeyedObjectPoolConfig<Session> config = new GenericKeyedObjectPoolConfig<>();
config.setMinIdlePerKey(1);
config.setTestOnBorrow(true);
log.debug("Creating GenericKeyedObjectPool");
pool = new GenericKeyedObjectPool<ConnectionDetails, Session>(new SSHSessionFactory(), config);
}

public static void clearConnectionPool() {
log.debug("Clearing connection pool");
try {
pool.clear();
}
catch (Exception e) {
log.error("Failed to clear connection pool: ", e);
}
}

public SSHMonSampler(String name, ConnectionDetails connectionDetails, String remoteCommand, boolean sampleDeltaValue) {
this.metricName = name;
this.connectionDetails = connectionDetails;
Expand All @@ -52,6 +62,7 @@ public SSHMonSampler(String name, ConnectionDetails connectionDetails, String re
public void generateSamples(MonitoringSampleGenerator collector) {
Session session = null;
ChannelExec channel = null;
ByteArrayOutputStream result = new ByteArrayOutputStream();

try {
log.debug("Borrowing session for "+connectionDetails);
Expand All @@ -60,10 +71,14 @@ public void generateSamples(MonitoringSampleGenerator collector) {
channel = (ChannelExec)session.openChannel("exec");
channel.setCommand(remoteCommand);
channel.setPty(true);
channel.setOutputStream(result);
channel.connect();

final double val = Double.valueOf(IOUtils.toString(channel.getInputStream()).trim());

while (!channel.isClosed()) { // wait for command execution to finish
Thread.sleep(10);
}

final double val = Double.valueOf(result.toString());
if (sampleDeltaValue) {
if (!Double.isNaN(oldValue)) {
collector.generateSample(val - oldValue, metricName);
Expand All @@ -73,31 +88,30 @@ public void generateSamples(MonitoringSampleGenerator collector) {
collector.generateSample(val, metricName);
}
}
catch (InterruptedIOException ex) { // stopping the test has caused a thread interrupt
log.info(ex.toString());
catch (JSchException ex) {
log.error("Channel failure for "+connectionDetails, ex);
}
catch (Exception ex) {
log.error(ex.toString());
catch (Exception ex) {
log.error("Sample failure for "+connectionDetails, ex);
}
finally {
if (channel != null && channel.isConnected()) {
if (channel != null) {
log.debug("Disconnecting channel for "+connectionDetails);
channel.disconnect();
}
if (session != null) {
try {
if (session.isConnected()) {
log.debug("Returning session for "+connectionDetails);
pool.returnObject(connectionDetails, session);
}
else {
log.debug("Invalidating session for "+connectionDetails);
pool.invalidateObject(connectionDetails, session);
}
try {
if (session != null && session.isConnected()) {
log.debug("Returning session for "+connectionDetails);
pool.returnObject(connectionDetails, session);
}
catch (Exception ex) {
log.warn(ex.getMessage());
else {
log.debug("Invalidating session for "+connectionDetails);
pool.invalidateObject(connectionDetails, session);
}
}
catch (Exception ex) {
log.warn("Failure returning session ", ex);
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,21 @@ public class SSHSessionFactory extends BaseKeyedPooledObjectFactory<ConnectionDe

private static final Logger log = LoggingManager.getLoggerForClass();

private final JSch jsch = new JSch();
private boolean hostKeyValidation = false;
private final JSch jsch;

public SSHSessionFactory() {
JSch.setLogger(new JSchLogger());
jsch = new JSch();

// Change default from "ask" to avoid interactive confirmation:
JSch.setConfig("StrictHostKeyChecking", "no");

String knownHosts = JMeterUtils.getProperty("jmeter.sshmon.knownHosts");
if (knownHosts != null && !knownHosts.isEmpty()) {
try {
log.debug("known hosts file set to "+knownHosts);
jsch.setKnownHosts(knownHosts);
hostKeyValidation = true;
JSch.setConfig("StrictHostKeyChecking", "yes");
}
catch (JSchException e) {
log.error("Failed to set known hosts ", e);
Expand All @@ -45,9 +50,7 @@ public Session create(ConnectionDetails connectionDetails) throws Exception {
}
session = jsch.getSession(connectionDetails.getUsername(), connectionDetails.getHost(), connectionDetails.getPort());
session.setPassword(connectionDetails.getPassword());
if (!hostKeyValidation) {
session.setConfig("StrictHostKeyChecking", "no");
}
session.setServerAliveCountMax(Integer.MAX_VALUE); // change from default value 1 to prevent disconnects
session.setDaemonThread(true);
session.connect();
} catch (Exception e) {
Expand All @@ -59,7 +62,7 @@ public Session create(ConnectionDetails connectionDetails) throws Exception {

@Override
public PooledObject<Session> wrap(Session session) {
return new DefaultPooledObject(session);
return new DefaultPooledObject<Session>(session);
}

@Override
Expand Down