Issue 632:enhance and automatically work around nodes with sftp problems

This commit is contained in:
Adrian Cole 2011-07-22 10:41:43 +10:00
parent b09c81177b
commit f3a0e6d0bd
3 changed files with 106 additions and 42 deletions

View File

@ -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);
}
}

View File

@ -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));

View File

@ -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;
} }