mirror of
https://github.com/apache/jclouds.git
synced 2025-02-17 15:35:44 +00:00
Issue 632:enhance and automatically work around nodes with sftp problems
This commit is contained in:
parent
b09c81177b
commit
f3a0e6d0bd
@ -0,0 +1,45 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright (C) 2011 Cloud Conscious, LLC. <info@cloudconscious.com>
|
||||||
|
*
|
||||||
|
* ====================================================================
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
* ====================================================================
|
||||||
|
*/
|
||||||
|
package org.jclouds.ssh;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Adrian Cole
|
||||||
|
*/
|
||||||
|
public class ChannelNotOpenException extends SshException {
|
||||||
|
|
||||||
|
/** The serialVersionUID */
|
||||||
|
private static final long serialVersionUID = 129347129837129837l;
|
||||||
|
|
||||||
|
public ChannelNotOpenException() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ChannelNotOpenException(String arg0, Throwable arg1) {
|
||||||
|
super(arg0, arg1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ChannelNotOpenException(String arg0) {
|
||||||
|
super(arg0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ChannelNotOpenException(Throwable arg0) {
|
||||||
|
super(arg0);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -45,6 +45,7 @@ import org.jclouds.io.Payload;
|
|||||||
import org.jclouds.io.Payloads;
|
import org.jclouds.io.Payloads;
|
||||||
import org.jclouds.logging.Logger;
|
import org.jclouds.logging.Logger;
|
||||||
import org.jclouds.net.IPSocket;
|
import org.jclouds.net.IPSocket;
|
||||||
|
import org.jclouds.ssh.ChannelNotOpenException;
|
||||||
import org.jclouds.ssh.SshClient;
|
import org.jclouds.ssh.SshClient;
|
||||||
import org.jclouds.ssh.SshException;
|
import org.jclouds.ssh.SshException;
|
||||||
import org.jclouds.util.Strings2;
|
import org.jclouds.util.Strings2;
|
||||||
@ -58,6 +59,7 @@ import com.google.inject.Inject;
|
|||||||
import com.jcraft.jsch.ChannelExec;
|
import com.jcraft.jsch.ChannelExec;
|
||||||
import com.jcraft.jsch.ChannelSftp;
|
import com.jcraft.jsch.ChannelSftp;
|
||||||
import com.jcraft.jsch.JSch;
|
import com.jcraft.jsch.JSch;
|
||||||
|
import com.jcraft.jsch.JSchException;
|
||||||
import com.jcraft.jsch.Session;
|
import com.jcraft.jsch.Session;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -114,7 +116,7 @@ public class JschSshClient implements SshClient {
|
|||||||
private final BackoffLimitedRetryHandler backoffLimitedRetryHandler;
|
private final BackoffLimitedRetryHandler backoffLimitedRetryHandler;
|
||||||
|
|
||||||
public JschSshClient(BackoffLimitedRetryHandler backoffLimitedRetryHandler, IPSocket socket, int timeout,
|
public JschSshClient(BackoffLimitedRetryHandler backoffLimitedRetryHandler, IPSocket socket, int timeout,
|
||||||
String username, String password, byte[] privateKey) {
|
String username, String password, byte[] privateKey) {
|
||||||
this.host = checkNotNull(socket, "socket").getAddress();
|
this.host = checkNotNull(socket, "socket").getAddress();
|
||||||
checkArgument(socket.getPort() > 0, "ssh port must be greater then zero" + socket.getPort());
|
checkArgument(socket.getPort() > 0, "ssh port must be greater then zero" + socket.getPort());
|
||||||
checkArgument(password != null || privateKey != null, "you must specify a password or a key");
|
checkArgument(password != null || privateKey != null, "you must specify a password or a key");
|
||||||
@ -222,10 +224,15 @@ public class JschSshClient implements SshClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelSftp create() throws Exception {
|
public ChannelSftp create() throws JSchException {
|
||||||
checkConnected();
|
checkConnected();
|
||||||
sftp = (ChannelSftp) session.openChannel("sftp");
|
String channel = "sftp";
|
||||||
sftp.connect();
|
sftp = (ChannelSftp) session.openChannel(channel);
|
||||||
|
try {
|
||||||
|
sftp.connect();
|
||||||
|
} catch (JSchException e) {
|
||||||
|
throwChannelNotOpenExceptionOrPropagate(channel, e);
|
||||||
|
}
|
||||||
return sftp;
|
return sftp;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -235,6 +242,11 @@ public class JschSshClient implements SshClient {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
public void throwChannelNotOpenExceptionOrPropagate(String channel, JSchException e) throws JSchException {
|
||||||
|
if (e.getMessage().indexOf("channel is not opened") != -1)
|
||||||
|
throw new ChannelNotOpenException(String.format("(%s) channel %s is not open", toString() , channel), e);
|
||||||
|
}
|
||||||
|
|
||||||
class GetConnection implements Connection<Payload> {
|
class GetConnection implements Connection<Payload> {
|
||||||
private final String path;
|
private final String path;
|
||||||
private ChannelSftp sftp;
|
private ChannelSftp sftp;
|
||||||
@ -285,7 +297,7 @@ public class JschSshClient implements SshClient {
|
|||||||
public Void create() throws Exception {
|
public Void create() throws Exception {
|
||||||
sftp = acquire(sftpConnection);
|
sftp = acquire(sftpConnection);
|
||||||
try {
|
try {
|
||||||
sftp.put(contents.getInput(), path);
|
sftp.put(checkNotNull(contents.getInput(), "inputstream for path %s", path), path);
|
||||||
} finally {
|
} finally {
|
||||||
Closeables.closeQuietly(contents);
|
Closeables.closeQuietly(contents);
|
||||||
clear();
|
clear();
|
||||||
@ -307,15 +319,17 @@ public class JschSshClient implements SshClient {
|
|||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
boolean shouldRetry(Exception from) {
|
boolean shouldRetry(Exception from) {
|
||||||
final String rootMessage = getRootCause(from).getMessage();
|
final String rootMessage = getRootCause(from).getMessage();
|
||||||
|
if (rootMessage == null)
|
||||||
|
return false;
|
||||||
return any(getCausalChain(from), retryPredicate)
|
return any(getCausalChain(from), retryPredicate)
|
||||||
|| Iterables.any(Splitter.on(",").split(retryableMessages), new Predicate<String>() {
|
|| Iterables.any(Splitter.on(",").split(retryableMessages), new Predicate<String>() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean apply(String input) {
|
public boolean apply(String input) {
|
||||||
return rootMessage.indexOf(input) != -1;
|
return rootMessage.indexOf(input) != -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void backoffForAttempt(int retryAttempt, String message) {
|
private void backoffForAttempt(int retryAttempt, String message) {
|
||||||
@ -325,7 +339,8 @@ public class JschSshClient implements SshClient {
|
|||||||
private SshException propagate(Exception e, String message) {
|
private SshException propagate(Exception e, String message) {
|
||||||
message += ": " + e.getMessage();
|
message += ": " + e.getMessage();
|
||||||
logger.error(e, "<< " + message);
|
logger.error(e, "<< " + message);
|
||||||
throw new SshException(message, e);
|
throw e instanceof SshException ? SshException.class.cast(e) : new SshException(
|
||||||
|
"(" + toString() + ") " + message, e);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -338,30 +353,42 @@ public class JschSshClient implements SshClient {
|
|||||||
sessionConnection.clear();
|
sessionConnection.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
Connection<ChannelExec> execConnection = new Connection<ChannelExec>() {
|
protected Connection<ChannelExec> execConnection(final String command) {
|
||||||
|
checkNotNull(command, "command");
|
||||||
|
return new Connection<ChannelExec>() {
|
||||||
|
|
||||||
private ChannelExec executor = null;
|
private ChannelExec executor = null;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void clear() {
|
public void clear() {
|
||||||
if (executor != null)
|
if (executor != null)
|
||||||
executor.disconnect();
|
executor.disconnect();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelExec create() throws Exception {
|
public ChannelExec create() throws Exception {
|
||||||
checkConnected();
|
checkConnected();
|
||||||
executor = (ChannelExec) session.openChannel("exec");
|
String channel = "exec";
|
||||||
executor.setPty(true);
|
executor = (ChannelExec) session.openChannel(channel);
|
||||||
return executor;
|
executor.setPty(true);
|
||||||
}
|
executor.setCommand(command);
|
||||||
|
ByteArrayOutputStream error = new ByteArrayOutputStream();
|
||||||
|
executor.setErrStream(error);
|
||||||
|
try {
|
||||||
|
executor.connect();
|
||||||
|
} catch (JSchException e) {
|
||||||
|
throwChannelNotOpenExceptionOrPropagate("exec", e);
|
||||||
|
}
|
||||||
|
return executor;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "ChannelExec(" + JschSshClient.this.toString() + ")";
|
return "ChannelExec(" + JschSshClient.this.toString() + ")";
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
};
|
}
|
||||||
|
|
||||||
class ExecConnection implements Connection<ExecResponse> {
|
class ExecConnection implements Connection<ExecResponse> {
|
||||||
private final String command;
|
private final String command;
|
||||||
@ -379,14 +406,10 @@ public class JschSshClient implements SshClient {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ExecResponse create() throws Exception {
|
public ExecResponse create() throws Exception {
|
||||||
executor = acquire(execConnection);
|
executor = acquire(execConnection(command));
|
||||||
executor.setCommand(command);
|
|
||||||
ByteArrayOutputStream error = new ByteArrayOutputStream();
|
|
||||||
executor.setErrStream(error);
|
|
||||||
try {
|
try {
|
||||||
executor.connect();
|
|
||||||
String outputString = Strings2.toStringAndClose(executor.getInputStream());
|
String outputString = Strings2.toStringAndClose(executor.getInputStream());
|
||||||
String errorString = error.toString();
|
String errorString = executor.getErrStream().toString();
|
||||||
int errorStatus = executor.getExitStatus();
|
int errorStatus = executor.getExitStatus();
|
||||||
int i = 0;
|
int i = 0;
|
||||||
String message = String.format("bad status -1 %s", toString());
|
String message = String.format("bad status -1 %s", toString());
|
||||||
@ -407,8 +430,7 @@ public class JschSshClient implements SshClient {
|
|||||||
public String toString() {
|
public String toString() {
|
||||||
return "ExecResponse(" + JschSshClient.this.toString() + ")[" + command + "]";
|
return "ExecResponse(" + JschSshClient.this.toString() + ")[" + command + "]";
|
||||||
}
|
}
|
||||||
|
}
|
||||||
};
|
|
||||||
|
|
||||||
public ExecResponse exec(String command) {
|
public ExecResponse exec(String command) {
|
||||||
return acquire(new ExecConnection(command));
|
return acquire(new ExecConnection(command));
|
||||||
|
@ -21,7 +21,6 @@ package org.jclouds.vcloud.terremark;
|
|||||||
import static org.jclouds.Constants.PROPERTY_API_VERSION;
|
import static org.jclouds.Constants.PROPERTY_API_VERSION;
|
||||||
import static org.jclouds.Constants.PROPERTY_ENDPOINT;
|
import static org.jclouds.Constants.PROPERTY_ENDPOINT;
|
||||||
import static org.jclouds.Constants.PROPERTY_ISO3166_CODES;
|
import static org.jclouds.Constants.PROPERTY_ISO3166_CODES;
|
||||||
import static org.jclouds.compute.callables.RunScriptOnNodeAsInitScriptUsingSsh.PROPERTY_PUSH_INIT_SCRIPT_VIA_SFTP;
|
|
||||||
import static org.jclouds.vcloud.reference.VCloudConstants.PROPERTY_VCLOUD_TIMEOUT_TASK_COMPLETED;
|
import static org.jclouds.vcloud.reference.VCloudConstants.PROPERTY_VCLOUD_TIMEOUT_TASK_COMPLETED;
|
||||||
import static org.jclouds.vcloud.terremark.reference.TerremarkConstants.PROPERTY_TERREMARK_EXTENSION_NAME;
|
import static org.jclouds.vcloud.terremark.reference.TerremarkConstants.PROPERTY_TERREMARK_EXTENSION_NAME;
|
||||||
import static org.jclouds.vcloud.terremark.reference.TerremarkConstants.PROPERTY_TERREMARK_EXTENSION_VERSION;
|
import static org.jclouds.vcloud.terremark.reference.TerremarkConstants.PROPERTY_TERREMARK_EXTENSION_VERSION;
|
||||||
@ -44,8 +43,6 @@ public class TerremarkECloudPropertiesBuilder extends TerremarkVCloudPropertiesB
|
|||||||
properties.setProperty(PROPERTY_TERREMARK_EXTENSION_VERSION, "2.8");
|
properties.setProperty(PROPERTY_TERREMARK_EXTENSION_VERSION, "2.8");
|
||||||
// default for ubuntu
|
// default for ubuntu
|
||||||
properties.setProperty(PROPERTY_VCLOUD_TIMEOUT_TASK_COMPLETED, 360l * 1000l + "");
|
properties.setProperty(PROPERTY_VCLOUD_TIMEOUT_TASK_COMPLETED, 360l * 1000l + "");
|
||||||
// ubuntu image has a problem with sftp
|
|
||||||
properties.setProperty(PROPERTY_PUSH_INIT_SCRIPT_VIA_SFTP, "false");
|
|
||||||
return properties;
|
return properties;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user