Skip to content

Commit 6fc8f60

Browse files
author
Dan
committed
Made mkdir create parent paths correctly - Resolves #41 . Added unittests for mkdir for relative path, absolute paths and multiple missing parent dirs
1 parent 1e09e56 commit 6fc8f60

File tree

2 files changed

+90
-12
lines changed

2 files changed

+90
-12
lines changed

pssh/ssh_client.py

Lines changed: 34 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,7 @@ def _make_sftp(self):
239239
transport.open_session()
240240
return paramiko.SFTPClient.from_transport(transport)
241241

242-
def mkdir(self, sftp, directory):
242+
def _mkdir(self, sftp, directory):
243243
"""Make directory via SFTP channel
244244
245245
:param sftp: SFTP client object
@@ -249,19 +249,41 @@ def mkdir(self, sftp, directory):
249249
250250
Catches and logs at error level remote IOErrors on creating directory.
251251
"""
252-
sub_dirs = [_dir for _dir in directory.split(os.path.sep) if _dir][:-1]
253-
sub_dirs = os.path.sep + os.path.sep.join(sub_dirs) if directory.startswith(os.path.sep) \
254-
else os.path.sep.join(sub_dirs)
255-
if sub_dirs:
256-
try:
257-
sftp.stat(sub_dirs)
258-
except IOError:
259-
return self.mkdir(sftp, sub_dirs)
260252
try:
261253
sftp.mkdir(directory)
262254
except IOError, error:
263255
logger.error("Error occured creating directory %s on %s - %s",
264256
directory, self.host, error)
257+
logger.debug("Creating remote directory %s", directory)
258+
return True
259+
260+
def mkdir(self, sftp, directory):
261+
"""Make directory via SFTP channel.
262+
263+
Parent paths in the directory are created if they do not exist.
264+
265+
:param sftp: SFTP client object
266+
:type sftp: :mod:`paramiko.SFTPClient`
267+
:param directory: Remote directory to create
268+
:type directory: str
269+
270+
Catches and logs at error level remote IOErrors on creating directory.
271+
"""
272+
try:
273+
parent_path, sub_dirs = directory.split(os.path.sep, 1)
274+
except ValueError:
275+
parent_path = directory.split(os.path.sep, 1)[0]
276+
sub_dirs = None
277+
if not parent_path and directory.startswith(os.path.sep):
278+
parent_path, sub_dirs = sub_dirs.split(os.path.sep, 1)
279+
try:
280+
sftp.stat(parent_path)
281+
except IOError:
282+
self._mkdir(sftp, parent_path)
283+
sftp.chdir(parent_path)
284+
if sub_dirs:
285+
return self.mkdir(sftp, sub_dirs)
286+
return True
265287

266288
def copy_file(self, local_file, remote_file):
267289
"""Copy local file to host via SFTP/SCP
@@ -276,14 +298,14 @@ def copy_file(self, local_file, remote_file):
276298
"""
277299
sftp = self._make_sftp()
278300
destination = [_dir for _dir in remote_file.split(os.path.sep)
279-
if _dir][:-1]
301+
if _dir][:-1][0]
280302
if remote_file.startswith(os.path.sep):
281-
destination[0] = os.path.sep + destination[0]
282-
# import ipdb; ipdb.set_trace()
303+
destination = os.path.sep + destination
283304
try:
284305
sftp.stat(destination)
285306
except IOError:
286307
self.mkdir(sftp, destination)
308+
sftp.chdir()
287309
try:
288310
sftp.put(local_file, remote_file)
289311
except Exception, error:

tests/test_ssh_client.py

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import os
3434
from test_pssh_client import USER_KEY
3535
import random, string
36+
import shutil
3637

3738
USER_KEY = paramiko.RSAKey.from_private_key_file(
3839
os.path.sep.join([os.path.dirname(__file__), 'test_client_private_key']))
@@ -51,6 +52,61 @@ def setUp(self):
5152
def tearDown(self):
5253
del self.server
5354
del self.listen_socket
55+
56+
def test_ssh_client_mkdir_recursive(self):
57+
"""Test SFTP mkdir of SSHClient"""
58+
base_path = 'remote_test_dir1'
59+
remote_dir = os.path.sep.join([base_path,
60+
'remote_test_dir2',
61+
'remote_test_dir3'])
62+
try:
63+
shutil.rmtree(base_path)
64+
except OSError:
65+
pass
66+
client = SSHClient(self.host, port=self.listen_port,
67+
pkey=self.user_key)
68+
client.mkdir(client._make_sftp(), remote_dir)
69+
self.assertTrue(os.path.isdir(remote_dir),
70+
msg="SFTP recursive mkdir failed")
71+
shutil.rmtree(base_path)
72+
del client
73+
74+
def test_ssh_client_mkdir_recursive_abspath(self):
75+
"""Test SFTP mkdir of SSHClient with absolute path
76+
77+
Absolute SFTP paths resolve under the users' home directory,
78+
not the root filesystem
79+
"""
80+
base_path = 'tmp'
81+
remote_dir = os.path.sep.join([base_path,
82+
'remote_test_dir2',
83+
'remote_test_dir3'])
84+
try:
85+
shutil.rmtree(base_path)
86+
except OSError:
87+
pass
88+
client = SSHClient(self.host, port=self.listen_port,
89+
pkey=self.user_key)
90+
client.mkdir(client._make_sftp(), '/' + remote_dir)
91+
self.assertTrue(os.path.isdir(remote_dir),
92+
msg="SFTP recursive mkdir failed")
93+
shutil.rmtree(base_path)
94+
del client
95+
96+
def test_ssh_client_mkdir_single(self):
97+
"""Test SFTP mkdir of SSHClient"""
98+
remote_dir = 'remote_test_dir1'
99+
try:
100+
shutil.rmtree(remote_dir)
101+
except OSError:
102+
pass
103+
client = SSHClient(self.host, port=self.listen_port,
104+
pkey=self.user_key)
105+
client.mkdir(client._make_sftp(), remote_dir)
106+
self.assertTrue(os.path.isdir(remote_dir),
107+
msg="SFTP recursive mkdir failed")
108+
shutil.rmtree(remote_dir)
109+
del client
54110

55111
def test_ssh_client_sftp(self):
56112
"""Test SFTP features of SSHClient. Copy local filename to server,

0 commit comments

Comments
 (0)