Skip to content

Commit

Permalink
Added "owner" and "mode" implementation in scp
Browse files Browse the repository at this point in the history
  • Loading branch information
Roman Imankulov committed Apr 25, 2012
1 parent da616e3 commit 5494d08
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 5 deletions.
6 changes: 4 additions & 2 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -55,13 +55,15 @@ forwarding ::


And finally there is a sample which shows how to copy a file from local to
remote machine ::
remote machine. You can also define owner and mode of the target ::

>>> fd = open('test.txt', 'w')
>>> fd.write('Hello world')
>>> fd.close()
>>> from openssh_wrapper import SSHConnection
>>> conn = SSHConnection('localhost', login='root')
>>> conn.scp(('test.txt', ), target='/tmp')
>>> conn.scp(('test.txt', ), target='/tmp', mode='0666', owner='nobody:')
>>> print conn.run('cat /tmp/test.txt').stdout
Hello world
>>> print conn.run('ls -l /tmp/test.txt').stdout
-rw-rw-rw- 1 nobody nogroup ... /tmp/test.txt
39 changes: 38 additions & 1 deletion openssh_wrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"""
This is a wrapper around the openssh binaries ssh and scp.
"""
import re, os, subprocess, signal
import re, os, subprocess, signal, pipes

__all__ = 'SSHConnection SSHResult SSHError'.split()

Expand Down Expand Up @@ -148,6 +148,43 @@ def scp(self, files, target, mode=None, owner=None):
if returncode != 0: # ssh client error
raise SSHError(err.strip())

if mode or owner:
targets = self.get_scp_targets(files, target) # XXX: files VS filenames
if mode:
cmd_chunks = ['chmod', mode] + targets
cmd = ' '.join([pipes.quote(chunk) for chunk in cmd_chunks])
result = self.run(cmd)
if result.returncode:
raise SSHError(result.stderr.strip())
if owner:
cmd_chunks = ['chown', owner] + targets
cmd = ' '.join([pipes.quote(chunk) for chunk in cmd_chunks])
result = self.run(cmd)
if result.returncode:
raise SSHError(result.stderr.strip())



def get_scp_targets(self, filenames, target):
"""
Given a list of filenames and a target name return the full list of targets
Internal command which is used to perform chmod and chown.
For example, get_scp_targets(['foo.txt', 'bar.txt'], '/etc') returns ['/etc/foo.txt', '/etc/bar.txt'],
whereas get_scp_targets(['foo.txt', ], '/etc/passwd') returns ['/etc/passwd', ]
"""
result = self.run('test -d %s' % pipes.quote(target))
is_directory = result.returncode == 0
if is_directory:
ret = []
for filename in filenames:
ret.append(os.path.join(target, os.path.basename(filename)))
return ret
else:
return [target, ]


def ssh_command(self, interpreter, forward_ssh_agent):
""" Build the command string to connect to the server
and start the given interpreter. """
Expand Down
26 changes: 24 additions & 2 deletions tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from openssh_wrapper import *
from nose.tools import *

test_file = os.path.join(os.path.dirname(__file__), 'tests.py')

class TestSSHCommandNames(object):

Expand All @@ -22,6 +23,12 @@ def test_scp_multiple_files(self):
eq_(self.c.scp_command(('/tmp/1.txt', '2.txt'), target='/home/username/'),
['/usr/bin/scp', '-q', '-r', '-F', 'ssh_config.test', '/tmp/1.txt', '2.txt', 'root@localhost:/home/username/'])

def test_scp_targets(self):
targets = self.c.get_scp_targets(['foo.txt', 'bar.txt'], '/etc')
eq_(targets, ['/etc/foo.txt', '/etc/bar.txt'])
targets = self.c.get_scp_targets(['foo.txt'], '/etc/passwd')
eq_(targets, ['/etc/passwd'])

def test_simple_command(self):
result = self.c.run('whoami')
eq_(result.stdout, 'root')
Expand Down Expand Up @@ -50,11 +57,26 @@ class TestSCP(object):

def setUp(self):
self.c = SSHConnection('localhost', login='root')
self.c.run('rm -f /tmp/*.py')

def test_scp(self):
self.c.scp((__file__, ), target='/tmp')
self.c.scp((test_file, ), target='/tmp')
ok_(os.path.isfile('/tmp/tests.py'))

@raises(SSHError)
def test_scp_to_nonexistent_dir(self):
self.c.scp((__file__, ), target='/abc/def/')
self.c.scp((test_file, ), target='/abc/def/')

def test_mode(self):
self.c.scp((test_file, ), target='/tmp', mode='0666')
mode = os.stat('/tmp/tests.py').st_mode & 0777
eq_(mode, 0666)

def test_owner(self):
import pwd, grp
uid, gid = os.getuid(), os.getgid()
user, group = pwd.getpwuid(uid).pw_name, grp.getgrgid(gid).gr_name
self.c.scp((test_file, ), target='/tmp', owner='%s:%s' % (user, group))
stat = os.stat('/tmp/tests.py')
eq_(stat.st_uid, uid)
eq_(stat.st_gid, gid)

0 comments on commit 5494d08

Please sign in to comment.