[
https://issues.apache.org/jira/browse/CLOUDSTACK-8611?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=15225540#comment-15225540
]
ASF GitHub Bot commented on CLOUDSTACK-8611:
--------------------------------------------
Github user rafaelweingartner commented on a diff in the pull request:
https://github.com/apache/cloudstack/pull/1459#discussion_r58478990
--- Diff: utils/src/main/java/com/cloud/utils/ssh/SshHelper.java ---
@@ -1,209 +1,306 @@
-//
-// Licensed to the Apache Software Foundation (ASF) under one
-// or more contributor license agreements. See the NOTICE file
-// distributed with this work for additional information
-// regarding copyright ownership. The ASF 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 com.cloud.utils.ssh;
-
-import java.io.File;
-import java.io.InputStream;
-
-import org.apache.log4j.Logger;
-
-import com.trilead.ssh2.ChannelCondition;
-
-import com.cloud.utils.Pair;
-
-public class SshHelper {
- private static final int DEFAULT_CONNECT_TIMEOUT = 180000;
- private static final int DEFAULT_KEX_TIMEOUT = 60000;
-
- private static final Logger s_logger =
Logger.getLogger(SshHelper.class);
-
- public static Pair<Boolean, String> sshExecute(String host, int port,
String user, File pemKeyFile, String password, String command) throws Exception
{
-
- return sshExecute(host, port, user, pemKeyFile, password, command,
DEFAULT_CONNECT_TIMEOUT, DEFAULT_KEX_TIMEOUT, 120000);
- }
-
- public static void scpTo(String host, int port, String user, File
pemKeyFile, String password, String remoteTargetDirectory, String localFile,
String fileMode)
- throws Exception {
-
- scpTo(host, port, user, pemKeyFile, password,
remoteTargetDirectory, localFile, fileMode, DEFAULT_CONNECT_TIMEOUT,
DEFAULT_KEX_TIMEOUT);
- }
-
- public static void scpTo(String host, int port, String user, File
pemKeyFile, String password, String remoteTargetDirectory, byte[] data, String
remoteFileName,
- String fileMode) throws Exception {
-
- scpTo(host, port, user, pemKeyFile, password,
remoteTargetDirectory, data, remoteFileName, fileMode, DEFAULT_CONNECT_TIMEOUT,
DEFAULT_KEX_TIMEOUT);
- }
-
- public static void scpTo(String host, int port, String user, File
pemKeyFile, String password, String remoteTargetDirectory, String localFile,
String fileMode,
- int connectTimeoutInMs, int kexTimeoutInMs) throws Exception {
-
- com.trilead.ssh2.Connection conn = null;
- com.trilead.ssh2.SCPClient scpClient = null;
-
- try {
- conn = new com.trilead.ssh2.Connection(host, port);
- conn.connect(null, connectTimeoutInMs, kexTimeoutInMs);
-
- if (pemKeyFile == null) {
- if (!conn.authenticateWithPassword(user, password)) {
- String msg = "Failed to authentication SSH user " +
user + " on host " + host;
- s_logger.error(msg);
- throw new Exception(msg);
- }
- } else {
- if (!conn.authenticateWithPublicKey(user, pemKeyFile,
password)) {
- String msg = "Failed to authentication SSH user " +
user + " on host " + host;
- s_logger.error(msg);
- throw new Exception(msg);
- }
- }
-
- scpClient = conn.createSCPClient();
-
- if (fileMode != null)
- scpClient.put(localFile, remoteTargetDirectory, fileMode);
- else
- scpClient.put(localFile, remoteTargetDirectory);
- } finally {
- if (conn != null)
- conn.close();
- }
- }
-
- public static void scpTo(String host, int port, String user, File
pemKeyFile, String password, String remoteTargetDirectory, byte[] data, String
remoteFileName,
- String fileMode, int connectTimeoutInMs, int kexTimeoutInMs)
throws Exception {
-
- com.trilead.ssh2.Connection conn = null;
- com.trilead.ssh2.SCPClient scpClient = null;
-
- try {
- conn = new com.trilead.ssh2.Connection(host, port);
- conn.connect(null, connectTimeoutInMs, kexTimeoutInMs);
-
- if (pemKeyFile == null) {
- if (!conn.authenticateWithPassword(user, password)) {
- String msg = "Failed to authentication SSH user " +
user + " on host " + host;
- s_logger.error(msg);
- throw new Exception(msg);
- }
- } else {
- if (!conn.authenticateWithPublicKey(user, pemKeyFile,
password)) {
- String msg = "Failed to authentication SSH user " +
user + " on host " + host;
- s_logger.error(msg);
- throw new Exception(msg);
- }
- }
-
- scpClient = conn.createSCPClient();
- if (fileMode != null)
- scpClient.put(data, remoteFileName, remoteTargetDirectory,
fileMode);
- else
- scpClient.put(data, remoteFileName, remoteTargetDirectory);
- } finally {
- if (conn != null)
- conn.close();
- }
- }
-
- public static Pair<Boolean, String> sshExecute(String host, int port,
String user, File pemKeyFile, String password, String command, int
connectTimeoutInMs,
- int kexTimeoutInMs, int waitResultTimeoutInMs) throws Exception {
-
- com.trilead.ssh2.Connection conn = null;
- com.trilead.ssh2.Session sess = null;
- try {
- conn = new com.trilead.ssh2.Connection(host, port);
- conn.connect(null, connectTimeoutInMs, kexTimeoutInMs);
-
- if (pemKeyFile == null) {
- if (!conn.authenticateWithPassword(user, password)) {
- String msg = "Failed to authentication SSH user " +
user + " on host " + host;
- s_logger.error(msg);
- throw new Exception(msg);
- }
- } else {
- if (!conn.authenticateWithPublicKey(user, pemKeyFile,
password)) {
- String msg = "Failed to authentication SSH user " +
user + " on host " + host;
- s_logger.error(msg);
- throw new Exception(msg);
- }
- }
- sess = conn.openSession();
-
- sess.execCommand(command);
-
- InputStream stdout = sess.getStdout();
- InputStream stderr = sess.getStderr();
-
- byte[] buffer = new byte[8192];
- StringBuffer sbResult = new StringBuffer();
-
- int currentReadBytes = 0;
- while (true) {
- if ((stdout.available() == 0) && (stderr.available() ==
0)) {
- int conditions =
- sess.waitForCondition(ChannelCondition.STDOUT_DATA
| ChannelCondition.STDERR_DATA | ChannelCondition.EOF |
ChannelCondition.EXIT_STATUS,
- waitResultTimeoutInMs);
-
- if ((conditions & ChannelCondition.TIMEOUT) != 0) {
- String msg = "Timed out in waiting SSH execution
result";
- s_logger.error(msg);
- throw new Exception(msg);
- }
-
- if ((conditions & ChannelCondition.EXIT_STATUS) != 0) {
- if ((conditions & (ChannelCondition.STDOUT_DATA |
ChannelCondition.STDERR_DATA)) == 0) {
- break;
- }
- }
- }
-
- while (stdout.available() > 0) {
- currentReadBytes = stdout.read(buffer);
- sbResult.append(new String(buffer, 0,
currentReadBytes));
- }
-
- while (stderr.available() > 0) {
- currentReadBytes = stderr.read(buffer);
- sbResult.append(new String(buffer, 0,
currentReadBytes));
- }
- }
-
- String result = sbResult.toString();
-
- if (sess.getExitStatus() == null) {
- //Exit status is NOT available. Returning failure result.
- return new Pair<Boolean, String>(false, result);
- }
-
- if (sess.getExitStatus() != null &&
sess.getExitStatus().intValue() != 0) {
- s_logger.error("SSH execution of command " + command + "
has an error status code in return. result output: " + result);
- return new Pair<Boolean, String>(false, result);
- }
-
- return new Pair<Boolean, String>(true, result);
- } finally {
- if (sess != null)
- sess.close();
-
- if (conn != null)
- conn.close();
- }
- }
-}
+//
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF 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 com.cloud.utils.ssh;
+
+import java.io.File;
+import java.io.InputStream;
+
+import org.apache.log4j.Logger;
+
+import com.trilead.ssh2.ChannelCondition;
+
+import com.cloud.utils.Pair;
+
+public class SshHelper {
+ private static final int DEFAULT_CONNECT_TIMEOUT = 180000;
+ private static final int DEFAULT_KEX_TIMEOUT = 60000;
+
+ /**
+ * Waiting time to check if the SSH session was successfully opened.
This value (of 1000
+ * milliseconds) represents one (1) second.
+ */
+ private static final int WAITING_OPEN_SSH_SESSION = 1000;
+
+ private static final Logger s_logger =
Logger.getLogger(SshHelper.class);
+
+ public static Pair<Boolean, String> sshExecute(String host, int port,
String user, File pemKeyFile, String password, String command) throws Exception
{
+
+ return sshExecute(host, port, user, pemKeyFile, password, command,
DEFAULT_CONNECT_TIMEOUT, DEFAULT_KEX_TIMEOUT, 120000);
+ }
+
+ public static void scpTo(String host, int port, String user, File
pemKeyFile, String password, String remoteTargetDirectory, String localFile,
String fileMode)
+ throws Exception {
+
+ scpTo(host, port, user, pemKeyFile, password,
remoteTargetDirectory, localFile, fileMode, DEFAULT_CONNECT_TIMEOUT,
DEFAULT_KEX_TIMEOUT);
+ }
+
+ public static void scpTo(String host, int port, String user, File
pemKeyFile, String password, String remoteTargetDirectory, byte[] data, String
remoteFileName,
+ String fileMode) throws Exception {
+
+ scpTo(host, port, user, pemKeyFile, password,
remoteTargetDirectory, data, remoteFileName, fileMode, DEFAULT_CONNECT_TIMEOUT,
DEFAULT_KEX_TIMEOUT);
+ }
+
+ public static void scpTo(String host, int port, String user, File
pemKeyFile, String password, String remoteTargetDirectory, String localFile,
String fileMode,
+ int connectTimeoutInMs, int kexTimeoutInMs) throws Exception {
+
+ com.trilead.ssh2.Connection conn = null;
+ com.trilead.ssh2.SCPClient scpClient = null;
+
+ try {
+ conn = new com.trilead.ssh2.Connection(host, port);
+ conn.connect(null, connectTimeoutInMs, kexTimeoutInMs);
+
+ if (pemKeyFile == null) {
+ if (!conn.authenticateWithPassword(user, password)) {
+ String msg = "Failed to authentication SSH user " +
user + " on host " + host;
+ s_logger.error(msg);
+ throw new Exception(msg);
+ }
+ } else {
+ if (!conn.authenticateWithPublicKey(user, pemKeyFile,
password)) {
+ String msg = "Failed to authentication SSH user " +
user + " on host " + host;
+ s_logger.error(msg);
+ throw new Exception(msg);
+ }
+ }
+
+ scpClient = conn.createSCPClient();
+
+ if (fileMode != null)
+ scpClient.put(localFile, remoteTargetDirectory, fileMode);
+ else
+ scpClient.put(localFile, remoteTargetDirectory);
+ } finally {
+ if (conn != null)
+ conn.close();
+ }
+ }
+
+ public static void scpTo(String host, int port, String user, File
pemKeyFile, String password, String remoteTargetDirectory, byte[] data, String
remoteFileName,
+ String fileMode, int connectTimeoutInMs, int kexTimeoutInMs)
throws Exception {
+
+ com.trilead.ssh2.Connection conn = null;
+ com.trilead.ssh2.SCPClient scpClient = null;
+
+ try {
+ conn = new com.trilead.ssh2.Connection(host, port);
+ conn.connect(null, connectTimeoutInMs, kexTimeoutInMs);
+
+ if (pemKeyFile == null) {
+ if (!conn.authenticateWithPassword(user, password)) {
+ String msg = "Failed to authentication SSH user " +
user + " on host " + host;
+ s_logger.error(msg);
+ throw new Exception(msg);
+ }
+ } else {
+ if (!conn.authenticateWithPublicKey(user, pemKeyFile,
password)) {
+ String msg = "Failed to authentication SSH user " +
user + " on host " + host;
+ s_logger.error(msg);
+ throw new Exception(msg);
+ }
+ }
+
+ scpClient = conn.createSCPClient();
+ if (fileMode != null)
+ scpClient.put(data, remoteFileName, remoteTargetDirectory,
fileMode);
+ else
+ scpClient.put(data, remoteFileName, remoteTargetDirectory);
+ } finally {
+ if (conn != null)
+ conn.close();
+ }
+ }
+
+ public static Pair<Boolean, String> sshExecute(String host, int port,
String user, File pemKeyFile, String password, String command, int
connectTimeoutInMs,
+ int kexTimeoutInMs, int waitResultTimeoutInMs) throws
Exception {
+
+ com.trilead.ssh2.Connection conn = null;
+ com.trilead.ssh2.Session sess = null;
+ try {
+ conn = new com.trilead.ssh2.Connection(host, port);
+ conn.connect(null, connectTimeoutInMs, kexTimeoutInMs);
+
+ if (pemKeyFile == null) {
+ if (!conn.authenticateWithPassword(user, password)) {
+ String msg = "Failed to authentication SSH user " +
user + " on host " + host;
+ s_logger.error(msg);
+ throw new Exception(msg);
+ }
+ } else {
+ if (!conn.authenticateWithPublicKey(user, pemKeyFile,
password)) {
+ String msg = "Failed to authentication SSH user " +
user + " on host " + host;
+ s_logger.error(msg);
+ throw new Exception(msg);
+ }
+ }
+ sess = conn.openSession();
+
+ throwSshExceptionIfSshConnectionIsNull(sess);
+
+ sess.execCommand(command);
+
+ InputStream stdout = sess.getStdout();
+ InputStream stderr = sess.getStderr();
+
+ byte[] buffer = new byte[8192];
+ StringBuffer sbResult = new StringBuffer();
+
+ int currentReadBytes = 0;
+ while (true) {
+ throwSshExceptionIfStdoutOrStdeerIsNull(stdout, stderr);
+
+ if ((stdout.available() == 0) && (stderr.available() ==
0)) {
+ int conditions =
+
sess.waitForCondition(ChannelCondition.STDOUT_DATA |
ChannelCondition.STDERR_DATA | ChannelCondition.EOF |
ChannelCondition.EXIT_STATUS,
+ waitResultTimeoutInMs);
+
+ throwSshExceptionIfConditionsTimeout(conditions);
+
+ if ((conditions & ChannelCondition.EXIT_STATUS) != 0) {
+ break;
+ }
+
+ if (canEndTheSshConnection(waitResultTimeoutInMs,
sess, conditions)) {
+ break;
+ }
+
+ }
+
+ while (stdout.available() > 0) {
+ currentReadBytes = stdout.read(buffer);
+ sbResult.append(new String(buffer, 0,
currentReadBytes));
+ }
+
+ while (stderr.available() > 0) {
+ currentReadBytes = stderr.read(buffer);
+ sbResult.append(new String(buffer, 0,
currentReadBytes));
+ }
+ }
+
+ String result = sbResult.toString();
+
+ if (sess.getExitStatus() == null) {
+ //Exit status is NOT available. Returning failure result.
+ s_logger.error(String.format("SSH execution of command %s
has no exit status set. Result output: %s", command, result));
+ return new Pair<Boolean, String>(false, result);
+ }
+
+ if (sess.getExitStatus() != null &&
sess.getExitStatus().intValue() != 0) {
+ s_logger.error(String.format("SSH execution of command %s
has an error status code in return. Result output: %s", command, result));
+ return new Pair<Boolean, String>(false, result);
+ }
+
+ return new Pair<Boolean, String>(true, result);
+ } finally {
+ if (sess != null)
+ sess.close();
+
+ if (conn != null)
+ conn.close();
+ }
+ }
+
+ /**
+ * Handles the SSH connection in case of timeout or exit. If the
session is with the timeout
+ * condition it throws an exception; if the channel reach an end of
file condition but yet does
+ * not send the exit status, then it return true to break the loop;
otherwise, it returns false.
+ *
+ * @param waitResultTimeoutInMs
+ * @param sess
+ * @param conditions
+ * @return boolean
+ * @throws SshException
+ */
+ protected static boolean canEndTheSshConnection(int
waitResultTimeoutInMs, com.trilead.ssh2.Session sess, int conditions) throws
SshException {
--- End diff --
what about an integration test for this one?
integration test != functional tests that test a system functionality.
> CS waits indefinitely for CheckS2SVpnConnectionsCommand to return
> -----------------------------------------------------------------
>
> Key: CLOUDSTACK-8611
> URL: https://issues.apache.org/jira/browse/CLOUDSTACK-8611
> Project: CloudStack
> Issue Type: Bug
> Security Level: Public(Anyone can view this level - this is the
> default.)
> Reporter: Likitha Shetty
> Assignee: Suresh Kumar Anaparti
> Fix For: 4.6.1
>
>
> On one instance, CS began to execute CheckS2SVpnConnectionsCommand command on
> a router but the command result was never returned to the MS. If a command
> never returns, then 'DirectAgent' thread executing this command is blocked
> indefinitely and cannot pick up any other request.
> Now since this command is designed to execute in sequence on a host and is
> run regularly, every execution of that command thereafter on that particular
> host ended up picking up a DirectAgent thread and waiting for the previous
> execution to complete. And hence overtime, the host ended up using and
> blocking all 'DirectAgent' threads indefinitely.
--
This message was sent by Atlassian JIRA
(v6.3.4#6332)