summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRoss Brattain <ross.b.brattain@intel.com>2016-12-20 21:40:52 -0800
committerRoss Brattain <ross.b.brattain@intel.com>2016-12-20 21:47:37 -0800
commit48a7b4fa8a9cfa2db8c002ffb68c46345551ee2a (patch)
tree717c748b93e58c18aa7e790441727171cd50bfe5
parentcd34d540ce1d5da5ba5df0a2d169013b5b222418 (diff)
ssh: don't quote ~ in remotepaths
~ is not expanded in double quotes, so we have a dilemma. We need to quote in order to preserve filenames with spaces, but we have to make sure we don't quote the ~ so it can be expanded. To resolve this we use a regex to search for tidle-prefixes and excluded them from quotes. Added unittests for the cases: path with tilde path with space path with tilde and space see bash man page for details of tidle expansion Tilde Expansion If a word begins with an unquoted tilde character (`~'), all of the characters preceding the first unquoted slash (or all characters, if there is no unquoted slash) are considered a tilde-prefix. If none of the characters in the tilde-prefix are quoted, the characters in the tilde-prefix following the tilde are treated as a possible login name. If this login name is the null string, the tilde is replaced with the value of the shell parameter HOME. If HOME is unset, the home directory of the user executing the shell is substituted instead. Otherwise, the tilde-prefix is replaced with the home directory associated with the specified login name. JIRA: YARDSTICK-501 Change-Id: I324be20aba0dbd50434fbd8081685c598ebd8a84 Signed-off-by: Ross Brattain <ross.b.brattain@intel.com>
-rw-r--r--tests/unit/test_ssh.py36
-rw-r--r--yardstick/ssh.py16
2 files changed, 41 insertions, 11 deletions
diff --git a/tests/unit/test_ssh.py b/tests/unit/test_ssh.py
index 8b828ed7c..045ac0f1b 100644
--- a/tests/unit/test_ssh.py
+++ b/tests/unit/test_ssh.py
@@ -310,12 +310,38 @@ class SSHRunTestCase(unittest.TestCase):
@mock.patch("yardstick.ssh.open", create=True)
def test__put_file_shell(self, mock_open):
- self.test_client.run = mock.Mock()
- self.test_client._put_file_shell("localfile", "remotefile", 0o42)
+ with mock.patch.object(self.test_client, "run") as run_mock:
+ self.test_client._put_file_shell("localfile", "remotefile", 0o42)
+ run_mock.assert_called_once_with(
+ 'cat > "remotefile"&& chmod -- 042 "remotefile"',
+ stdin=mock_open.return_value.__enter__.return_value)
- self.test_client.run.assert_called_once_with(
- 'cat > remotefile && chmod -- 042 remotefile',
- stdin=mock_open.return_value.__enter__.return_value)
+ @mock.patch("yardstick.ssh.open", create=True)
+ def test__put_file_shell_space(self, mock_open):
+ with mock.patch.object(self.test_client, "run") as run_mock:
+ self.test_client._put_file_shell("localfile",
+ "filename with space", 0o42)
+ run_mock.assert_called_once_with(
+ 'cat > "filename with space"&& chmod -- 042 "filename with '
+ 'space"',
+ stdin=mock_open.return_value.__enter__.return_value)
+
+ @mock.patch("yardstick.ssh.open", create=True)
+ def test__put_file_shell_tilde(self, mock_open):
+ with mock.patch.object(self.test_client, "run") as run_mock:
+ self.test_client._put_file_shell("localfile", "~/remotefile", 0o42)
+ run_mock.assert_called_once_with(
+ 'cat > ~/"remotefile"&& chmod -- 042 ~/"remotefile"',
+ stdin=mock_open.return_value.__enter__.return_value)
+
+ @mock.patch("yardstick.ssh.open", create=True)
+ def test__put_file_shell_tilde_spaces(self, mock_open):
+ with mock.patch.object(self.test_client, "run") as run_mock:
+ self.test_client._put_file_shell("localfile", "~/file with space",
+ 0o42)
+ run_mock.assert_called_once_with(
+ 'cat > ~/"file with space"&& chmod -- 042 ~/"file with space"',
+ stdin=mock_open.return_value.__enter__.return_value)
@mock.patch("yardstick.ssh.os.stat")
def test__put_file_sftp(self, mock_stat):
diff --git a/yardstick/ssh.py b/yardstick/ssh.py
index 3081001b6..927ca94db 100644
--- a/yardstick/ssh.py
+++ b/yardstick/ssh.py
@@ -66,6 +66,7 @@ import os
import select
import socket
import time
+import re
import logging
import paramiko
@@ -252,7 +253,7 @@ class SSH(object):
raise SSHError("Socket error.")
exit_status = session.recv_exit_status()
- if 0 != exit_status and raise_on_error:
+ if exit_status != 0 and raise_on_error:
fmt = "Command '%(cmd)s' failed with exit_status %(status)d."
details = fmt % {"cmd": cmd, "status": exit_status}
if stderr_data:
@@ -311,17 +312,21 @@ class SSH(object):
mode = 0o777 & os.stat(localpath).st_mode
sftp.chmod(remotepath, mode)
+ TILDE_EXPANSIONS_RE = re.compile("(^~[^/]*/)?(.*)")
+
def _put_file_shell(self, localpath, remotepath, mode=None):
# quote to stop wordpslit
- cmd = ['cat > %s' % remotepath]
+ tilde, remotepath = self.TILDE_EXPANSIONS_RE.match(remotepath).groups()
+ if not tilde:
+ tilde = ''
+ cmd = ['cat > %s"%s"' % (tilde, remotepath)]
if mode is not None:
# use -- so no options
- cmd.append('chmod -- 0%o %s' % (mode, remotepath))
+ cmd.append('chmod -- 0%o %s"%s"' % (mode, tilde, remotepath))
with open(localpath, "rb") as localfile:
# only chmod on successful cat
- cmd = " && ".join(cmd)
- self.run(cmd, stdin=localfile)
+ self.run("&& ".join(cmd), stdin=localfile)
def put_file(self, localpath, remotepath, mode=None):
"""Copy specified local file to the server.
@@ -330,7 +335,6 @@ class SSH(object):
:param remotepath: Remote filename.
:param mode: Permissions to set after upload
"""
- import socket
try:
self._put_file_sftp(localpath, remotepath, mode=mode)
except (paramiko.SSHException, socket.error):