mirror of https://github.com/apache/jclouds.git
Merge remote-tracking branch 'upstream/master'
This commit is contained in:
commit
982022be3e
|
@ -123,7 +123,7 @@ public class ServerToNodeMetadata implements Function<Server, NodeMetadata> {
|
||||||
try {
|
try {
|
||||||
return Iterables.find(hardwares.get(), new FindHardwareForServer(from));
|
return Iterables.find(hardwares.get(), new FindHardwareForServer(from));
|
||||||
} catch (NoSuchElementException e) {
|
} catch (NoSuchElementException e) {
|
||||||
logger.warn("could not find a matching hardware for server %s", from);
|
logger.debug("could not find a matching hardware for server %s", from);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -132,7 +132,7 @@ public class ServerToNodeMetadata implements Function<Server, NodeMetadata> {
|
||||||
try {
|
try {
|
||||||
return Iterables.find(images.get(), new FindImageForServer(from)).getOperatingSystem();
|
return Iterables.find(images.get(), new FindImageForServer(from)).getOperatingSystem();
|
||||||
} catch (NoSuchElementException e) {
|
} catch (NoSuchElementException e) {
|
||||||
logger.warn("could not find a matching image for server %s in location %s", from, location.get());
|
logger.debug("could not find a matching image for server %s in location %s", from, location.get());
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -98,7 +98,7 @@ public class InstanceToNodeMetadata implements Function<Instance, NodeMetadata>
|
||||||
try {
|
try {
|
||||||
return Iterables.find(hardwares.get(), new FindHardwareForInstance(from));
|
return Iterables.find(hardwares.get(), new FindHardwareForInstance(from));
|
||||||
} catch (NoSuchElementException e) {
|
} catch (NoSuchElementException e) {
|
||||||
logger.warn("could not find a matching hardware for instance %s", from);
|
logger.debug("could not find a matching hardware for instance %s", from);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -107,7 +107,7 @@ public class InstanceToNodeMetadata implements Function<Instance, NodeMetadata>
|
||||||
try {
|
try {
|
||||||
return Iterables.find(images.get(), new FindImageForInstance(from)).getOperatingSystem();
|
return Iterables.find(images.get(), new FindImageForInstance(from)).getOperatingSystem();
|
||||||
} catch (NoSuchElementException e) {
|
} catch (NoSuchElementException e) {
|
||||||
logger.warn("could not find a matching image for instance %s", from);
|
logger.debug("could not find a matching image for instance %s", from);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -129,7 +129,7 @@ public class InstanceToNodeMetadata implements Function<Instance, NodeMetadata>
|
||||||
try {
|
try {
|
||||||
return Iterables.find(locations.get(), new FindLocationForInstance(from));
|
return Iterables.find(locations.get(), new FindLocationForInstance(from));
|
||||||
} catch (NoSuchElementException e) {
|
} catch (NoSuchElementException e) {
|
||||||
logger.warn("could not find a matching realm for instance %s", from);
|
logger.debug("could not find a matching realm for instance %s", from);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -126,7 +126,7 @@ public class ServerToNodeMetadata implements Function<Server, NodeMetadata> {
|
||||||
try {
|
try {
|
||||||
return Iterables.find(hardwares.get(), new FindHardwareForServer(from));
|
return Iterables.find(hardwares.get(), new FindHardwareForServer(from));
|
||||||
} catch (NoSuchElementException e) {
|
} catch (NoSuchElementException e) {
|
||||||
logger.warn("could not find a matching hardware for server %s", from);
|
logger.debug("could not find a matching hardware for server %s", from);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -135,7 +135,7 @@ public class ServerToNodeMetadata implements Function<Server, NodeMetadata> {
|
||||||
try {
|
try {
|
||||||
return Iterables.find(images.get(), new FindImageForServer(from));
|
return Iterables.find(images.get(), new FindImageForServer(from));
|
||||||
} catch (NoSuchElementException e) {
|
} catch (NoSuchElementException e) {
|
||||||
logger.warn("could not find a matching image for server %s in location %s", from, location.get());
|
logger.debug("could not find a matching image for server %s in location %s", from, location.get());
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,7 +39,7 @@ public interface SshClient {
|
||||||
*/
|
*/
|
||||||
@Deprecated
|
@Deprecated
|
||||||
SshClient create(IPSocket socket, Credentials credentials);
|
SshClient create(IPSocket socket, Credentials credentials);
|
||||||
|
|
||||||
SshClient create(IPSocket socket, LoginCredentials credentials);
|
SshClient create(IPSocket socket, LoginCredentials credentials);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -51,19 +51,23 @@ public interface SshClient {
|
||||||
void put(String path, Payload contents);
|
void put(String path, Payload contents);
|
||||||
|
|
||||||
Payload get(String path);
|
Payload get(String path);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Execute a process and block until it is complete
|
* Execute a process and block until it is complete
|
||||||
*
|
*
|
||||||
* @param command command line to invoke
|
* @param command
|
||||||
|
* command line to invoke
|
||||||
* @return output of the command
|
* @return output of the command
|
||||||
*/
|
*/
|
||||||
ExecResponse exec(String command);
|
ExecResponse exec(String command);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Execute a process and allow the user to interact with it
|
* Execute a process and allow the user to interact with it. Note that this will allow the
|
||||||
|
* session to exist indefinitely, and its connection is not closed when {@link #disconnect()} is
|
||||||
|
* called.
|
||||||
*
|
*
|
||||||
* @param command command line to invoke
|
* @param command
|
||||||
|
* command line to invoke
|
||||||
* @return reference to the running process
|
* @return reference to the running process
|
||||||
* @since 1.5.0
|
* @since 1.5.0
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -34,7 +34,6 @@ import java.io.Closeable;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.net.ConnectException;
|
import java.net.ConnectException;
|
||||||
import java.util.Arrays;
|
|
||||||
|
|
||||||
import javax.annotation.PreDestroy;
|
import javax.annotation.PreDestroy;
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
|
@ -44,6 +43,7 @@ import org.apache.commons.io.input.ProxyInputStream;
|
||||||
import org.apache.commons.io.output.ByteArrayOutputStream;
|
import org.apache.commons.io.output.ByteArrayOutputStream;
|
||||||
import org.jclouds.compute.domain.ExecChannel;
|
import org.jclouds.compute.domain.ExecChannel;
|
||||||
import org.jclouds.compute.domain.ExecResponse;
|
import org.jclouds.compute.domain.ExecResponse;
|
||||||
|
import org.jclouds.domain.LoginCredentials;
|
||||||
import org.jclouds.http.handlers.BackoffLimitedRetryHandler;
|
import org.jclouds.http.handlers.BackoffLimitedRetryHandler;
|
||||||
import org.jclouds.io.Payload;
|
import org.jclouds.io.Payload;
|
||||||
import org.jclouds.io.Payloads;
|
import org.jclouds.io.Payloads;
|
||||||
|
@ -52,7 +52,6 @@ import org.jclouds.net.IPSocket;
|
||||||
import org.jclouds.rest.AuthorizationException;
|
import org.jclouds.rest.AuthorizationException;
|
||||||
import org.jclouds.ssh.SshClient;
|
import org.jclouds.ssh.SshClient;
|
||||||
import org.jclouds.ssh.SshException;
|
import org.jclouds.ssh.SshException;
|
||||||
import org.jclouds.util.CredentialUtils;
|
|
||||||
import org.jclouds.util.Strings2;
|
import org.jclouds.util.Strings2;
|
||||||
|
|
||||||
import com.google.common.annotations.VisibleForTesting;
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
|
@ -61,10 +60,10 @@ import com.google.common.base.Predicates;
|
||||||
import com.google.common.base.Splitter;
|
import com.google.common.base.Splitter;
|
||||||
import com.google.common.base.Supplier;
|
import com.google.common.base.Supplier;
|
||||||
import com.google.common.io.Closeables;
|
import com.google.common.io.Closeables;
|
||||||
|
import com.google.common.net.HostAndPort;
|
||||||
import com.google.inject.Inject;
|
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.JSchException;
|
import com.jcraft.jsch.JSchException;
|
||||||
import com.jcraft.jsch.Session;
|
import com.jcraft.jsch.Session;
|
||||||
|
|
||||||
|
@ -91,11 +90,7 @@ public class JschSshClient implements SshClient {
|
||||||
sftp.disconnect();
|
sftp.disconnect();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private final String host;
|
|
||||||
private final int port;
|
|
||||||
private final String username;
|
|
||||||
private final String password;
|
|
||||||
private final String toString;
|
private final String toString;
|
||||||
|
|
||||||
@Inject(optional = true)
|
@Inject(optional = true)
|
||||||
|
@ -121,31 +116,31 @@ public class JschSshClient implements SshClient {
|
||||||
@Named("jclouds.ssh")
|
@Named("jclouds.ssh")
|
||||||
protected Logger logger = Logger.NULL;
|
protected Logger logger = Logger.NULL;
|
||||||
|
|
||||||
private Session session;
|
|
||||||
private final byte[] privateKey;
|
|
||||||
final byte[] emptyPassPhrase = new byte[0];
|
|
||||||
private final int timeout;
|
|
||||||
private final BackoffLimitedRetryHandler backoffLimitedRetryHandler;
|
private final BackoffLimitedRetryHandler backoffLimitedRetryHandler;
|
||||||
|
|
||||||
public JschSshClient(BackoffLimitedRetryHandler backoffLimitedRetryHandler, IPSocket socket, int timeout,
|
final SessionConnection sessionConnection;
|
||||||
String username, String password, byte[] privateKey) {
|
final String user;
|
||||||
|
final String host;
|
||||||
|
|
||||||
|
public JschSshClient(BackoffLimitedRetryHandler backoffLimitedRetryHandler, IPSocket socket,
|
||||||
|
LoginCredentials loginCredentials, int timeout) {
|
||||||
|
this.user = checkNotNull(loginCredentials, "loginCredentials").getUser();
|
||||||
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(loginCredentials.getPassword() != null || loginCredentials.getPrivateKey() != null,
|
||||||
this.port = socket.getPort();
|
"you must specify a password or a key");
|
||||||
this.username = checkNotNull(username, "username");
|
|
||||||
this.backoffLimitedRetryHandler = checkNotNull(backoffLimitedRetryHandler, "backoffLimitedRetryHandler");
|
this.backoffLimitedRetryHandler = checkNotNull(backoffLimitedRetryHandler, "backoffLimitedRetryHandler");
|
||||||
this.timeout = timeout;
|
if (loginCredentials.getPrivateKey() == null) {
|
||||||
this.password = password;
|
this.toString = String.format("%s:pw[%s]@%s:%d", loginCredentials.getUser(), hex(md5(loginCredentials
|
||||||
this.privateKey = privateKey;
|
.getPassword().getBytes())), host, socket.getPort());
|
||||||
if (privateKey == null) {
|
|
||||||
this.toString = String.format("%s:pw[%s]@%s:%d", username, hex(md5(password.getBytes())), host, port);
|
|
||||||
} else {
|
} else {
|
||||||
String fingerPrint = fingerprintPrivateKey(new String(privateKey));
|
String fingerPrint = fingerprintPrivateKey(loginCredentials.getPrivateKey());
|
||||||
String sha1 = sha1PrivateKey(new String(privateKey));
|
String sha1 = sha1PrivateKey(loginCredentials.getPrivateKey());
|
||||||
this.toString = String.format("%s:rsa[fingerprint(%s),sha1(%s)]@%s:%d", username, fingerPrint, sha1, host,
|
this.toString = String.format("%s:rsa[fingerprint(%s),sha1(%s)]@%s:%d", loginCredentials.getUser(),
|
||||||
port);
|
fingerPrint, sha1, host, socket.getPort());
|
||||||
}
|
}
|
||||||
|
sessionConnection = SessionConnection.builder().hostAndPort(HostAndPort.fromParts(host, socket.getPort())).loginCredentials(
|
||||||
|
loginCredentials).connectTimeout(timeout).sessionTimeout(timeout).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -154,7 +149,8 @@ public class JschSshClient implements SshClient {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkConnected() {
|
private void checkConnected() {
|
||||||
checkState(session != null && session.isConnected(), String.format("(%s) Session not connected!", toString()));
|
checkState(sessionConnection.getSession() != null && sessionConnection.getSession().isConnected(), String.format(
|
||||||
|
"(%s) Session not connected!", toString()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static interface Connection<T> {
|
public static interface Connection<T> {
|
||||||
|
@ -163,45 +159,6 @@ public class JschSshClient implements SshClient {
|
||||||
T create() throws Exception;
|
T create() throws Exception;
|
||||||
}
|
}
|
||||||
|
|
||||||
Connection<Session> sessionConnection = new Connection<Session>() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void clear() {
|
|
||||||
if (session != null && session.isConnected()) {
|
|
||||||
session.disconnect();
|
|
||||||
session = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Session create() throws Exception {
|
|
||||||
JSch jsch = new JSch();
|
|
||||||
session = jsch.getSession(username, host, port);
|
|
||||||
if (timeout != 0)
|
|
||||||
session.setTimeout(timeout);
|
|
||||||
if (password != null) {
|
|
||||||
session.setPassword(password);
|
|
||||||
} else {
|
|
||||||
// jsch wipes out your private key
|
|
||||||
if (CredentialUtils.isPrivateKeyEncrypted(privateKey)) {
|
|
||||||
throw new IllegalArgumentException(
|
|
||||||
"JschSshClientModule does not support private keys that require a passphrase");
|
|
||||||
}
|
|
||||||
jsch.addIdentity(username, Arrays.copyOf(privateKey, privateKey.length), null, emptyPassPhrase);
|
|
||||||
}
|
|
||||||
java.util.Properties config = new java.util.Properties();
|
|
||||||
config.put("StrictHostKeyChecking", "no");
|
|
||||||
session.setConfig(config);
|
|
||||||
session.connect(timeout);
|
|
||||||
return session;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return String.format("Session(timeout=%d)", timeout);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
protected <T, C extends Connection<T>> T acquire(C connection) {
|
protected <T, C extends Connection<T>> T acquire(C connection) {
|
||||||
connection.clear();
|
connection.clear();
|
||||||
String errorMessage = String.format("(%s) error acquiring %s", toString(), connection);
|
String errorMessage = String.format("(%s) error acquiring %s", toString(), connection);
|
||||||
|
@ -245,7 +202,7 @@ public class JschSshClient implements SshClient {
|
||||||
public ChannelSftp create() throws JSchException {
|
public ChannelSftp create() throws JSchException {
|
||||||
checkConnected();
|
checkConnected();
|
||||||
String channel = "sftp";
|
String channel = "sftp";
|
||||||
sftp = (ChannelSftp) session.openChannel(channel);
|
sftp = (ChannelSftp) sessionConnection.getSession().openChannel(channel);
|
||||||
sftp.connect();
|
sftp.connect();
|
||||||
return sftp;
|
return sftp;
|
||||||
}
|
}
|
||||||
|
@ -394,7 +351,7 @@ public class JschSshClient implements SshClient {
|
||||||
public ChannelExec create() throws Exception {
|
public ChannelExec create() throws Exception {
|
||||||
checkConnected();
|
checkConnected();
|
||||||
String channel = "exec";
|
String channel = "exec";
|
||||||
executor = (ChannelExec) session.openChannel(channel);
|
executor = (ChannelExec) sessionConnection.getSession().openChannel(channel);
|
||||||
executor.setPty(true);
|
executor.setPty(true);
|
||||||
executor.setCommand(command);
|
executor.setCommand(command);
|
||||||
ByteArrayOutputStream error = new ByteArrayOutputStream();
|
ByteArrayOutputStream error = new ByteArrayOutputStream();
|
||||||
|
@ -468,13 +425,13 @@ public class JschSshClient implements SshClient {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getUsername() {
|
public String getUsername() {
|
||||||
return this.username;
|
return this.user;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class ExecChannelConnection implements Connection<ExecChannel> {
|
class ExecChannelConnection implements Connection<ExecChannel> {
|
||||||
private final String command;
|
private final String command;
|
||||||
private ChannelExec executor = null;
|
private ChannelExec executor = null;
|
||||||
|
private Session sessionConnection;
|
||||||
|
|
||||||
ExecChannelConnection(String command) {
|
ExecChannelConnection(String command) {
|
||||||
this.command = checkNotNull(command, "command");
|
this.command = checkNotNull(command, "command");
|
||||||
|
@ -484,14 +441,16 @@ public class JschSshClient implements SshClient {
|
||||||
public void clear() {
|
public void clear() {
|
||||||
if (executor != null)
|
if (executor != null)
|
||||||
executor.disconnect();
|
executor.disconnect();
|
||||||
|
if (sessionConnection != null)
|
||||||
|
sessionConnection.disconnect();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ExecChannel create() throws Exception {
|
public ExecChannel create() throws Exception {
|
||||||
checkConnected();
|
this.sessionConnection = acquire(SessionConnection.builder().fromSessionConnection(
|
||||||
|
JschSshClient.this.sessionConnection).sessionTimeout(0).build());
|
||||||
String channel = "exec";
|
String channel = "exec";
|
||||||
executor = (ChannelExec) session.openChannel(channel);
|
executor = (ChannelExec) sessionConnection.openChannel(channel);
|
||||||
executor.setPty(true);
|
|
||||||
executor.setCommand(command);
|
executor.setCommand(command);
|
||||||
ByteArrayOutputStream error = new ByteArrayOutputStream();
|
ByteArrayOutputStream error = new ByteArrayOutputStream();
|
||||||
executor.setErrStream(error);
|
executor.setErrStream(error);
|
||||||
|
@ -521,7 +480,6 @@ public class JschSshClient implements SshClient {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ExecChannel execChannel(String command) {
|
public ExecChannel execChannel(String command) {
|
||||||
return acquire(new ExecChannelConnection(command));
|
return acquire(new ExecChannelConnection(command));
|
||||||
|
|
|
@ -0,0 +1,201 @@
|
||||||
|
/**
|
||||||
|
* Licensed to jclouds, Inc. (jclouds) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. jclouds licenses this file
|
||||||
|
* to you 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.jsch;
|
||||||
|
|
||||||
|
import static com.google.common.base.Objects.equal;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
import org.jclouds.domain.LoginCredentials;
|
||||||
|
import org.jclouds.ssh.jsch.JschSshClient.Connection;
|
||||||
|
import org.jclouds.util.CredentialUtils;
|
||||||
|
|
||||||
|
import com.google.common.base.Objects;
|
||||||
|
import com.google.common.net.HostAndPort;
|
||||||
|
import com.jcraft.jsch.JSch;
|
||||||
|
import com.jcraft.jsch.Session;
|
||||||
|
|
||||||
|
public class SessionConnection implements Connection<Session> {
|
||||||
|
public static Builder builder() {
|
||||||
|
return new Builder();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Builder {
|
||||||
|
|
||||||
|
protected HostAndPort hostAndPort;
|
||||||
|
protected LoginCredentials loginCredentials;
|
||||||
|
protected int connectTimeout;
|
||||||
|
protected int sessionTimeout;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see SessionConnection#getHostAndPort()
|
||||||
|
*/
|
||||||
|
public Builder hostAndPort(HostAndPort hostAndPort) {
|
||||||
|
this.hostAndPort = hostAndPort;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see SessionConnection#getLoginCredentials()
|
||||||
|
*/
|
||||||
|
public Builder loginCredentials(LoginCredentials loginCredentials) {
|
||||||
|
this.loginCredentials = loginCredentials;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see SessionConnection#getConnectTimeout()
|
||||||
|
*/
|
||||||
|
public Builder connectTimeout(int connectTimeout) {
|
||||||
|
this.connectTimeout = connectTimeout;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see SessionConnection#getConnectTimeout()
|
||||||
|
*/
|
||||||
|
public Builder sessionTimeout(int sessionTimeout) {
|
||||||
|
this.sessionTimeout = sessionTimeout;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SessionConnection build() {
|
||||||
|
return new SessionConnection(hostAndPort, loginCredentials, connectTimeout, sessionTimeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Builder fromSessionConnection(SessionConnection in) {
|
||||||
|
return hostAndPort(in.getHostAndPort()).connectTimeout(in.getConnectTimeout()).loginCredentials(
|
||||||
|
in.getLoginCredentials());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private SessionConnection(HostAndPort hostAndPort, LoginCredentials loginCredentials, int connectTimeout,
|
||||||
|
int sessionTimeout) {
|
||||||
|
this.hostAndPort = hostAndPort;
|
||||||
|
this.loginCredentials = loginCredentials;
|
||||||
|
this.connectTimeout = connectTimeout;
|
||||||
|
this.sessionTimeout = sessionTimeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final byte[] emptyPassPhrase = new byte[0];
|
||||||
|
|
||||||
|
private final HostAndPort hostAndPort;
|
||||||
|
private final LoginCredentials loginCredentials;
|
||||||
|
private final int connectTimeout;
|
||||||
|
private final int sessionTimeout;
|
||||||
|
|
||||||
|
private transient Session session;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clear() {
|
||||||
|
if (session != null && session.isConnected()) {
|
||||||
|
session.disconnect();
|
||||||
|
session = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Session create() throws Exception {
|
||||||
|
JSch jsch = new JSch();
|
||||||
|
session = jsch
|
||||||
|
.getSession(loginCredentials.getUser(), hostAndPort.getHostText(), hostAndPort.getPortOrDefault(22));
|
||||||
|
if (sessionTimeout != 0)
|
||||||
|
session.setTimeout(sessionTimeout);
|
||||||
|
if (loginCredentials.getPrivateKey() == null) {
|
||||||
|
session.setPassword(loginCredentials.getPassword());
|
||||||
|
} else {
|
||||||
|
byte[] privateKey = loginCredentials.getPrivateKey().getBytes();
|
||||||
|
if (CredentialUtils.isPrivateKeyEncrypted(privateKey)) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"JschSshClientModule does not support private keys that require a passphrase");
|
||||||
|
}
|
||||||
|
jsch.addIdentity(loginCredentials.getUser(), Arrays.copyOf(privateKey, privateKey.length), null,
|
||||||
|
emptyPassPhrase);
|
||||||
|
}
|
||||||
|
java.util.Properties config = new java.util.Properties();
|
||||||
|
config.put("StrictHostKeyChecking", "no");
|
||||||
|
session.setConfig(config);
|
||||||
|
session.connect(connectTimeout);
|
||||||
|
return session;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return host and port, where port if not present defaults to {@code 22}
|
||||||
|
*/
|
||||||
|
public HostAndPort getHostAndPort() {
|
||||||
|
return hostAndPort;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @return login used in this session
|
||||||
|
*/
|
||||||
|
public LoginCredentials getLoginCredentials() {
|
||||||
|
return loginCredentials;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @return how long to wait for the initial connection to be made
|
||||||
|
*/
|
||||||
|
public int getConnectTimeout() {
|
||||||
|
return connectTimeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @return how long to keep the session open, or {@code 0} for indefinitely
|
||||||
|
*/
|
||||||
|
public int getSessionTimeout() {
|
||||||
|
return sessionTimeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @return the current session or {@code null} if not connected
|
||||||
|
*/
|
||||||
|
public Session getSession() {
|
||||||
|
return session;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o)
|
||||||
|
return true;
|
||||||
|
if (o == null || getClass() != o.getClass())
|
||||||
|
return false;
|
||||||
|
SessionConnection that = SessionConnection.class.cast(o);
|
||||||
|
return equal(this.hostAndPort, that.hostAndPort) && equal(this.loginCredentials, that.loginCredentials)
|
||||||
|
&& equal(this.session, that.session);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hashCode(hostAndPort, loginCredentials, session);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return Objects.toStringHelper("").add("hostAndPort", hostAndPort).add("loginUser", loginCredentials.getUser())
|
||||||
|
.add("session", session != null ? session.hashCode() : null).add("connectTimeout", connectTimeout).add(
|
||||||
|
"sessionTimeout", sessionTimeout).toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -65,9 +65,7 @@ public class JschSshClientModule extends AbstractModule {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SshClient create(IPSocket socket, LoginCredentials credentials) {
|
public SshClient create(IPSocket socket, LoginCredentials credentials) {
|
||||||
SshClient client = new JschSshClient(backoffLimitedRetryHandler, socket, timeout, credentials.getUser(),
|
SshClient client = new JschSshClient(backoffLimitedRetryHandler, socket, credentials, timeout);
|
||||||
(credentials.getPrivateKey() == null) ? credentials.getPassword() : null,
|
|
||||||
credentials.getPrivateKey() != null ? credentials.getPrivateKey().getBytes() : null);
|
|
||||||
injector.injectMembers(client);// add logger
|
injector.injectMembers(client);// add logger
|
||||||
return client;
|
return client;
|
||||||
}
|
}
|
||||||
|
|
|
@ -170,8 +170,10 @@ public class JschSshClientLiveTest {
|
||||||
: sshHost);
|
: sshHost);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testExecChannelTakesStdinAndEchosBack() throws IOException {
|
public void testExecChannelTakesStdinAndNoEchoOfCharsInOuputAndOutlivesClient() throws IOException {
|
||||||
ExecChannel response = setupClient().execChannel("cat <<EOF");
|
SshClient client = setupClient();
|
||||||
|
ExecChannel response = client.execChannel("cat <<EOF");
|
||||||
|
client.disconnect();
|
||||||
assertEquals(response.getExitStatus().get(), null);
|
assertEquals(response.getExitStatus().get(), null);
|
||||||
try {
|
try {
|
||||||
PrintStream printStream = new PrintStream(response.getInput());
|
PrintStream printStream = new PrintStream(response.getInput());
|
||||||
|
@ -179,12 +181,10 @@ public class JschSshClientLiveTest {
|
||||||
printStream.append("EOF\n");
|
printStream.append("EOF\n");
|
||||||
printStream.close();
|
printStream.close();
|
||||||
assertEquals(Strings2.toStringAndClose(response.getError()), "");
|
assertEquals(Strings2.toStringAndClose(response.getError()), "");
|
||||||
// local echo
|
assertEquals(Strings2.toStringAndClose(response.getOutput()), "");
|
||||||
assertEquals(Strings2.toStringAndClose(response.getOutput()), "foo\r\nEOF\r\n");
|
|
||||||
} finally {
|
} finally {
|
||||||
Closeables.closeQuietly(response);
|
Closeables.closeQuietly(response);
|
||||||
}
|
}
|
||||||
assertEquals(response.getExitStatus().get(), new Integer(0));
|
assertEquals(response.getExitStatus().get(), new Integer(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -0,0 +1,209 @@
|
||||||
|
/**
|
||||||
|
* Licensed to jclouds, Inc. (jclouds) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. jclouds licenses this file
|
||||||
|
* to you 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.sshj;
|
||||||
|
|
||||||
|
import static com.google.common.base.Objects.equal;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import javax.annotation.Resource;
|
||||||
|
import javax.inject.Named;
|
||||||
|
|
||||||
|
import net.schmizz.sshj.SSHClient;
|
||||||
|
import net.schmizz.sshj.transport.verification.PromiscuousVerifier;
|
||||||
|
import net.schmizz.sshj.userauth.keyprovider.OpenSSHKeyFile;
|
||||||
|
|
||||||
|
import org.jclouds.domain.LoginCredentials;
|
||||||
|
import org.jclouds.logging.Logger;
|
||||||
|
import org.jclouds.sshj.SshjSshClient.Connection;
|
||||||
|
|
||||||
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
|
import com.google.common.base.Objects;
|
||||||
|
import com.google.common.net.HostAndPort;
|
||||||
|
|
||||||
|
public class SSHClientConnection implements Connection<SSHClient> {
|
||||||
|
public static Builder builder() {
|
||||||
|
return new Builder();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Builder {
|
||||||
|
|
||||||
|
protected HostAndPort hostAndPort;
|
||||||
|
protected LoginCredentials loginCredentials;
|
||||||
|
protected int connectTimeout;
|
||||||
|
protected int sessionTimeout;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see SSHClientConnection#getHostAndPort()
|
||||||
|
*/
|
||||||
|
public Builder hostAndPort(HostAndPort hostAndPort) {
|
||||||
|
this.hostAndPort = hostAndPort;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see SSHClientConnection#getLoginCredentials()
|
||||||
|
*/
|
||||||
|
public Builder loginCredentials(LoginCredentials loginCredentials) {
|
||||||
|
this.loginCredentials = loginCredentials;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see SSHClientConnection#getConnectTimeout()
|
||||||
|
*/
|
||||||
|
public Builder connectTimeout(int connectTimeout) {
|
||||||
|
this.connectTimeout = connectTimeout;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see SSHClientConnection#getConnectTimeout()
|
||||||
|
*/
|
||||||
|
public Builder sessionTimeout(int sessionTimeout) {
|
||||||
|
this.sessionTimeout = sessionTimeout;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SSHClientConnection build() {
|
||||||
|
return new SSHClientConnection(hostAndPort, loginCredentials, connectTimeout, sessionTimeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Builder fromSSHClientConnection(SSHClientConnection in) {
|
||||||
|
return hostAndPort(in.getHostAndPort()).connectTimeout(in.getConnectTimeout()).loginCredentials(
|
||||||
|
in.getLoginCredentials());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private SSHClientConnection(HostAndPort hostAndPort, LoginCredentials loginCredentials, int connectTimeout,
|
||||||
|
int sessionTimeout) {
|
||||||
|
this.hostAndPort = hostAndPort;
|
||||||
|
this.loginCredentials = loginCredentials;
|
||||||
|
this.connectTimeout = connectTimeout;
|
||||||
|
this.sessionTimeout = sessionTimeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
@Named("jclouds.ssh")
|
||||||
|
protected Logger logger = Logger.NULL;
|
||||||
|
|
||||||
|
private final HostAndPort hostAndPort;
|
||||||
|
private final LoginCredentials loginCredentials;
|
||||||
|
private final int connectTimeout;
|
||||||
|
private final int sessionTimeout;
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
transient SSHClient ssh;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clear() {
|
||||||
|
if (ssh != null && ssh.isConnected()) {
|
||||||
|
try {
|
||||||
|
ssh.disconnect();
|
||||||
|
} catch (IOException e) {
|
||||||
|
logger.debug("<< exception disconnecting from %s: %s", e, e.getMessage());
|
||||||
|
}
|
||||||
|
ssh = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SSHClient create() throws Exception {
|
||||||
|
ssh = new net.schmizz.sshj.SSHClient();
|
||||||
|
ssh.addHostKeyVerifier(new PromiscuousVerifier());
|
||||||
|
if (connectTimeout != 0) {
|
||||||
|
ssh.setConnectTimeout(connectTimeout);
|
||||||
|
}
|
||||||
|
if (sessionTimeout != 0) {
|
||||||
|
ssh.setTimeout(sessionTimeout);
|
||||||
|
}
|
||||||
|
ssh.connect(hostAndPort.getHostText(), hostAndPort.getPortOrDefault(22));
|
||||||
|
if (loginCredentials.getPassword() != null) {
|
||||||
|
ssh.authPassword(loginCredentials.getUser(), loginCredentials.getPassword());
|
||||||
|
} else {
|
||||||
|
OpenSSHKeyFile key = new OpenSSHKeyFile();
|
||||||
|
key.init(loginCredentials.getPrivateKey(), null);
|
||||||
|
ssh.authPublickey(loginCredentials.getUser(), key);
|
||||||
|
}
|
||||||
|
return ssh;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return host and port, where port if not present defaults to {@code 22}
|
||||||
|
*/
|
||||||
|
public HostAndPort getHostAndPort() {
|
||||||
|
return hostAndPort;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @return login used in this ssh
|
||||||
|
*/
|
||||||
|
public LoginCredentials getLoginCredentials() {
|
||||||
|
return loginCredentials;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @return how long to wait for the initial connection to be made
|
||||||
|
*/
|
||||||
|
public int getConnectTimeout() {
|
||||||
|
return connectTimeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @return how long to keep the ssh open, or {@code 0} for indefinitely
|
||||||
|
*/
|
||||||
|
public int getSessionTimeout() {
|
||||||
|
return sessionTimeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @return the current ssh or {@code null} if not connected
|
||||||
|
*/
|
||||||
|
public SSHClient getSSHClient() {
|
||||||
|
return ssh;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o)
|
||||||
|
return true;
|
||||||
|
if (o == null || getClass() != o.getClass())
|
||||||
|
return false;
|
||||||
|
SSHClientConnection that = SSHClientConnection.class.cast(o);
|
||||||
|
return equal(this.hostAndPort, that.hostAndPort) && equal(this.loginCredentials, that.loginCredentials)
|
||||||
|
&& equal(this.ssh, that.ssh);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hashCode(hostAndPort, loginCredentials, ssh);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return Objects.toStringHelper("").add("hostAndPort", hostAndPort).add("loginUser", loginCredentials.getUser())
|
||||||
|
.add("ssh", ssh != null ? ssh.hashCode() : null).add("connectTimeout", connectTimeout).add(
|
||||||
|
"sessionTimeout", sessionTimeout).toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -52,14 +52,13 @@ import net.schmizz.sshj.connection.channel.direct.Session.Command;
|
||||||
import net.schmizz.sshj.sftp.SFTPClient;
|
import net.schmizz.sshj.sftp.SFTPClient;
|
||||||
import net.schmizz.sshj.sftp.SFTPException;
|
import net.schmizz.sshj.sftp.SFTPException;
|
||||||
import net.schmizz.sshj.transport.TransportException;
|
import net.schmizz.sshj.transport.TransportException;
|
||||||
import net.schmizz.sshj.transport.verification.PromiscuousVerifier;
|
|
||||||
import net.schmizz.sshj.userauth.UserAuthException;
|
import net.schmizz.sshj.userauth.UserAuthException;
|
||||||
import net.schmizz.sshj.userauth.keyprovider.OpenSSHKeyFile;
|
|
||||||
import net.schmizz.sshj.xfer.InMemorySourceFile;
|
import net.schmizz.sshj.xfer.InMemorySourceFile;
|
||||||
|
|
||||||
import org.apache.commons.io.input.ProxyInputStream;
|
import org.apache.commons.io.input.ProxyInputStream;
|
||||||
import org.jclouds.compute.domain.ExecChannel;
|
import org.jclouds.compute.domain.ExecChannel;
|
||||||
import org.jclouds.compute.domain.ExecResponse;
|
import org.jclouds.compute.domain.ExecResponse;
|
||||||
|
import org.jclouds.domain.LoginCredentials;
|
||||||
import org.jclouds.http.handlers.BackoffLimitedRetryHandler;
|
import org.jclouds.http.handlers.BackoffLimitedRetryHandler;
|
||||||
import org.jclouds.io.Payload;
|
import org.jclouds.io.Payload;
|
||||||
import org.jclouds.io.Payloads;
|
import org.jclouds.io.Payloads;
|
||||||
|
@ -77,6 +76,7 @@ import com.google.common.base.Splitter;
|
||||||
import com.google.common.base.Supplier;
|
import com.google.common.base.Supplier;
|
||||||
import com.google.common.base.Throwables;
|
import com.google.common.base.Throwables;
|
||||||
import com.google.common.io.Closeables;
|
import com.google.common.io.Closeables;
|
||||||
|
import com.google.common.net.HostAndPort;
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -104,10 +104,6 @@ public class SshjSshClient implements SshClient {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private final String host;
|
|
||||||
private final int port;
|
|
||||||
private final String username;
|
|
||||||
private final String password;
|
|
||||||
private final String toString;
|
private final String toString;
|
||||||
|
|
||||||
@Inject(optional = true)
|
@Inject(optional = true)
|
||||||
|
@ -129,41 +125,42 @@ public class SshjSshClient implements SshClient {
|
||||||
@Named("jclouds.ssh.retry-predicate")
|
@Named("jclouds.ssh.retry-predicate")
|
||||||
// NOTE cannot retry io exceptions, as SSHException is a part of the chain
|
// NOTE cannot retry io exceptions, as SSHException is a part of the chain
|
||||||
private Predicate<Throwable> retryPredicate = or(instanceOf(ConnectionException.class),
|
private Predicate<Throwable> retryPredicate = or(instanceOf(ConnectionException.class),
|
||||||
instanceOf(ConnectException.class), instanceOf(SocketTimeoutException.class),
|
instanceOf(ConnectException.class), instanceOf(SocketTimeoutException.class),
|
||||||
instanceOf(TransportException.class),
|
instanceOf(TransportException.class),
|
||||||
// safe to retry sftp exceptions as they are idempotent
|
// safe to retry sftp exceptions as they are idempotent
|
||||||
instanceOf(SFTPException.class));
|
instanceOf(SFTPException.class));
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
@Named("jclouds.ssh")
|
@Named("jclouds.ssh")
|
||||||
protected Logger logger = Logger.NULL;
|
protected Logger logger = Logger.NULL;
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
SSHClient ssh;
|
SSHClientConnection sshClientConnection;
|
||||||
private final byte[] privateKey;
|
|
||||||
final byte[] emptyPassPhrase = new byte[0];
|
final String user;
|
||||||
private final int timeoutMillis;
|
final String host;
|
||||||
|
|
||||||
private final BackoffLimitedRetryHandler backoffLimitedRetryHandler;
|
private final BackoffLimitedRetryHandler backoffLimitedRetryHandler;
|
||||||
|
|
||||||
public SshjSshClient(BackoffLimitedRetryHandler backoffLimitedRetryHandler, IPSocket socket, int timeout,
|
public SshjSshClient(BackoffLimitedRetryHandler backoffLimitedRetryHandler, IPSocket socket,
|
||||||
String username, String password, byte[] privateKey) {
|
LoginCredentials loginCredentials, int timeout) {
|
||||||
|
this.user = checkNotNull(loginCredentials, "loginCredentials").getUser();
|
||||||
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(loginCredentials.getPassword() != null || loginCredentials.getPrivateKey() != null,
|
||||||
this.port = socket.getPort();
|
"you must specify a password or a key");
|
||||||
this.username = checkNotNull(username, "username");
|
|
||||||
this.backoffLimitedRetryHandler = checkNotNull(backoffLimitedRetryHandler, "backoffLimitedRetryHandler");
|
this.backoffLimitedRetryHandler = checkNotNull(backoffLimitedRetryHandler, "backoffLimitedRetryHandler");
|
||||||
this.timeoutMillis = timeout;
|
if (loginCredentials.getPrivateKey() == null) {
|
||||||
this.password = password;
|
this.toString = String.format("%s:pw[%s]@%s:%d", loginCredentials.getUser(), hex(md5(loginCredentials
|
||||||
this.privateKey = privateKey;
|
.getPassword().getBytes())), host, socket.getPort());
|
||||||
if (privateKey == null) {
|
|
||||||
this.toString = String.format("%s:pw[%s]@%s:%d", username, hex(md5(password.getBytes())), host, port);
|
|
||||||
} else {
|
} else {
|
||||||
String fingerPrint = fingerprintPrivateKey(new String(privateKey));
|
String fingerPrint = fingerprintPrivateKey(loginCredentials.getPrivateKey());
|
||||||
String sha1 = sha1PrivateKey(new String(privateKey));
|
String sha1 = sha1PrivateKey(loginCredentials.getPrivateKey());
|
||||||
this.toString = String.format("%s:rsa[fingerprint(%s),sha1(%s)]@%s:%d", username, fingerPrint, sha1, host,
|
this.toString = String.format("%s:rsa[fingerprint(%s),sha1(%s)]@%s:%d", loginCredentials.getUser(),
|
||||||
port);
|
fingerPrint, sha1, host, socket.getPort());
|
||||||
}
|
}
|
||||||
|
sshClientConnection = SSHClientConnection.builder().hostAndPort(HostAndPort.fromParts(host, socket.getPort()))
|
||||||
|
.loginCredentials(loginCredentials).connectTimeout(timeout).sessionTimeout(timeout).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -172,7 +169,8 @@ public class SshjSshClient implements SshClient {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkConnected() {
|
private void checkConnected() {
|
||||||
checkState(ssh != null && ssh.isConnected(), String.format("(%s) ssh not connected!", toString()));
|
checkState(sshClientConnection.ssh != null && sshClientConnection.ssh.isConnected(), String
|
||||||
|
.format("(%s) ssh not connected!", toString()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static interface Connection<T> {
|
public static interface Connection<T> {
|
||||||
|
@ -181,45 +179,6 @@ public class SshjSshClient implements SshClient {
|
||||||
T create() throws Exception;
|
T create() throws Exception;
|
||||||
}
|
}
|
||||||
|
|
||||||
Connection<net.schmizz.sshj.SSHClient> sshConnection = new Connection<net.schmizz.sshj.SSHClient>() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void clear() {
|
|
||||||
if (ssh != null && ssh.isConnected()) {
|
|
||||||
try {
|
|
||||||
ssh.disconnect();
|
|
||||||
} catch (IOException e) {
|
|
||||||
logger.warn(e, "<< exception disconnecting from %s: %s", e, e.getMessage());
|
|
||||||
}
|
|
||||||
ssh = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public net.schmizz.sshj.SSHClient create() throws Exception {
|
|
||||||
net.schmizz.sshj.SSHClient ssh = new net.schmizz.sshj.SSHClient();
|
|
||||||
ssh.addHostKeyVerifier(new PromiscuousVerifier());
|
|
||||||
if (timeoutMillis != 0) {
|
|
||||||
ssh.setTimeout(timeoutMillis);
|
|
||||||
ssh.setConnectTimeout(timeoutMillis);
|
|
||||||
}
|
|
||||||
ssh.connect(host, port);
|
|
||||||
if (password != null) {
|
|
||||||
ssh.authPassword(username, password);
|
|
||||||
} else {
|
|
||||||
OpenSSHKeyFile key = new OpenSSHKeyFile();
|
|
||||||
key.init(new String(privateKey), null);
|
|
||||||
ssh.authPublickey(username, key);
|
|
||||||
}
|
|
||||||
return ssh;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return String.format("SSHClient(timeout=%d)", timeoutMillis);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
private void backoffForAttempt(int retryAttempt, String message) {
|
private void backoffForAttempt(int retryAttempt, String message) {
|
||||||
backoffLimitedRetryHandler.imposeBackoffExponentialDelay(200L, 2, retryAttempt, sshRetries, message);
|
backoffLimitedRetryHandler.imposeBackoffExponentialDelay(200L, 2, retryAttempt, sshRetries, message);
|
||||||
}
|
}
|
||||||
|
@ -240,16 +199,17 @@ public class SshjSshClient implements SshClient {
|
||||||
logger.warn(from, "<< (%s) error closing connection", toString());
|
logger.warn(from, "<< (%s) error closing connection", toString());
|
||||||
}
|
}
|
||||||
if (i + 1 == sshRetries) {
|
if (i + 1 == sshRetries) {
|
||||||
throw propagate(from, errorMessage+" (out of retries - max "+sshRetries+")");
|
throw propagate(from, errorMessage + " (out of retries - max " + sshRetries + ")");
|
||||||
} else if (shouldRetry(from) ||
|
} else if (shouldRetry(from)
|
||||||
(Throwables2.getFirstThrowableOfType(from, IllegalStateException.class) != null)) {
|
|| (Throwables2.getFirstThrowableOfType(from, IllegalStateException.class) != null)) {
|
||||||
logger.info("<< " + errorMessage + " (attempt " + (i + 1) + " of " + sshRetries + "): " + from.getMessage());
|
logger.info("<< " + errorMessage + " (attempt " + (i + 1) + " of " + sshRetries + "): "
|
||||||
|
+ from.getMessage());
|
||||||
backoffForAttempt(i + 1, errorMessage + ": " + from.getMessage());
|
backoffForAttempt(i + 1, errorMessage + ": " + from.getMessage());
|
||||||
if (connection != sshConnection)
|
if (connection != sshClientConnection)
|
||||||
connect();
|
connect();
|
||||||
continue;
|
continue;
|
||||||
} else {
|
} else {
|
||||||
throw propagate(from, errorMessage+" (not retryable)");
|
throw propagate(from, errorMessage + " (not retryable)");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -259,7 +219,7 @@ public class SshjSshClient implements SshClient {
|
||||||
|
|
||||||
public void connect() {
|
public void connect() {
|
||||||
try {
|
try {
|
||||||
ssh = acquire(sshConnection);
|
acquire(sshClientConnection);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Throwables.propagate(e);
|
Throwables.propagate(e);
|
||||||
}
|
}
|
||||||
|
@ -282,7 +242,7 @@ public class SshjSshClient implements SshClient {
|
||||||
@Override
|
@Override
|
||||||
public SFTPClient create() throws IOException {
|
public SFTPClient create() throws IOException {
|
||||||
checkConnected();
|
checkConnected();
|
||||||
sftp = ssh.newSFTPClient();
|
sftp = sshClientConnection.ssh.newSFTPClient();
|
||||||
return sftp;
|
return sftp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -310,7 +270,7 @@ public class SshjSshClient implements SshClient {
|
||||||
public Payload create() throws Exception {
|
public Payload create() throws Exception {
|
||||||
sftp = acquire(sftpConnection);
|
sftp = acquire(sftpConnection);
|
||||||
return Payloads.newInputStreamPayload(new CloseFtpChannelOnCloseInputStream(sftp.getSFTPEngine().open(path)
|
return Payloads.newInputStreamPayload(new CloseFtpChannelOnCloseInputStream(sftp.getSFTPEngine().open(path)
|
||||||
.getInputStream(), sftp));
|
.getInputStream(), sftp));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -385,7 +345,7 @@ public class SshjSshClient implements SshClient {
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
boolean shouldRetry(Exception from) {
|
boolean shouldRetry(Exception from) {
|
||||||
Predicate<Throwable> predicate = retryAuth ? Predicates.<Throwable> or(retryPredicate,
|
Predicate<Throwable> predicate = retryAuth ? Predicates.<Throwable> or(retryPredicate,
|
||||||
instanceOf(AuthorizationException.class), instanceOf(UserAuthException.class)) : retryPredicate;
|
instanceOf(AuthorizationException.class), instanceOf(UserAuthException.class)) : retryPredicate;
|
||||||
if (any(getCausalChain(from), predicate))
|
if (any(getCausalChain(from), predicate))
|
||||||
return true;
|
return true;
|
||||||
if (!retryableMessages.equals(""))
|
if (!retryableMessages.equals(""))
|
||||||
|
@ -404,7 +364,7 @@ public class SshjSshClient implements SshClient {
|
||||||
@Override
|
@Override
|
||||||
public boolean apply(Throwable arg0) {
|
public boolean apply(Throwable arg0) {
|
||||||
return (arg0.toString().indexOf(input) != -1)
|
return (arg0.toString().indexOf(input) != -1)
|
||||||
|| (arg0.getMessage() != null && arg0.getMessage().indexOf(input) != -1);
|
|| (arg0.getMessage() != null && arg0.getMessage().indexOf(input) != -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -420,7 +380,7 @@ public class SshjSshClient implements SshClient {
|
||||||
if (e instanceof UserAuthException)
|
if (e instanceof UserAuthException)
|
||||||
throw new AuthorizationException("(" + toString() + ") " + message, e);
|
throw new AuthorizationException("(" + toString() + ") " + message, e);
|
||||||
throw e instanceof SshException ? SshException.class.cast(e) : new SshException(
|
throw e instanceof SshException ? SshException.class.cast(e) : new SshException(
|
||||||
"(" + toString() + ") " + message, e);
|
"(" + toString() + ") " + message, e);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -431,7 +391,7 @@ public class SshjSshClient implements SshClient {
|
||||||
@PreDestroy
|
@PreDestroy
|
||||||
public void disconnect() {
|
public void disconnect() {
|
||||||
try {
|
try {
|
||||||
sshConnection.clear();
|
sshClientConnection.clear();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Throwables.propagate(e);
|
Throwables.propagate(e);
|
||||||
}
|
}
|
||||||
|
@ -452,8 +412,8 @@ public class SshjSshClient implements SshClient {
|
||||||
@Override
|
@Override
|
||||||
public Session create() throws Exception {
|
public Session create() throws Exception {
|
||||||
checkConnected();
|
checkConnected();
|
||||||
session = ssh.startSession();
|
session = sshClientConnection.ssh.startSession();
|
||||||
session.allocatePTY("vt100", 80, 24, 0, 0, Collections.<PTYMode, Integer>emptyMap());
|
session.allocatePTY("vt100", 80, 24, 0, 0, Collections.<PTYMode, Integer> emptyMap());
|
||||||
return session;
|
return session;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -485,7 +445,7 @@ public class SshjSshClient implements SshClient {
|
||||||
session = acquire(execConnection());
|
session = acquire(execConnection());
|
||||||
Command output = session.exec(checkNotNull(command, "command"));
|
Command output = session.exec(checkNotNull(command, "command"));
|
||||||
String outputString = IOUtils.readFully(output.getInputStream()).toString();
|
String outputString = IOUtils.readFully(output.getInputStream()).toString();
|
||||||
output.join(timeoutMillis, TimeUnit.SECONDS);
|
output.join(sshClientConnection.getSessionTimeout(), TimeUnit.MILLISECONDS);
|
||||||
int errorStatus = output.getExitStatus();
|
int errorStatus = output.getExitStatus();
|
||||||
String errorString = IOUtils.readFully(output.getErrorStream()).toString();
|
String errorString = IOUtils.readFully(output.getErrorStream()).toString();
|
||||||
return new ExecResponse(outputString, errorString, errorStatus);
|
return new ExecResponse(outputString, errorString, errorStatus);
|
||||||
|
@ -504,6 +464,37 @@ public class SshjSshClient implements SshClient {
|
||||||
return acquire(new ExecConnection(command));
|
return acquire(new ExecConnection(command));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected Connection<Session> noPTYConnection() {
|
||||||
|
|
||||||
|
return new Connection<Session>() {
|
||||||
|
|
||||||
|
private Session session = null;
|
||||||
|
private SSHClient sshClientConnection;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clear() throws TransportException, ConnectionException {
|
||||||
|
if (session != null)
|
||||||
|
session.close();
|
||||||
|
if (sshClientConnection != null)
|
||||||
|
Closeables.closeQuietly(sshClientConnection);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Session create() throws Exception {
|
||||||
|
this.sshClientConnection = acquire(SSHClientConnection.builder().fromSSHClientConnection(
|
||||||
|
SshjSshClient.this.sshClientConnection).sessionTimeout(0).build());
|
||||||
|
session = sshClientConnection.startSession();
|
||||||
|
return session;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Session()";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
class ExecChannelConnection implements Connection<ExecChannel> {
|
class ExecChannelConnection implements Connection<ExecChannel> {
|
||||||
private final String command;
|
private final String command;
|
||||||
private SessionChannel session;
|
private SessionChannel session;
|
||||||
|
@ -521,7 +512,7 @@ public class SshjSshClient implements SshClient {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ExecChannel create() throws Exception {
|
public ExecChannel create() throws Exception {
|
||||||
session = SessionChannel.class.cast(acquire(execConnection()));
|
session = SessionChannel.class.cast(acquire(noPTYConnection()));
|
||||||
output = session.exec(command);
|
output = session.exec(command);
|
||||||
return new ExecChannel(output.getOutputStream(), output.getInputStream(), output.getErrorStream(),
|
return new ExecChannel(output.getOutputStream(), output.getInputStream(), output.getErrorStream(),
|
||||||
new Supplier<Integer>() {
|
new Supplier<Integer>() {
|
||||||
|
@ -552,7 +543,7 @@ public class SshjSshClient implements SshClient {
|
||||||
public ExecChannel execChannel(String command) {
|
public ExecChannel execChannel(String command) {
|
||||||
return acquire(new ExecChannelConnection(command));
|
return acquire(new ExecChannelConnection(command));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getHostAddress() {
|
public String getHostAddress() {
|
||||||
return this.host;
|
return this.host;
|
||||||
|
@ -560,7 +551,7 @@ public class SshjSshClient implements SshClient {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getUsername() {
|
public String getUsername() {
|
||||||
return this.username;
|
return this.user;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -65,9 +65,7 @@ public class SshjSshClientModule extends AbstractModule {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SshClient create(IPSocket socket, LoginCredentials credentials) {
|
public SshClient create(IPSocket socket, LoginCredentials credentials) {
|
||||||
SshClient client = new SshjSshClient(backoffLimitedRetryHandler, socket, timeout, credentials.getUser(),
|
SshClient client = new SshjSshClient(backoffLimitedRetryHandler, socket, credentials, timeout);
|
||||||
(credentials.getPrivateKey() == null) ? credentials.getPassword() : null,
|
|
||||||
credentials.getPrivateKey() != null ? credentials.getPrivateKey().getBytes() : null);
|
|
||||||
injector.injectMembers(client);// add logger
|
injector.injectMembers(client);// add logger
|
||||||
return client;
|
return client;
|
||||||
}
|
}
|
||||||
|
|
|
@ -169,9 +169,11 @@ public class SshjSshClientLiveTest {
|
||||||
assertEquals(response.getOutput().trim(), "localhost".equals(sshHost) ? InetAddress.getLocalHost().getHostName()
|
assertEquals(response.getOutput().trim(), "localhost".equals(sshHost) ? InetAddress.getLocalHost().getHostName()
|
||||||
: sshHost);
|
: sshHost);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testExecChannelTakesStdinAndEchosBack() throws IOException {
|
public void testExecChannelTakesStdinAndNoEchoOfCharsInOuputAndOutlivesClient() throws IOException {
|
||||||
ExecChannel response = setupClient().execChannel("cat <<EOF");
|
SshClient client = setupClient();
|
||||||
|
ExecChannel response = client.execChannel("cat <<EOF");
|
||||||
|
client.disconnect();
|
||||||
assertEquals(response.getExitStatus().get(), null);
|
assertEquals(response.getExitStatus().get(), null);
|
||||||
try {
|
try {
|
||||||
PrintStream printStream = new PrintStream(response.getInput());
|
PrintStream printStream = new PrintStream(response.getInput());
|
||||||
|
@ -179,8 +181,7 @@ public class SshjSshClientLiveTest {
|
||||||
printStream.append("EOF\n");
|
printStream.append("EOF\n");
|
||||||
printStream.close();
|
printStream.close();
|
||||||
assertEquals(Strings2.toStringAndClose(response.getError()), "");
|
assertEquals(Strings2.toStringAndClose(response.getError()), "");
|
||||||
// local echo
|
assertEquals(Strings2.toStringAndClose(response.getOutput()), "");
|
||||||
assertEquals(Strings2.toStringAndClose(response.getOutput()), "foo\r\nEOF\r\n");
|
|
||||||
} finally {
|
} finally {
|
||||||
Closeables.closeQuietly(response);
|
Closeables.closeQuietly(response);
|
||||||
}
|
}
|
||||||
|
|
|
@ -172,8 +172,8 @@ public class SshjSshClientTest {
|
||||||
ssh.disconnect();
|
ssh.disconnect();
|
||||||
expectLastCall().andThrow(new ConnectionException("disconnected"));
|
expectLastCall().andThrow(new ConnectionException("disconnected"));
|
||||||
replay(ssh);
|
replay(ssh);
|
||||||
ssh1.ssh = ssh;
|
ssh1.sshClientConnection.ssh = ssh;
|
||||||
ssh1.sshConnection.clear();
|
ssh1.sshClientConnection.clear();
|
||||||
verify(ssh);
|
verify(ssh);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -186,10 +186,9 @@ public class SshjSshClientTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testRetriesLoggedAtInfoWithCount() throws Exception {
|
public void testRetriesLoggedAtInfoWithCount() throws Exception {
|
||||||
@SuppressWarnings("unchecked")
|
SSHClientConnection mockConnection = createMock(SSHClientConnection.class);
|
||||||
SshjSshClient.Connection<net.schmizz.sshj.SSHClient> mockConnection = createMock(SshjSshClient.Connection.class);
|
|
||||||
net.schmizz.sshj.SSHClient mockClient = createMock(net.schmizz.sshj.SSHClient.class);
|
net.schmizz.sshj.SSHClient mockClient = createMock(net.schmizz.sshj.SSHClient.class);
|
||||||
|
|
||||||
mockConnection.clear(); expectLastCall();
|
mockConnection.clear(); expectLastCall();
|
||||||
mockConnection.create(); expectLastCall().andThrow(new ConnectionException("test1"));
|
mockConnection.create(); expectLastCall().andThrow(new ConnectionException("test1"));
|
||||||
mockConnection.clear(); expectLastCall();
|
mockConnection.clear(); expectLastCall();
|
||||||
|
@ -199,14 +198,14 @@ public class SshjSshClientTest {
|
||||||
replay(mockConnection);
|
replay(mockConnection);
|
||||||
replay(mockClient);
|
replay(mockClient);
|
||||||
|
|
||||||
ssh.sshConnection = mockConnection;
|
ssh.sshClientConnection = mockConnection;
|
||||||
BufferLogger logcheck = new BufferLogger(ssh.getClass().getCanonicalName());
|
BufferLogger logcheck = new BufferLogger(ssh.getClass().getCanonicalName());
|
||||||
ssh.logger = logcheck;
|
ssh.logger = logcheck;
|
||||||
logcheck.setLevel(Level.INFO);
|
logcheck.setLevel(Level.INFO);
|
||||||
|
|
||||||
ssh.connect();
|
ssh.connect();
|
||||||
|
|
||||||
Assert.assertEquals(ssh.ssh, mockClient);
|
Assert.assertEquals(ssh.sshClientConnection, mockConnection);
|
||||||
verify(mockConnection);
|
verify(mockConnection);
|
||||||
verify(mockClient);
|
verify(mockClient);
|
||||||
Record r = logcheck.assertLogContains("attempt 1 of 5");
|
Record r = logcheck.assertLogContains("attempt 1 of 5");
|
||||||
|
|
|
@ -127,7 +127,7 @@ public class ServerDetailsToNodeMetadata implements Function<ServerDetails, Node
|
||||||
try {
|
try {
|
||||||
return Iterables.find(images.get(), new FindImageForServer(from)).getOperatingSystem();
|
return Iterables.find(images.get(), new FindImageForServer(from)).getOperatingSystem();
|
||||||
} catch (NoSuchElementException e) {
|
} catch (NoSuchElementException e) {
|
||||||
logger.warn("could not find a matching image for server %s", from);
|
logger.debug("could not find a matching image for server %s", from);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,13 +19,22 @@
|
||||||
|
|
||||||
package org.jclouds.virtualbox;
|
package org.jclouds.virtualbox;
|
||||||
|
|
||||||
|
import java.net.URI;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
|
|
||||||
|
import org.jclouds.byon.Node;
|
||||||
|
import org.jclouds.byon.config.CacheNodeStoreModule;
|
||||||
import org.jclouds.compute.StandaloneComputeServiceContextBuilder;
|
import org.jclouds.compute.StandaloneComputeServiceContextBuilder;
|
||||||
|
import org.jclouds.compute.domain.OsFamily;
|
||||||
|
import org.jclouds.concurrent.MoreExecutors;
|
||||||
|
import org.jclouds.concurrent.config.ExecutorServiceModule;
|
||||||
import org.jclouds.virtualbox.config.VirtualBoxComputeServiceContextModule;
|
import org.jclouds.virtualbox.config.VirtualBoxComputeServiceContextModule;
|
||||||
|
|
||||||
|
import com.google.common.base.Predicate;
|
||||||
import com.google.common.base.Supplier;
|
import com.google.common.base.Supplier;
|
||||||
|
import com.google.common.collect.ImmutableMap;
|
||||||
|
import com.google.common.collect.Iterables;
|
||||||
import com.google.inject.Module;
|
import com.google.inject.Module;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -37,12 +46,31 @@ import com.google.inject.Module;
|
||||||
public class VirtualBoxContextBuilder extends StandaloneComputeServiceContextBuilder<Supplier> {
|
public class VirtualBoxContextBuilder extends StandaloneComputeServiceContextBuilder<Supplier> {
|
||||||
|
|
||||||
public VirtualBoxContextBuilder(Properties properties) {
|
public VirtualBoxContextBuilder(Properties properties) {
|
||||||
super(Supplier.class, properties);
|
super(Supplier.class, new VirtualBoxPropertiesBuilder(properties).defaultProperties());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void addContextModule(List<Module> modules) {
|
protected void addContextModule(List<Module> modules) {
|
||||||
modules.add(new VirtualBoxComputeServiceContextModule());
|
modules.add(new VirtualBoxComputeServiceContextModule());
|
||||||
|
addHostModuleIfNotPresent(modules);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void addHostModuleIfNotPresent(List<Module> modules) {
|
||||||
|
if (!Iterables.any(modules, new Predicate<Module>() {
|
||||||
|
public boolean apply(Module input) {
|
||||||
|
return input instanceof CacheNodeStoreModule;
|
||||||
|
}
|
||||||
|
})) {
|
||||||
|
CacheNodeStoreModule hostModule = new CacheNodeStoreModule(ImmutableMap.of(
|
||||||
|
"host",
|
||||||
|
Node.builder().id("host").name("host installing virtualbox").hostname("localhost")
|
||||||
|
.osFamily(OsFamily.LINUX.toString()).osDescription(System.getProperty("os.name"))
|
||||||
|
.osVersion(System.getProperty("os.version")).group("ssh")
|
||||||
|
.username(System.getProperty("user.name"))
|
||||||
|
.credentialUrl(URI.create("file://" + System.getProperty("user.home") + "/.ssh/id_rsa"))
|
||||||
|
.build()));
|
||||||
|
modules.add(hostModule);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,27 +20,34 @@
|
||||||
package org.jclouds.virtualbox.compute;
|
package org.jclouds.virtualbox.compute;
|
||||||
|
|
||||||
import static com.google.common.base.Preconditions.checkNotNull;
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
import static com.google.common.base.Preconditions.checkState;
|
||||||
import static com.google.common.collect.Iterables.filter;
|
import static com.google.common.collect.Iterables.filter;
|
||||||
import static org.jclouds.virtualbox.config.VirtualBoxConstants.VIRTUALBOX_IMAGE_PREFIX;
|
import static org.jclouds.virtualbox.config.VirtualBoxConstants.VIRTUALBOX_IMAGE_PREFIX;
|
||||||
import static org.jclouds.virtualbox.config.VirtualBoxConstants.VIRTUALBOX_NODE_PREFIX;
|
import static org.jclouds.virtualbox.config.VirtualBoxConstants.VIRTUALBOX_NODE_PREFIX;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import javax.annotation.Resource;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Named;
|
||||||
|
|
||||||
import org.jclouds.compute.ComputeServiceAdapter;
|
import org.jclouds.compute.ComputeServiceAdapter;
|
||||||
import org.jclouds.compute.domain.Image;
|
import org.jclouds.compute.domain.Image;
|
||||||
import org.jclouds.compute.domain.Template;
|
import org.jclouds.compute.domain.Template;
|
||||||
|
import org.jclouds.compute.reference.ComputeServiceConstants;
|
||||||
import org.jclouds.domain.Location;
|
import org.jclouds.domain.Location;
|
||||||
import org.jclouds.javax.annotation.Nullable;
|
import org.jclouds.javax.annotation.Nullable;
|
||||||
|
import org.jclouds.logging.Logger;
|
||||||
import org.jclouds.virtualbox.domain.Master;
|
import org.jclouds.virtualbox.domain.Master;
|
||||||
import org.jclouds.virtualbox.domain.NodeSpec;
|
import org.jclouds.virtualbox.domain.NodeSpec;
|
||||||
import org.jclouds.virtualbox.domain.YamlImage;
|
import org.jclouds.virtualbox.domain.YamlImage;
|
||||||
import org.virtualbox_4_1.CleanupMode;
|
import org.jclouds.virtualbox.functions.admin.UnregisterMachineIfExistsAndForceDeleteItsMedia;
|
||||||
import org.virtualbox_4_1.IMachine;
|
import org.virtualbox_4_1.IMachine;
|
||||||
import org.virtualbox_4_1.IProgress;
|
import org.virtualbox_4_1.IProgress;
|
||||||
import org.virtualbox_4_1.ISession;
|
import org.virtualbox_4_1.ISession;
|
||||||
|
import org.virtualbox_4_1.MachineState;
|
||||||
import org.virtualbox_4_1.SessionState;
|
import org.virtualbox_4_1.SessionState;
|
||||||
|
import org.virtualbox_4_1.VBoxException;
|
||||||
import org.virtualbox_4_1.VirtualBoxManager;
|
import org.virtualbox_4_1.VirtualBoxManager;
|
||||||
|
|
||||||
import com.google.common.base.Function;
|
import com.google.common.base.Function;
|
||||||
|
@ -61,6 +68,10 @@ import com.google.inject.Singleton;
|
||||||
@Singleton
|
@Singleton
|
||||||
public class VirtualBoxComputeServiceAdapter implements ComputeServiceAdapter<IMachine, IMachine, Image, Location> {
|
public class VirtualBoxComputeServiceAdapter implements ComputeServiceAdapter<IMachine, IMachine, Image, Location> {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
@Named(ComputeServiceConstants.COMPUTE_LOGGER)
|
||||||
|
protected Logger logger = Logger.NULL;
|
||||||
|
|
||||||
private final Supplier<VirtualBoxManager> manager;
|
private final Supplier<VirtualBoxManager> manager;
|
||||||
private final Map<Image, YamlImage> images;
|
private final Map<Image, YamlImage> images;
|
||||||
private final LoadingCache<Image, Master> mastersLoader;
|
private final LoadingCache<Image, Master> mastersLoader;
|
||||||
|
@ -81,6 +92,7 @@ public class VirtualBoxComputeServiceAdapter implements ComputeServiceAdapter<IM
|
||||||
Template template) {
|
Template template) {
|
||||||
try {
|
try {
|
||||||
Master master = mastersLoader.get(template.getImage());
|
Master master = mastersLoader.get(template.getImage());
|
||||||
|
checkState(master != null, "could not find a master for image: "+template.getClass());
|
||||||
NodeSpec nodeSpec = NodeSpec.builder().master(master).name(name).tag(tag).template(template).build();
|
NodeSpec nodeSpec = NodeSpec.builder().master(master).name(name).tag(tag).template(template).build();
|
||||||
return cloneCreator.apply(nodeSpec);
|
return cloneCreator.apply(nodeSpec);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
@ -93,7 +105,7 @@ public class VirtualBoxComputeServiceAdapter implements ComputeServiceAdapter<IM
|
||||||
return Iterables.filter(manager.get().getVBox().getMachines(), new Predicate<IMachine>() {
|
return Iterables.filter(manager.get().getVBox().getMachines(), new Predicate<IMachine>() {
|
||||||
@Override
|
@Override
|
||||||
public boolean apply(IMachine arg0) {
|
public boolean apply(IMachine arg0) {
|
||||||
return !arg0.getName().startsWith(VIRTUALBOX_NODE_PREFIX);
|
return arg0.getName().startsWith(VIRTUALBOX_NODE_PREFIX);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -127,14 +139,21 @@ public class VirtualBoxComputeServiceAdapter implements ComputeServiceAdapter<IM
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IMachine getNode(String vmName) {
|
public IMachine getNode(String vmName) {
|
||||||
return manager.get().getVBox().findMachine(vmName);
|
try {
|
||||||
|
return manager.get().getVBox().findMachine(vmName);
|
||||||
|
} catch (VBoxException e) {
|
||||||
|
if (e.getMessage().contains("Could not find a registered machine named")){
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
throw Throwables.propagate(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void destroyNode(String vmName) {
|
public void destroyNode(String vmName) {
|
||||||
IMachine machine = manager.get().getVBox().findMachine(vmName);
|
IMachine machine = manager.get().getVBox().findMachine(vmName);
|
||||||
powerDownMachine(machine);
|
powerDownMachine(machine);
|
||||||
machine.unregister(CleanupMode.Full);
|
new UnregisterMachineIfExistsAndForceDeleteItsMedia().apply(machine);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -178,6 +197,11 @@ public class VirtualBoxComputeServiceAdapter implements ComputeServiceAdapter<IM
|
||||||
|
|
||||||
private void powerDownMachine(IMachine machine) {
|
private void powerDownMachine(IMachine machine) {
|
||||||
try {
|
try {
|
||||||
|
if (machine.getState() == MachineState.PoweredOff){
|
||||||
|
logger.debug("vm was already powered down: ", machine.getName());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
logger.debug("powering down vm: ", machine.getName());
|
||||||
ISession machineSession = manager.get().openMachineSession(machine);
|
ISession machineSession = manager.get().openMachineSession(machine);
|
||||||
IProgress progress = machineSession.getConsole().powerDown();
|
IProgress progress = machineSession.getConsole().powerDown();
|
||||||
progress.waitForCompletion(-1);
|
progress.waitForCompletion(-1);
|
||||||
|
@ -185,7 +209,7 @@ public class VirtualBoxComputeServiceAdapter implements ComputeServiceAdapter<IM
|
||||||
|
|
||||||
while (!machine.getSessionState().equals(SessionState.Unlocked)) {
|
while (!machine.getSessionState().equals(SessionState.Unlocked)) {
|
||||||
try {
|
try {
|
||||||
System.out.println("waiting for unlocking session - session state: " + machine.getSessionState());
|
logger.info("waiting for unlocking session - session state: " + machine.getSessionState());
|
||||||
Thread.sleep(1000);
|
Thread.sleep(1000);
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,6 +49,7 @@ import org.jclouds.compute.domain.NodeState;
|
||||||
import org.jclouds.compute.domain.OsFamily;
|
import org.jclouds.compute.domain.OsFamily;
|
||||||
import org.jclouds.compute.domain.TemplateBuilder;
|
import org.jclouds.compute.domain.TemplateBuilder;
|
||||||
import org.jclouds.compute.reference.ComputeServiceConstants.Timeouts;
|
import org.jclouds.compute.reference.ComputeServiceConstants.Timeouts;
|
||||||
|
import org.jclouds.concurrent.SingleThreaded;
|
||||||
import org.jclouds.domain.Location;
|
import org.jclouds.domain.Location;
|
||||||
import org.jclouds.functions.IdentityFunction;
|
import org.jclouds.functions.IdentityFunction;
|
||||||
import org.jclouds.logging.slf4j.config.SLF4JLoggingModule;
|
import org.jclouds.logging.slf4j.config.SLF4JLoggingModule;
|
||||||
|
@ -104,6 +105,7 @@ import com.google.inject.TypeLiteral;
|
||||||
* @author Mattias Holmqvist, Andrea Turli
|
* @author Mattias Holmqvist, Andrea Turli
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||||
|
@SingleThreaded
|
||||||
public class VirtualBoxComputeServiceContextModule extends
|
public class VirtualBoxComputeServiceContextModule extends
|
||||||
ComputeServiceAdapterContextModule<Supplier, Supplier, IMachine, IMachine, Image, Location> {
|
ComputeServiceAdapterContextModule<Supplier, Supplier, IMachine, IMachine, Image, Location> {
|
||||||
|
|
||||||
|
@ -176,16 +178,8 @@ public class VirtualBoxComputeServiceContextModule extends
|
||||||
String provider = "byon";
|
String provider = "byon";
|
||||||
String identity = "";
|
String identity = "";
|
||||||
String credential = "";
|
String credential = "";
|
||||||
CacheNodeStoreModule hostModule = new CacheNodeStoreModule(ImmutableMap.of(
|
|
||||||
"host",
|
|
||||||
Node.builder().id("host").name("host installing virtualbox").hostname("localhost")
|
|
||||||
.osFamily(OsFamily.LINUX.toString()).osDescription(System.getProperty("os.name"))
|
|
||||||
.osVersion(System.getProperty("os.version")).group("ssh")
|
|
||||||
.username(System.getProperty("user.name"))
|
|
||||||
.credentialUrl(URI.create("file://" + System.getProperty("user.home") + "/.ssh/id_rsa"))
|
|
||||||
.build()));
|
|
||||||
return new ComputeServiceContextFactory().createContext(provider, identity, credential,
|
return new ComputeServiceContextFactory().createContext(provider, identity, credential,
|
||||||
ImmutableSet.<Module> of(new SLF4JLoggingModule(), new SshjSshClientModule(), hostModule));
|
ImmutableSet.<Module> of(new SLF4JLoggingModule(), new SshjSshClientModule()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
|
|
|
@ -24,6 +24,8 @@ import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
import org.virtualbox_4_1.NATProtocol;
|
import org.virtualbox_4_1.NATProtocol;
|
||||||
import org.virtualbox_4_1.NetworkAttachmentType;
|
import org.virtualbox_4_1.NetworkAttachmentType;
|
||||||
|
|
||||||
|
@ -41,13 +43,16 @@ public class NetworkAdapter {
|
||||||
private final NetworkAttachmentType networkAttachmentType;
|
private final NetworkAttachmentType networkAttachmentType;
|
||||||
private final String macAddress;
|
private final String macAddress;
|
||||||
private final Set<RedirectRule> redirectRules;
|
private final Set<RedirectRule> redirectRules;
|
||||||
|
private final String staticIp;
|
||||||
|
|
||||||
public NetworkAdapter(NetworkAttachmentType networkAttachmentType,
|
public NetworkAdapter(NetworkAttachmentType networkAttachmentType,
|
||||||
String macAddress, Set<RedirectRule> redirectRules) {
|
String macAddress, Set<RedirectRule> redirectRules,
|
||||||
|
String staticIp) {
|
||||||
this.networkAttachmentType = checkNotNull(networkAttachmentType,
|
this.networkAttachmentType = checkNotNull(networkAttachmentType,
|
||||||
"networkAttachmentType");
|
"networkAttachmentType");
|
||||||
this.macAddress = macAddress;
|
this.macAddress = macAddress;
|
||||||
this.redirectRules = ImmutableSet.<RedirectRule>copyOf(redirectRules);
|
this.redirectRules = ImmutableSet.<RedirectRule>copyOf(redirectRules);
|
||||||
|
this.staticIp = staticIp;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Builder builder() {
|
public static Builder builder() {
|
||||||
|
@ -59,6 +64,7 @@ public class NetworkAdapter {
|
||||||
private NetworkAttachmentType networkAttachmentType;
|
private NetworkAttachmentType networkAttachmentType;
|
||||||
private String macAddress;
|
private String macAddress;
|
||||||
private Set<RedirectRule> redirectRules = Sets.newLinkedHashSet();
|
private Set<RedirectRule> redirectRules = Sets.newLinkedHashSet();
|
||||||
|
private String staticIp;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
|
@ -112,10 +118,15 @@ public class NetworkAdapter {
|
||||||
guest, guestPort));
|
guest, guestPort));
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Builder staticIp(@Nullable String staticIp) {
|
||||||
|
this.staticIp = staticIp;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public NetworkAdapter build() {
|
public NetworkAdapter build() {
|
||||||
return new NetworkAdapter(networkAttachmentType, macAddress,
|
return new NetworkAdapter(networkAttachmentType, macAddress,
|
||||||
redirectRules);
|
redirectRules,staticIp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -130,6 +141,10 @@ public class NetworkAdapter {
|
||||||
public String getMacAddress() {
|
public String getMacAddress() {
|
||||||
return macAddress;
|
return macAddress;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getStaticIp() {
|
||||||
|
return staticIp;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object o) {
|
public boolean equals(Object o) {
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
*/
|
*/
|
||||||
package org.jclouds.virtualbox.domain;
|
package org.jclouds.virtualbox.domain;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkArgument;
|
||||||
import static com.google.common.base.Preconditions.checkNotNull;
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
|
||||||
import com.google.common.base.Objects;
|
import com.google.common.base.Objects;
|
||||||
|
@ -41,11 +42,12 @@ public class NetworkInterfaceCard {
|
||||||
|
|
||||||
public static class Builder {
|
public static class Builder {
|
||||||
|
|
||||||
private long slot;
|
private long slot = 0L;
|
||||||
private NetworkAdapter networkAdapter;
|
private NetworkAdapter networkAdapter;
|
||||||
private String hostInterfaceName;
|
private String hostInterfaceName;
|
||||||
|
|
||||||
public Builder slot(long slot) {
|
public Builder slot(long slot) {
|
||||||
|
checkArgument(slot >= 0 && slot < 4, "must be 0, 1, 2, 3: %s", slot);
|
||||||
this.slot = slot;
|
this.slot = slot;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,6 @@
|
||||||
*/
|
*/
|
||||||
package org.jclouds.virtualbox.domain;
|
package org.jclouds.virtualbox.domain;
|
||||||
|
|
||||||
import static com.google.common.base.Preconditions.checkArgument;
|
|
||||||
import static com.google.common.base.Preconditions.checkNotNull;
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
@ -47,10 +46,8 @@ public class NetworkSpec {
|
||||||
|
|
||||||
private List<NetworkInterfaceCard> networkInterfaceCards = new ArrayList<NetworkInterfaceCard>();
|
private List<NetworkInterfaceCard> networkInterfaceCards = new ArrayList<NetworkInterfaceCard>();
|
||||||
|
|
||||||
public Builder addNIC(long slot, NetworkInterfaceCard networkInterfaceCard) {
|
public Builder addNIC(NetworkInterfaceCard networkInterfaceCard) {
|
||||||
checkArgument(slot >= 0 && slot < 4, "must be 0, 1, 2, 3: %s", slot);
|
this.networkInterfaceCards.add(networkInterfaceCard);
|
||||||
NetworkInterfaceCard nic = NetworkInterfaceCard.builder().slot(slot).addNetworkAdapter(networkInterfaceCard.getNetworkAdapter()).build();
|
|
||||||
this.networkInterfaceCards.add(nic);
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,55 @@
|
||||||
|
/**
|
||||||
|
* Licensed to jclouds, Inc. (jclouds) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. jclouds licenses this file
|
||||||
|
* to you 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.virtualbox.functions;
|
||||||
|
|
||||||
|
import static org.virtualbox_4_1.NetworkAdapterType.Am79C973;
|
||||||
|
import static org.virtualbox_4_1.NetworkAttachmentType.HostOnly;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
import org.jclouds.virtualbox.domain.NetworkInterfaceCard;
|
||||||
|
import org.virtualbox_4_1.IMachine;
|
||||||
|
import org.virtualbox_4_1.INetworkAdapter;
|
||||||
|
|
||||||
|
import com.google.common.base.Function;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author dralves
|
||||||
|
*/
|
||||||
|
public class AttachHostOnlyAdapter implements Function<IMachine, Void> {
|
||||||
|
|
||||||
|
private NetworkInterfaceCard networkInterfaceCard;
|
||||||
|
|
||||||
|
public AttachHostOnlyAdapter(NetworkInterfaceCard networkInterfaceCard) {
|
||||||
|
this.networkInterfaceCard = networkInterfaceCard;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Void apply(@Nullable IMachine machine) {
|
||||||
|
INetworkAdapter iNetworkAdapter = machine.getNetworkAdapter(networkInterfaceCard.getSlot());
|
||||||
|
iNetworkAdapter.setAttachmentType(HostOnly);
|
||||||
|
iNetworkAdapter.setAdapterType(Am79C973);
|
||||||
|
iNetworkAdapter.setMACAddress(networkInterfaceCard.getNetworkAdapter().getMacAddress());
|
||||||
|
iNetworkAdapter.setHostOnlyInterface(networkInterfaceCard.getHostInterfaceName());
|
||||||
|
iNetworkAdapter.setEnabled(true);
|
||||||
|
machine.saveSettings();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -49,6 +49,8 @@ public class AttachNicToMachine implements Function<NetworkInterfaceCard, Void>
|
||||||
|
|
||||||
} else if (hasBridgedAdapter(nic)) {
|
} else if (hasBridgedAdapter(nic)) {
|
||||||
return machineUtils.writeLockMachineAndApply(vmName, new AttachBridgedAdapterToMachine(nic));
|
return machineUtils.writeLockMachineAndApply(vmName, new AttachBridgedAdapterToMachine(nic));
|
||||||
|
} else if (hasHostOnlyAdapter(nic)) {
|
||||||
|
return machineUtils.writeLockMachineAndApply(vmName, new AttachHostOnlyAdapter(nic));
|
||||||
} else
|
} else
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -62,4 +64,8 @@ public class AttachNicToMachine implements Function<NetworkInterfaceCard, Void>
|
||||||
return nic.getNetworkAdapter().getNetworkAttachmentType()
|
return nic.getNetworkAdapter().getNetworkAttachmentType()
|
||||||
.equals(NetworkAttachmentType.Bridged);
|
.equals(NetworkAttachmentType.Bridged);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean hasHostOnlyAdapter(NetworkInterfaceCard nic) {
|
||||||
|
return nic.getNetworkAdapter().getNetworkAttachmentType().equals(NetworkAttachmentType.HostOnly);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,6 +37,7 @@ import javax.annotation.PostConstruct;
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.inject.Named;
|
import javax.inject.Named;
|
||||||
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
import org.jclouds.Constants;
|
import org.jclouds.Constants;
|
||||||
import org.jclouds.compute.domain.Image;
|
import org.jclouds.compute.domain.Image;
|
||||||
|
@ -72,7 +73,11 @@ import com.google.common.collect.Maps;
|
||||||
* @author dralves
|
* @author dralves
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
@Singleton
|
||||||
public class MastersCache extends AbstractLoadingCache<Image, Master> {
|
public class MastersCache extends AbstractLoadingCache<Image, Master> {
|
||||||
|
|
||||||
|
// TODO parameterize
|
||||||
|
public static final int MASTER_PORT = 2222;
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
@Named(ComputeServiceConstants.COMPUTE_LOGGER)
|
@Named(ComputeServiceConstants.COMPUTE_LOGGER)
|
||||||
|
@ -121,7 +126,7 @@ public class MastersCache extends AbstractLoadingCache<Image, Master> {
|
||||||
public synchronized Master get(Image key) throws ExecutionException {
|
public synchronized Master get(Image key) throws ExecutionException {
|
||||||
// check if we have loaded this machine before
|
// check if we have loaded this machine before
|
||||||
if (masters.containsKey(key.getId())) {
|
if (masters.containsKey(key.getId())) {
|
||||||
return masters.get(key);
|
return masters.get(key.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
String guestAdditionsFileName = String.format("VBoxGuestAdditions_%s.iso", version);
|
String guestAdditionsFileName = String.format("VBoxGuestAdditions_%s.iso", version);
|
||||||
|
@ -154,12 +159,12 @@ public class MastersCache extends AbstractLoadingCache<Image, Master> {
|
||||||
.controller(ideController).forceOverwrite(true).cleanUpMode(CleanupMode.Full).build();
|
.controller(ideController).forceOverwrite(true).cleanUpMode(CleanupMode.Full).build();
|
||||||
|
|
||||||
NetworkAdapter networkAdapter = NetworkAdapter.builder().networkAttachmentType(NetworkAttachmentType.NAT)
|
NetworkAdapter networkAdapter = NetworkAdapter.builder().networkAttachmentType(NetworkAttachmentType.NAT)
|
||||||
.tcpRedirectRule("127.0.0.1", 2222, "", 22).build();
|
.tcpRedirectRule("127.0.0.1", MASTER_PORT, "", 22).build();
|
||||||
|
|
||||||
NetworkInterfaceCard networkInterfaceCard = NetworkInterfaceCard.builder().addNetworkAdapter(networkAdapter)
|
NetworkInterfaceCard networkInterfaceCard = NetworkInterfaceCard.builder().addNetworkAdapter(networkAdapter)
|
||||||
.build();
|
.slot(0L).build();
|
||||||
|
|
||||||
NetworkSpec networkSpec = NetworkSpec.builder().addNIC(0L, networkInterfaceCard).build();
|
NetworkSpec networkSpec = NetworkSpec.builder().addNIC(networkInterfaceCard).build();
|
||||||
|
|
||||||
MasterSpec masterSpec = MasterSpec
|
MasterSpec masterSpec = MasterSpec
|
||||||
.builder()
|
.builder()
|
||||||
|
@ -189,6 +194,14 @@ public class MastersCache extends AbstractLoadingCache<Image, Master> {
|
||||||
return master;
|
return master;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized Master getIfPresent(Image key) {
|
||||||
|
if (masters.containsKey(key.getId())) {
|
||||||
|
return masters.get(key.getId());
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
private String getFilePathOrDownload(String httpUrl) throws ExecutionException {
|
private String getFilePathOrDownload(String httpUrl) throws ExecutionException {
|
||||||
String fileName = httpUrl.substring(httpUrl.lastIndexOf('/') + 1, httpUrl.length());
|
String fileName = httpUrl.substring(httpUrl.lastIndexOf('/') + 1, httpUrl.length());
|
||||||
File localFile = new File(isosDir, fileName);
|
File localFile = new File(isosDir, fileName);
|
||||||
|
@ -200,12 +213,4 @@ public class MastersCache extends AbstractLoadingCache<Image, Master> {
|
||||||
return localFile.getAbsolutePath();
|
return localFile.getAbsolutePath();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public Master getIfPresent(Image key) {
|
|
||||||
if (masters.containsKey(key.getId())) {
|
|
||||||
return masters.get(key.getId());
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/**
|
/**
|
||||||
mh * Licensed to jclouds, Inc. (jclouds) under one or more
|
* Licensed to jclouds, Inc. (jclouds) under one or more
|
||||||
* contributor license agreements. See the NOTICE file
|
* contributor license agreements. See the NOTICE file
|
||||||
* distributed with this work for additional information
|
* distributed with this work for additional information
|
||||||
* regarding copyright ownership. jclouds licenses this file
|
* regarding copyright ownership. jclouds licenses this file
|
||||||
|
@ -19,13 +19,18 @@ mh * Licensed to jclouds, Inc. (jclouds) under one or more
|
||||||
|
|
||||||
package org.jclouds.virtualbox.functions;
|
package org.jclouds.virtualbox.functions;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
import static org.jclouds.virtualbox.config.VirtualBoxConstants.VIRTUALBOX_IMAGE_PREFIX;
|
import static org.jclouds.virtualbox.config.VirtualBoxConstants.VIRTUALBOX_IMAGE_PREFIX;
|
||||||
import static org.jclouds.virtualbox.config.VirtualBoxConstants.VIRTUALBOX_NODE_PREFIX;
|
import static org.jclouds.virtualbox.config.VirtualBoxConstants.VIRTUALBOX_NODE_PREFIX;
|
||||||
|
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
import org.jclouds.compute.ComputeServiceAdapter.NodeAndInitialCredentials;
|
import org.jclouds.compute.ComputeServiceAdapter.NodeAndInitialCredentials;
|
||||||
|
import org.jclouds.compute.domain.NodeMetadata;
|
||||||
|
import org.jclouds.compute.options.RunScriptOptions;
|
||||||
import org.jclouds.domain.LoginCredentials;
|
import org.jclouds.domain.LoginCredentials;
|
||||||
import org.jclouds.virtualbox.domain.CloneSpec;
|
import org.jclouds.virtualbox.domain.CloneSpec;
|
||||||
import org.jclouds.virtualbox.domain.ExecutionType;
|
import org.jclouds.virtualbox.domain.ExecutionType;
|
||||||
|
@ -35,6 +40,7 @@ import org.jclouds.virtualbox.domain.NetworkInterfaceCard;
|
||||||
import org.jclouds.virtualbox.domain.NetworkSpec;
|
import org.jclouds.virtualbox.domain.NetworkSpec;
|
||||||
import org.jclouds.virtualbox.domain.NodeSpec;
|
import org.jclouds.virtualbox.domain.NodeSpec;
|
||||||
import org.jclouds.virtualbox.domain.VmSpec;
|
import org.jclouds.virtualbox.domain.VmSpec;
|
||||||
|
import org.jclouds.virtualbox.statements.SetIpAddress;
|
||||||
import org.jclouds.virtualbox.util.MachineUtils;
|
import org.jclouds.virtualbox.util.MachineUtils;
|
||||||
import org.virtualbox_4_1.CleanupMode;
|
import org.virtualbox_4_1.CleanupMode;
|
||||||
import org.virtualbox_4_1.IMachine;
|
import org.virtualbox_4_1.IMachine;
|
||||||
|
@ -48,20 +54,43 @@ import com.google.common.base.Supplier;
|
||||||
@Singleton
|
@Singleton
|
||||||
public class NodeCreator implements Function<NodeSpec, NodeAndInitialCredentials<IMachine>> {
|
public class NodeCreator implements Function<NodeSpec, NodeAndInitialCredentials<IMachine>> {
|
||||||
|
|
||||||
|
// TODO parameterize
|
||||||
|
public static final int NODE_PORT_INIT = 3000;
|
||||||
|
|
||||||
|
// TODO parameterize
|
||||||
|
public static final String VMS_NETWORK = "33.33.33.";
|
||||||
|
|
||||||
|
// TODO parameterize
|
||||||
|
public static final String HOST_ONLY_IFACE_NAME = "vboxnet0";
|
||||||
|
|
||||||
|
// TODO parameterize
|
||||||
|
public static final boolean USE_LINKED = true;
|
||||||
|
|
||||||
private final Supplier<VirtualBoxManager> manager;
|
private final Supplier<VirtualBoxManager> manager;
|
||||||
private final Function<CloneSpec, IMachine> cloner;
|
private final Function<CloneSpec, IMachine> cloner;
|
||||||
|
private final AtomicInteger nodePorts;
|
||||||
|
private final AtomicInteger nodeIps;
|
||||||
|
private MachineUtils machineUtils;
|
||||||
|
private Function<IMachine, NodeMetadata> imachineToNodeMetadata;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public NodeCreator(Supplier<VirtualBoxManager> manager, Function<CloneSpec, IMachine> cloner,
|
public NodeCreator(Supplier<VirtualBoxManager> manager, Function<CloneSpec, IMachine> cloner,
|
||||||
MachineUtils machineUtils) {
|
MachineUtils machineUtils, Function<IMachine, NodeMetadata> imachineToNodeMetadata) {
|
||||||
this.manager = manager;
|
this.manager = manager;
|
||||||
this.cloner = cloner;
|
this.cloner = cloner;
|
||||||
|
this.nodePorts = new AtomicInteger(NODE_PORT_INIT);
|
||||||
|
this.nodeIps = new AtomicInteger(1);
|
||||||
|
this.machineUtils = machineUtils;
|
||||||
|
this.imachineToNodeMetadata = imachineToNodeMetadata;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public NodeAndInitialCredentials<IMachine> apply(NodeSpec nodeSpec) {
|
public NodeAndInitialCredentials<IMachine> apply(NodeSpec nodeSpec) {
|
||||||
|
|
||||||
|
checkNotNull(nodeSpec, "NodeSpec");
|
||||||
|
|
||||||
Master master = nodeSpec.getMaster();
|
Master master = nodeSpec.getMaster();
|
||||||
|
checkNotNull(master, "Master");
|
||||||
|
|
||||||
if (master.getMachine().getCurrentSnapshot() != null) {
|
if (master.getMachine().getCurrentSnapshot() != null) {
|
||||||
ISession session;
|
ISession session;
|
||||||
|
@ -81,21 +110,29 @@ public class NodeCreator implements Function<NodeSpec, NodeAndInitialCredentials
|
||||||
VmSpec cloneVmSpec = VmSpec.builder().id(cloneName).name(cloneName).memoryMB(512).cleanUpMode(CleanupMode.Full)
|
VmSpec cloneVmSpec = VmSpec.builder().id(cloneName).name(cloneName).memoryMB(512).cleanUpMode(CleanupMode.Full)
|
||||||
.forceOverwrite(true).build();
|
.forceOverwrite(true).build();
|
||||||
|
|
||||||
NetworkAdapter networkAdapter = NetworkAdapter.builder().networkAttachmentType(NetworkAttachmentType.NAT)
|
NetworkAdapter natAdapter = NetworkAdapter.builder().networkAttachmentType(NetworkAttachmentType.NAT)
|
||||||
.tcpRedirectRule("127.0.0.1", 2222, "", 22).build();
|
.tcpRedirectRule("127.0.0.1", this.nodePorts.getAndIncrement(), "", 22).build();
|
||||||
|
|
||||||
NetworkInterfaceCard networkInterfaceCard = NetworkInterfaceCard.builder().addNetworkAdapter(networkAdapter)
|
NetworkInterfaceCard natIfaceCard = NetworkInterfaceCard.builder().addNetworkAdapter(natAdapter).slot(0L).build();
|
||||||
.build();
|
|
||||||
|
|
||||||
NetworkSpec networkSpec = NetworkSpec.builder().addNIC(0L, networkInterfaceCard).build();
|
NetworkAdapter hostOnlyAdapter = NetworkAdapter.builder().networkAttachmentType(NetworkAttachmentType.HostOnly)
|
||||||
|
.staticIp(VMS_NETWORK + this.nodeIps.getAndIncrement()).build();
|
||||||
|
|
||||||
CloneSpec cloneSpec = CloneSpec.builder().linked(false).master(master.getMachine()).network(networkSpec)
|
NetworkInterfaceCard hostOnlyIfaceCard = NetworkInterfaceCard.builder().addNetworkAdapter(hostOnlyAdapter)
|
||||||
|
.addHostInterfaceName(HOST_ONLY_IFACE_NAME).slot(1L).build();
|
||||||
|
|
||||||
|
NetworkSpec networkSpec = NetworkSpec.builder().addNIC(natIfaceCard).addNIC(hostOnlyIfaceCard).build();
|
||||||
|
|
||||||
|
CloneSpec cloneSpec = CloneSpec.builder().linked(USE_LINKED).master(master.getMachine()).network(networkSpec)
|
||||||
.vm(cloneVmSpec).build();
|
.vm(cloneVmSpec).build();
|
||||||
|
|
||||||
IMachine cloned = cloner.apply(cloneSpec);
|
IMachine cloned = cloner.apply(cloneSpec);
|
||||||
|
|
||||||
new LaunchMachineIfNotAlreadyRunning(manager.get(), ExecutionType.GUI, "").apply(cloned);
|
new LaunchMachineIfNotAlreadyRunning(manager.get(), ExecutionType.GUI, "").apply(cloned);
|
||||||
|
|
||||||
|
machineUtils.runScriptOnNode(imachineToNodeMetadata.apply(cloned), new SetIpAddress(hostOnlyIfaceCard),
|
||||||
|
RunScriptOptions.NONE);
|
||||||
|
|
||||||
// TODO get credentials from somewhere else (they are also HC in IMachineToSshClient)
|
// TODO get credentials from somewhere else (they are also HC in IMachineToSshClient)
|
||||||
NodeAndInitialCredentials<IMachine> nodeAndInitialCredentials = new NodeAndInitialCredentials<IMachine>(cloned,
|
NodeAndInitialCredentials<IMachine> nodeAndInitialCredentials = new NodeAndInitialCredentials<IMachine>(cloned,
|
||||||
cloneName, LoginCredentials.builder().user("toor").password("password").authenticateSudo(true).build());
|
cloneName, LoginCredentials.builder().user("toor").password("password").authenticateSudo(true).build());
|
||||||
|
|
|
@ -0,0 +1,117 @@
|
||||||
|
/**
|
||||||
|
* Licensed to jclouds, Inc. (jclouds) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. jclouds licenses this file
|
||||||
|
* to you 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.
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
U * Licensed to jclouds, Inc. (jclouds) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. jclouds licenses this file
|
||||||
|
* to you 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.virtualbox.functions.admin;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
import static com.google.common.collect.Iterables.transform;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import javax.annotation.Resource;
|
||||||
|
import javax.inject.Named;
|
||||||
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
|
import org.jclouds.compute.reference.ComputeServiceConstants;
|
||||||
|
import org.jclouds.logging.Logger;
|
||||||
|
import org.jclouds.virtualbox.domain.ErrorCode;
|
||||||
|
import org.virtualbox_4_1.CleanupMode;
|
||||||
|
import org.virtualbox_4_1.DeviceType;
|
||||||
|
import org.virtualbox_4_1.IMachine;
|
||||||
|
import org.virtualbox_4_1.IMedium;
|
||||||
|
import org.virtualbox_4_1.IProgress;
|
||||||
|
import org.virtualbox_4_1.VBoxException;
|
||||||
|
|
||||||
|
import com.google.common.base.Function;
|
||||||
|
import com.google.common.base.Throwables;
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
|
||||||
|
@Singleton
|
||||||
|
public class UnregisterMachineIfExistsAndForceDeleteItsMedia implements Function<IMachine, Void> {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
@Named(ComputeServiceConstants.COMPUTE_LOGGER)
|
||||||
|
protected Logger logger = Logger.NULL;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Void apply(IMachine machine) {
|
||||||
|
List<IMedium> mediaToBeDeleted = Collections.emptyList();
|
||||||
|
try {
|
||||||
|
mediaToBeDeleted = machine.unregister(CleanupMode.Full);
|
||||||
|
} catch (VBoxException e) {
|
||||||
|
ErrorCode errorCode = ErrorCode.valueOf(e);
|
||||||
|
switch (errorCode) {
|
||||||
|
case VBOX_E_OBJECT_NOT_FOUND:
|
||||||
|
logger.debug("Machine %s does not exists, cannot unregister", machine.getName());
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
List<IMedium> filteredMediaToBeDeleted = Lists.newArrayList(transform(mediaToBeDeleted,
|
||||||
|
new DeleteChildrenOfMedium()));
|
||||||
|
if (!filteredMediaToBeDeleted.isEmpty()) {
|
||||||
|
try {
|
||||||
|
IProgress deletion = machine.delete(filteredMediaToBeDeleted);
|
||||||
|
deletion.waitForCompletion(-1);
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error(e, "Problem in deleting the media attached to %s", machine.getName());
|
||||||
|
Throwables.propagate(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private class DeleteChildrenOfMedium implements Function<IMedium, IMedium> {
|
||||||
|
@Override
|
||||||
|
public IMedium apply(IMedium medium) {
|
||||||
|
checkNotNull(medium.getChildren());
|
||||||
|
if (medium.getDeviceType().equals(DeviceType.HardDisk)) {
|
||||||
|
for (IMedium child : medium.getChildren()) {
|
||||||
|
IProgress deletion = child.deleteStorage();
|
||||||
|
deletion.waitForCompletion(-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return medium;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,76 @@
|
||||||
|
/**
|
||||||
|
* Licensed to jclouds, Inc. (jclouds) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. jclouds licenses this file
|
||||||
|
* to you 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.virtualbox.statements;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
|
||||||
|
import org.jclouds.scriptbuilder.domain.OsFamily;
|
||||||
|
import org.jclouds.scriptbuilder.domain.Statement;
|
||||||
|
import org.jclouds.virtualbox.domain.NetworkInterfaceCard;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the ipaddress using ssh. Used for host-only networking
|
||||||
|
*
|
||||||
|
* @author dralves
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class SetIpAddress implements Statement {
|
||||||
|
|
||||||
|
private String script;
|
||||||
|
|
||||||
|
public SetIpAddress(NetworkInterfaceCard networkInterfaceCard) {
|
||||||
|
String ipAddress = networkInterfaceCard.getNetworkAdapter().getStaticIp();
|
||||||
|
checkNotNull(ipAddress, "ip address");
|
||||||
|
int slot = (int) networkInterfaceCard.getSlot();
|
||||||
|
String iface = null;
|
||||||
|
switch (slot) {
|
||||||
|
case 0:
|
||||||
|
iface = "eth0";
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
iface = "eth1";
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
iface = "eth2";
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
iface = "eth3";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new IllegalArgumentException("slot must be 0,1,2,3 (was: " + slot + ")");
|
||||||
|
}
|
||||||
|
script = String.format("ifconfig %s %s;", iface, ipAddress);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterable<String> functionDependencies(OsFamily family) {
|
||||||
|
return ImmutableList.of();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String render(OsFamily family) {
|
||||||
|
if (checkNotNull(family, "family") == OsFamily.WINDOWS)
|
||||||
|
throw new UnsupportedOperationException("windows not yet implemented");
|
||||||
|
return script;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -24,6 +24,7 @@ import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.inject.Named;
|
import javax.inject.Named;
|
||||||
|
@ -39,9 +40,10 @@ import org.jclouds.compute.domain.NodeMetadata;
|
||||||
import org.jclouds.compute.domain.OsFamily;
|
import org.jclouds.compute.domain.OsFamily;
|
||||||
import org.jclouds.compute.domain.Template;
|
import org.jclouds.compute.domain.Template;
|
||||||
import org.jclouds.compute.strategy.PrioritizeCredentialsFromTemplate;
|
import org.jclouds.compute.strategy.PrioritizeCredentialsFromTemplate;
|
||||||
|
import org.jclouds.concurrent.MoreExecutors;
|
||||||
|
import org.jclouds.concurrent.config.ExecutorServiceModule;
|
||||||
import org.jclouds.logging.slf4j.config.SLF4JLoggingModule;
|
import org.jclouds.logging.slf4j.config.SLF4JLoggingModule;
|
||||||
import org.jclouds.sshj.config.SshjSshClientModule;
|
import org.jclouds.sshj.config.SshjSshClientModule;
|
||||||
import org.jclouds.virtualbox.compute.VirtualBoxComputeServiceAdapter;
|
|
||||||
import org.jclouds.virtualbox.config.VirtualBoxConstants;
|
import org.jclouds.virtualbox.config.VirtualBoxConstants;
|
||||||
import org.jclouds.virtualbox.domain.IsoSpec;
|
import org.jclouds.virtualbox.domain.IsoSpec;
|
||||||
import org.jclouds.virtualbox.domain.Master;
|
import org.jclouds.virtualbox.domain.Master;
|
||||||
|
@ -109,6 +111,8 @@ public class BaseVirtualBoxClientLiveTest extends BaseVersionedServiceLiveTest {
|
||||||
protected PrioritizeCredentialsFromTemplate prioritizeCredentialsFromTemplate;
|
protected PrioritizeCredentialsFromTemplate prioritizeCredentialsFromTemplate;
|
||||||
@Inject
|
@Inject
|
||||||
protected LoadingCache<Image, Master> mastersCache;
|
protected LoadingCache<Image, Master> mastersCache;
|
||||||
|
|
||||||
|
private final ExecutorService singleThreadExec = MoreExecutors.sameThreadExecutor();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void setupCredentials() {
|
protected void setupCredentials() {
|
||||||
|
@ -131,17 +135,10 @@ public class BaseVirtualBoxClientLiveTest extends BaseVersionedServiceLiveTest {
|
||||||
setupCredentials();
|
setupCredentials();
|
||||||
Properties overrides = new VirtualBoxPropertiesBuilder(setupProperties()).build();
|
Properties overrides = new VirtualBoxPropertiesBuilder(setupProperties()).build();
|
||||||
|
|
||||||
CacheNodeStoreModule hostModule = new CacheNodeStoreModule(ImmutableMap.of(
|
context = new ComputeServiceContextFactory().createContext(provider, identity, credential, ImmutableSet
|
||||||
"host",
|
.<Module> of(new SLF4JLoggingModule(), new SshjSshClientModule(), new ExecutorServiceModule(
|
||||||
Node.builder().id("host").name("host installing virtualbox").hostname("localhost")
|
singleThreadExec, singleThreadExec)), overrides);
|
||||||
.osFamily(OsFamily.LINUX.toString()).osDescription(System.getProperty("os.name"))
|
|
||||||
.osVersion(System.getProperty("os.version")).group("ssh")
|
|
||||||
.username(System.getProperty("user.name"))
|
|
||||||
.credentialUrl(URI.create("file://" + System.getProperty("user.home") + "/.ssh/id_rsa"))
|
|
||||||
.build()));
|
|
||||||
|
|
||||||
context = new ComputeServiceContextFactory().createContext(provider, identity, credential,
|
|
||||||
ImmutableSet.<Module> of(new SLF4JLoggingModule(), new SshjSshClientModule(), hostModule), overrides);
|
|
||||||
context.utils().injector().injectMembers(this);
|
context.utils().injector().injectMembers(this);
|
||||||
|
|
||||||
imageId = "ubuntu-11.04-server-i386";
|
imageId = "ubuntu-11.04-server-i386";
|
||||||
|
@ -157,6 +154,7 @@ public class BaseVirtualBoxClientLiveTest extends BaseVersionedServiceLiveTest {
|
||||||
checkNotNull(mastersCache.apply(template.getImage()));
|
checkNotNull(mastersCache.apply(template.getImage()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected void undoVm(VmSpec vmSpecification) {
|
protected void undoVm(VmSpec vmSpecification) {
|
||||||
machineUtils.unlockMachineAndApplyOrReturnNullIfNotRegistered(vmSpecification.getVmId(),
|
machineUtils.unlockMachineAndApplyOrReturnNullIfNotRegistered(vmSpecification.getVmId(),
|
||||||
new UnregisterMachineIfExistsAndDeleteItsMedia(vmSpecification));
|
new UnregisterMachineIfExistsAndDeleteItsMedia(vmSpecification));
|
||||||
|
|
|
@ -58,20 +58,6 @@ public class VirtualBoxComputeServiceAdapterLiveTest extends BaseVirtualBoxClien
|
||||||
assertEquals(machine.getNode().getName(), machineName);
|
assertEquals(machine.getNode().getName(), machineName);
|
||||||
doConnectViaSsh(machine.getNode(), prioritizeCredentialsFromTemplate.apply(template, machine.getCredentials()));
|
doConnectViaSsh(machine.getNode(), prioritizeCredentialsFromTemplate.apply(template, machine.getCredentials()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testListHardwareProfiles() {
|
|
||||||
Iterable<IMachine> profiles = adapter.listHardwareProfiles();
|
|
||||||
assertEquals(1, Iterables.size(profiles));
|
|
||||||
//TODO: check state;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testListImages() {
|
|
||||||
Iterable<Image> iMageIterable = adapter.listImages();
|
|
||||||
assertEquals(1, Iterables.size(iMageIterable));
|
|
||||||
//TODO: check state;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void doConnectViaSsh(IMachine machine, LoginCredentials creds) {
|
protected void doConnectViaSsh(IMachine machine, LoginCredentials creds) {
|
||||||
SshClient ssh = context.utils().injector().getInstance(IMachineToSshClient.class).apply(machine);
|
SshClient ssh = context.utils().injector().getInstance(IMachineToSshClient.class).apply(machine);
|
||||||
|
@ -88,6 +74,20 @@ public class VirtualBoxComputeServiceAdapterLiveTest extends BaseVirtualBoxClien
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testListHardwareProfiles() {
|
||||||
|
Iterable<IMachine> profiles = adapter.listHardwareProfiles();
|
||||||
|
assertEquals(1, Iterables.size(profiles));
|
||||||
|
//TODO: check state;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testListImages() {
|
||||||
|
Iterable<Image> iMageIterable = adapter.listImages();
|
||||||
|
assertEquals(1, Iterables.size(iMageIterable));
|
||||||
|
//TODO: check state;
|
||||||
|
}
|
||||||
|
|
||||||
@AfterClass
|
@AfterClass
|
||||||
@Override
|
@Override
|
||||||
protected void tearDown() throws Exception {
|
protected void tearDown() throws Exception {
|
||||||
|
|
|
@ -19,19 +19,56 @@
|
||||||
|
|
||||||
package org.jclouds.virtualbox.compute;
|
package org.jclouds.virtualbox.compute;
|
||||||
|
|
||||||
|
import static org.testng.Assert.assertEquals;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import javax.annotation.Resource;
|
||||||
|
import javax.inject.Named;
|
||||||
|
|
||||||
|
import org.jclouds.compute.RunNodesException;
|
||||||
|
import org.jclouds.compute.domain.ExecResponse;
|
||||||
|
import org.jclouds.compute.domain.NodeMetadata;
|
||||||
|
import org.jclouds.compute.reference.ComputeServiceConstants;
|
||||||
|
import org.jclouds.logging.Logger;
|
||||||
|
import org.jclouds.ssh.SshClient;
|
||||||
import org.jclouds.virtualbox.BaseVirtualBoxClientLiveTest;
|
import org.jclouds.virtualbox.BaseVirtualBoxClientLiveTest;
|
||||||
import org.testng.annotations.Test;
|
import org.testng.annotations.Test;
|
||||||
|
|
||||||
|
import com.google.common.base.Predicate;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @author Adrian Cole
|
* @author Adrian Cole
|
||||||
*/
|
*/
|
||||||
@Test(groups = "live", testName = "VirtualBoxExperimentLiveTest")
|
@Test(groups = "live", singleThreaded = true, testName = "VirtualBoxExperimentLiveTest")
|
||||||
public class VirtualBoxExperimentLiveTest extends BaseVirtualBoxClientLiveTest {
|
public class VirtualBoxExperimentLiveTest extends BaseVirtualBoxClientLiveTest {
|
||||||
|
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
@Named(ComputeServiceConstants.COMPUTE_LOGGER)
|
||||||
|
protected Logger logger = Logger.NULL;
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testAndExperiment() {
|
public void testLaunchCluster() throws RunNodesException {
|
||||||
context.getComputeService().listNodes();
|
int numNodes = 4;
|
||||||
|
final String clusterName = "test-launch-cluster";
|
||||||
|
Set<? extends NodeMetadata> nodes = context.getComputeService().createNodesInGroup(clusterName,
|
||||||
|
numNodes);
|
||||||
|
assertEquals(numNodes, nodes.size(), "wrong number of nodes");
|
||||||
|
for (NodeMetadata node : nodes) {
|
||||||
|
logger.debug("Created Node: %s", node);
|
||||||
|
SshClient client = context.utils().sshForNode().apply(node);
|
||||||
|
client.connect();
|
||||||
|
ExecResponse hello = client.exec("echo hello");
|
||||||
|
assertEquals(hello.getOutput().trim(), "hello");
|
||||||
|
}
|
||||||
|
context.getComputeService().destroyNodesMatching(new Predicate<NodeMetadata>() {
|
||||||
|
@Override
|
||||||
|
public boolean apply(NodeMetadata input) {
|
||||||
|
return input.getId().contains(clusterName);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -99,7 +99,7 @@ public class CloneAndRegisterMachineFromIMachineIfNotAlreadyExistsLiveTest exten
|
||||||
NetworkInterfaceCard networkInterfaceCard = NetworkInterfaceCard.builder().addNetworkAdapter(networkAdapter)
|
NetworkInterfaceCard networkInterfaceCard = NetworkInterfaceCard.builder().addNetworkAdapter(networkAdapter)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
this.cloneNetworkSpec = NetworkSpec.builder().addNIC(0L, networkInterfaceCard).build();
|
this.cloneNetworkSpec = NetworkSpec.builder().addNIC(networkInterfaceCard).build();
|
||||||
|
|
||||||
sourceMachineSpec = MasterSpec.builder().iso(isoSpec).vm(sourceVmSpec).network(cloneNetworkSpec).build();
|
sourceMachineSpec = MasterSpec.builder().iso(isoSpec).vm(sourceVmSpec).network(cloneNetworkSpec).build();
|
||||||
|
|
||||||
|
|
|
@ -105,7 +105,7 @@ public class CreateAndInstallVmLiveTest extends BaseVirtualBoxClientLiveTest {
|
||||||
NetworkInterfaceCard networkInterfaceCard = NetworkInterfaceCard.builder().addNetworkAdapter(networkAdapter)
|
NetworkInterfaceCard networkInterfaceCard = NetworkInterfaceCard.builder().addNetworkAdapter(networkAdapter)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
NetworkSpec networkSpec = NetworkSpec.builder().addNIC(0L, networkInterfaceCard).build();
|
NetworkSpec networkSpec = NetworkSpec.builder().addNIC(networkInterfaceCard).build();
|
||||||
|
|
||||||
masterSpec = MasterSpec
|
masterSpec = MasterSpec
|
||||||
.builder()
|
.builder()
|
||||||
|
|
|
@ -80,7 +80,7 @@ public class CreateAndRegisterMachineFromIsoIfNotAlreadyExistsLiveTest extends
|
||||||
.builder().addNetworkAdapter(networkAdapter).build();
|
.builder().addNetworkAdapter(networkAdapter).build();
|
||||||
|
|
||||||
NetworkSpec networkSpec = NetworkSpec.builder()
|
NetworkSpec networkSpec = NetworkSpec.builder()
|
||||||
.addNIC(0L, networkInterfaceCard).build();
|
.addNIC(networkInterfaceCard).build();
|
||||||
|
|
||||||
MasterSpec machineSpec = MasterSpec
|
MasterSpec machineSpec = MasterSpec
|
||||||
.builder()
|
.builder()
|
||||||
|
|
|
@ -99,7 +99,7 @@ public class GuestAdditionsInstallerLiveTest extends
|
||||||
.builder().addNetworkAdapter(networkAdapter).build();
|
.builder().addNetworkAdapter(networkAdapter).build();
|
||||||
|
|
||||||
NetworkSpec networkSpec = NetworkSpec.builder()
|
NetworkSpec networkSpec = NetworkSpec.builder()
|
||||||
.addNIC(0L, networkInterfaceCard).build();
|
.addNIC(networkInterfaceCard).build();
|
||||||
sourceMachineSpec = MasterSpec.builder().iso(isoSpec).vm(sourceVmSpec)
|
sourceMachineSpec = MasterSpec.builder().iso(isoSpec).vm(sourceVmSpec)
|
||||||
.network(networkSpec).build();
|
.network(networkSpec).build();
|
||||||
|
|
||||||
|
|
|
@ -90,7 +90,7 @@ public class IMachinePredicatesLiveTest extends BaseVirtualBoxClientLiveTest {
|
||||||
NetworkInterfaceCard networkInterfaceCard = NetworkInterfaceCard.builder().addNetworkAdapter(networkAdapter)
|
NetworkInterfaceCard networkInterfaceCard = NetworkInterfaceCard.builder().addNetworkAdapter(networkAdapter)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
this.networkSpec = NetworkSpec.builder().addNIC(0L, networkInterfaceCard).build();
|
this.networkSpec = NetworkSpec.builder().addNIC(networkInterfaceCard).build();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,67 @@
|
||||||
|
/**
|
||||||
|
* Licensed to jclouds, Inc. (jclouds) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. jclouds licenses this file
|
||||||
|
* to you 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.virtualbox.statements;
|
||||||
|
|
||||||
|
import static junit.framework.Assert.assertEquals;
|
||||||
|
|
||||||
|
import org.jclouds.scriptbuilder.domain.OsFamily;
|
||||||
|
import org.jclouds.virtualbox.domain.NetworkAdapter;
|
||||||
|
import org.jclouds.virtualbox.domain.NetworkInterfaceCard;
|
||||||
|
import org.testng.annotations.Test;
|
||||||
|
import org.virtualbox_4_1.NetworkAttachmentType;
|
||||||
|
|
||||||
|
public class SetIpAddressTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSetIpeth0() {
|
||||||
|
NetworkInterfaceCard networkInterfaceCard = NetworkInterfaceCard
|
||||||
|
.builder()
|
||||||
|
.slot(0L)
|
||||||
|
.addNetworkAdapter(
|
||||||
|
NetworkAdapter.builder().staticIp("127.0.0.1").networkAttachmentType(NetworkAttachmentType.NAT)
|
||||||
|
.build()).build();
|
||||||
|
SetIpAddress setIpAddressStmtm = new SetIpAddress(networkInterfaceCard);
|
||||||
|
assertEquals("ifconfig eth0 127.0.0.1;", setIpAddressStmtm.render(OsFamily.UNIX));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSetIpeth3() {
|
||||||
|
NetworkInterfaceCard networkInterfaceCard = NetworkInterfaceCard
|
||||||
|
.builder()
|
||||||
|
.slot(3L)
|
||||||
|
.addNetworkAdapter(
|
||||||
|
NetworkAdapter.builder().staticIp("localhost").networkAttachmentType(NetworkAttachmentType.NAT)
|
||||||
|
.build()).build();
|
||||||
|
SetIpAddress setIpAddressStmtm = new SetIpAddress(networkInterfaceCard);
|
||||||
|
assertEquals("ifconfig eth3 localhost;", setIpAddressStmtm.render(OsFamily.UNIX));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expectedExceptions = IllegalArgumentException.class)
|
||||||
|
public void testThrowsIllegalArgumentExceptionOnWrongSlot() {
|
||||||
|
NetworkInterfaceCard networkInterfaceCard = NetworkInterfaceCard
|
||||||
|
.builder()
|
||||||
|
.slot(4L)
|
||||||
|
.addNetworkAdapter(
|
||||||
|
NetworkAdapter.builder().staticIp("localhost").networkAttachmentType(NetworkAttachmentType.NAT)
|
||||||
|
.build()).build();
|
||||||
|
new SetIpAddress(networkInterfaceCard);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -124,7 +124,7 @@ public class ServerToNodeMetadata implements Function<Server, NodeMetadata> {
|
||||||
try {
|
try {
|
||||||
image = Iterables.find(images.get(), new FindImageForServer(from));
|
image = Iterables.find(images.get(), new FindImageForServer(from));
|
||||||
} catch (NoSuchElementException e) {
|
} catch (NoSuchElementException e) {
|
||||||
logger.warn("could not find a matching image for server %s", from);
|
logger.debug("could not find a matching image for server %s", from);
|
||||||
}
|
}
|
||||||
return image;
|
return image;
|
||||||
}
|
}
|
||||||
|
@ -134,8 +134,8 @@ public class ServerToNodeMetadata implements Function<Server, NodeMetadata> {
|
||||||
try {
|
try {
|
||||||
hardware = Iterables.find(hardwares.get(), new FindHardwareForServer(from));
|
hardware = Iterables.find(hardwares.get(), new FindHardwareForServer(from));
|
||||||
} catch (NoSuchElementException e) {
|
} catch (NoSuchElementException e) {
|
||||||
logger.warn("could not find a matching hardware for server %s", from);
|
logger.debug("could not find a matching hardware for server %s", from);
|
||||||
}
|
}
|
||||||
return hardware;
|
return hardware;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -354,8 +354,7 @@ public class GoGridLiveTestDisabled extends BaseVersionedServiceLiveTest {
|
||||||
|
|
||||||
socketOpen.apply(socket);
|
socketOpen.apply(socket);
|
||||||
|
|
||||||
SshClient sshClient = new SshjSshClient(new BackoffLimitedRetryHandler(), socket, 60000,
|
SshClient sshClient = context.utils().injector().getInstance(SshClient.Factory.class).create(socket, instanceCredentials);
|
||||||
instanceCredentials.identity, instanceCredentials.credential, null);
|
|
||||||
sshClient.connect();
|
sshClient.connect();
|
||||||
String output = sshClient.exec("df").getOutput();
|
String output = sshClient.exec("df").getOutput();
|
||||||
assertTrue(output.contains("Filesystem"),
|
assertTrue(output.contains("Filesystem"),
|
||||||
|
|
|
@ -129,8 +129,8 @@ public class ServerToNodeMetadata implements Function<Server, NodeMetadata> {
|
||||||
try {
|
try {
|
||||||
return Iterables.find(images.get(), new FindImageForServer(location, from)).getOperatingSystem();
|
return Iterables.find(images.get(), new FindImageForServer(location, from)).getOperatingSystem();
|
||||||
} catch (NoSuchElementException e) {
|
} catch (NoSuchElementException e) {
|
||||||
logger.warn("could not find a matching image for server %s in location %s", from, location);
|
logger.debug("could not find a matching image for server %s in location %s", from, location);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -129,7 +129,7 @@ public class SliceToNodeMetadata implements Function<Slice, NodeMetadata> {
|
||||||
try {
|
try {
|
||||||
return Iterables.find(hardwares.get(), new FindHardwareForSlice(from));
|
return Iterables.find(hardwares.get(), new FindHardwareForSlice(from));
|
||||||
} catch (NoSuchElementException e) {
|
} catch (NoSuchElementException e) {
|
||||||
logger.warn("could not find a matching hardware for slice %s", from);
|
logger.debug("could not find a matching hardware for slice %s", from);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -138,7 +138,7 @@ public class SliceToNodeMetadata implements Function<Slice, NodeMetadata> {
|
||||||
try {
|
try {
|
||||||
return Iterables.find(images.get(), new FindImageForSlice(from)).getOperatingSystem();
|
return Iterables.find(images.get(), new FindImageForSlice(from)).getOperatingSystem();
|
||||||
} catch (NoSuchElementException e) {
|
} catch (NoSuchElementException e) {
|
||||||
logger.warn("could not find a matching image for slice %s in location %s", from, location);
|
logger.debug("could not find a matching image for slice %s in location %s", from, location);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue