/* * (C) Copyright 2014 Kurento (http://kurento.org/) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package org.kurento.test.utils; import static org.kurento.commons.PropertiesManager.getProperty; import static org.kurento.test.config.TestConfiguration.TEST_NODE_LOGIN_PROPERTY; import static org.kurento.test.config.TestConfiguration.TEST_NODE_PASSWD_PROPERTY; import static org.kurento.test.config.TestConfiguration.TEST_NODE_PEM_PROPERTY; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.nio.file.Files; import java.nio.file.Paths; import java.nio.file.StandardCopyOption; import java.util.Arrays; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import org.kurento.commons.exception.KurentoException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.io.CharStreams; import com.xebialabs.overthere.CmdLine; import com.xebialabs.overthere.ConnectionOptions; import com.xebialabs.overthere.OperatingSystemFamily; import com.xebialabs.overthere.Overthere; import com.xebialabs.overthere.OverthereConnection; import com.xebialabs.overthere.OverthereFile; import com.xebialabs.overthere.OverthereProcess; import com.xebialabs.overthere.ssh.SshConnectionBuilder; import com.xebialabs.overthere.ssh.SshConnectionType; /** * SSH connection to a remote host. * * @author Boni Garcia (bgarcia@gsyc.es) * @since 4.2.5 */ public class SshConnection { public static Logger log = LoggerFactory.getLogger(SshConnection.class); public static final String DEFAULT_TMP_FOLDER = "/tmp"; private static final int NODE_INITIAL_PORT = 5555; private static final int PING_TIMEOUT = 2; // seconds private static final int DEFAULT_CONNECTION_TIMEOUT = 30000; // ms private String host; private String login; private String passwd; private String pem; private String tmpFolder; private int connectionTimeout; private OverthereConnection connection; public SshConnection(String host) { this.host = host; this.login = getProperty(TEST_NODE_LOGIN_PROPERTY); String pem = getProperty(TEST_NODE_PEM_PROPERTY); if (pem != null) { this.pem = pem; } else { this.passwd = getProperty(TEST_NODE_PASSWD_PROPERTY); } this.connectionTimeout = DEFAULT_CONNECTION_TIMEOUT; } public SshConnection(String host, String login, String passwd, String pem) { this.host = host; this.login = login; if (pem != null) { this.pem = pem; } else { this.passwd = passwd; } } public List<String> listFiles(String folder, boolean recursive, boolean includeFolders) { String[] command = null; if (recursive && includeFolders) { command = new String[] { "find", folder }; } else if (recursive && !includeFolders) { command = new String[] { "find", folder, "-type", "f" }; } else if (!recursive && includeFolders) { command = new String[] { "find", folder, "-maxdepth", "1" }; } else if (!recursive && !includeFolders) { command = new String[] { "find", folder, "-maxdepth", "1", "-type", "f" }; } return Arrays.asList(execAndWaitCommand(command).split("\r\n")); } public void mkdirs(String dir) { execAndWaitCommand("mkdir", "-p", dir); } public String createTmpFolder() { try { do { tmpFolder = DEFAULT_TMP_FOLDER + "/" + System.nanoTime(); } while (exists(tmpFolder)); execAndWaitCommand("mkdir", tmpFolder); } catch (IOException e) { tmpFolder = DEFAULT_TMP_FOLDER; } log.debug("Remote folder to store temporal files in node {}: {} ", host, tmpFolder); return tmpFolder; } public void getFile(String targetFile, String origFile) { log.debug("Getting remote file: {} (in host {}) to local file: {}", origFile, host, targetFile); OverthereFile motd = connection.getFile(origFile); if (!motd.isDirectory()) { InputStream is = motd.getInputStream(); try { Files.copy(is, Paths.get(targetFile), StandardCopyOption.REPLACE_EXISTING); is.close(); } catch (IOException e) { log.error("Exception getting file: {} to {} ({})", origFile, targetFile, e.getMessage()); } } } public void scp(String origFile, String targetFile) { log.debug("Copying local file: {} to remote file: {} (in host {})", origFile, targetFile, host); OverthereFile motd = connection.getFile(targetFile); OutputStream w = motd.getOutputStream(); try { byte[] origBytes = Files.readAllBytes(Paths.get(origFile)); w.write(origBytes); w.close(); } catch (IOException e) { throw new KurentoException("Exception in SCP " + origFile + " " + targetFile, e); } } public void start() { ConnectionOptions options = new ConnectionOptions(); if (pem != null) { options.set(SshConnectionBuilder.PRIVATE_KEY_FILE, pem); } else { options.set(ConnectionOptions.PASSWORD, passwd); } options.set(ConnectionOptions.CONNECTION_TIMEOUT_MILLIS, connectionTimeout); options.set(ConnectionOptions.USERNAME, login); options.set(ConnectionOptions.ADDRESS, host); options.set(ConnectionOptions.OPERATING_SYSTEM, OperatingSystemFamily.UNIX); options.set(SshConnectionBuilder.CONNECTION_TYPE, SshConnectionType.SCP); connection = Overthere.getConnection(SshConnectionBuilder.SSH_PROTOCOL, options); } public boolean isStarted() { return connection != null; } public void stop() { if (isStarted()) { connection.close(); connection = null; } } public void execCommand(final String... command) { if (connection.canStartProcess()) { connection.startProcess(CmdLine.build(command)); } } public int runAndWaitCommand(String... command) { return connection.execute(CmdLine.build(command)); } public String execAndWaitCommand(String... command) { log.debug("execAndWaitCommand: {} ", Arrays.toString(command)); CmdLine cmdLine = new CmdLine(); for (String c : command) { cmdLine.addRaw(c); } OverthereProcess process = connection.startProcess(cmdLine); StringBuilder sb = new StringBuilder(); try { BufferedReader r = new BufferedReader(new InputStreamReader(process.getStdout(), "UTF-8")); String line = null; while ((line = r.readLine()) != null) { log.debug(line); sb.append(line).append("\r\n"); } } catch (Exception e) { throw new KurentoException("Exception executing command " + Arrays.toString(command), e); } return sb.toString(); } public String execAndWaitCommandWithStderr(String... command) throws IOException { OverthereProcess process = connection.startProcess(CmdLine.build(command)); String result = CharStreams.toString(new InputStreamReader(process.getStdout(), "UTF-8")); result += CharStreams.toString(new InputStreamReader(process.getStderr(), "UTF-8")); return result; } public String execAndWaitCommandNoBr(String... command) { return execAndWaitCommand(command).replace("\n", "").replace("\r", ""); } public boolean exists(String fileOrFolder) throws IOException { String output = execAndWaitCommand("file", fileOrFolder); return !output.contains("ERROR"); } public int getFreePort() throws IOException { int port = NODE_INITIAL_PORT - 1; String output; do { port++; output = execAndWaitCommand("netstat", "-auxn"); } while (output.contains(":" + port)); return port; } public static boolean ping(String ipAddress) { return ping(ipAddress, SshConnection.PING_TIMEOUT); } public static boolean ping(final String ipAddress, int timeout) { final CountDownLatch latch = new CountDownLatch(1); Thread t = new Thread() { @Override public void run() { try { String[] command = { "ping", "-c", "1", ipAddress }; Process p = new ProcessBuilder(command).redirectErrorStream(true).start(); CharStreams.toString(new InputStreamReader(p.getInputStream(), "UTF-8")); latch.countDown(); } catch (Exception e) { // Intentionally left blank } } }; t.setDaemon(true); t.start(); boolean ping = false; try { ping = latch.await(timeout, TimeUnit.SECONDS); } catch (InterruptedException e) { log.error("Exception making ping to {} : {}", ipAddress, e.getClass()); } if (!ping) { t.interrupt(); } return ping; } public String getTmpFolder() { return tmpFolder; } public String getHost() { return host; } public String getPem() { return pem; } public void setPem(String pem) { this.pem = pem; } public OverthereConnection getConnection() { return connection; } public int getConnectionTimeout() { return connectionTimeout; } public void setConnectionTimeout(int connectionTimeout) { this.connectionTimeout = connectionTimeout; } }