blob: 3fb17a52180bd3841f866cd7c1195bac3b079145 [file] [log] [blame]
#!/usr/bin/env python
# Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Utility script for uploading files via SFTP.
Example Usage:
./sftp_uploader.py somefile.zip
./sftp_uploader.py -h localhost -p 22 -i private.key -u sftp-user somefile.zip
Run "./sftp_uploader.py -h" for a complete list of options.
Supports use of a private key for authentication to the remote server. The
private key must not require a password.
Requires the python-paramiko library be installed.
"""
import errno
import logging
import optparse
import os
import sys
try:
import paramiko # pylint: disable=F0401
except ImportError:
print ('Paramiko SSH2 library not found. Try something like: '
'sudo apt-get install python-paramiko')
sys.exit(1)
SFTP_SERVER = 'localhost'
SFTP_PORT = 22
SFTP_USER = 'sftp-user'
SFTP_IDENTITY_KEY = 'sftp-test-key'
REMOTE_DIR = ''
def ProgressCallback(bytes_transferred, bytes_total):
"""Periodically called by sftp.put() to display progress.
Args:
bytes_transferred: Int. Number of bytes already transferred.
bytes_total: Int. Total number of bytes to copy.
Returns:
None
"""
sys.stdout.write('%0.2f%% of %d bytes' %
(100 * (bytes_transferred / float(bytes_total)),
bytes_total))
sys.stdout.write('\r')
sys.stdout.flush()
def main():
"""Main entry point into script."""
def AlreadyUploaded(local_file):
"""Checks that a file of the same name and size is on the server.
Args:
local_file: String. Path to the local file.
Returns:
True if there is a matching file on the server.
"""
filename = os.path.basename(local_file)
remote_file = os.path.join(options.remote_dir, filename)
local_file_attr = os.stat(local_file)
try:
remote_file_attr = sftp.stat(remote_file)
except IOError as e:
if e.errno not in [errno.ENOENT, errno.EACCES]:
raise
remote_file_attr = None
return (remote_file_attr is not None and
local_file_attr.st_size == remote_file_attr.st_size)
USAGE = 'usage: %prog [options] [file]...'
parser = optparse.OptionParser(USAGE)
parser.add_option('-d', '--delete_local', dest='delete_local',
default=False, action='store_true',
help='Delete local files after upload.')
parser.add_option('-i', '--identity', dest='identity_key',
default=SFTP_IDENTITY_KEY,
help='Private key to authenticate to the server.')
parser.add_option('-p', '--port', dest='port', default=SFTP_PORT,
help='Port on SFTP server to connect with.')
parser.add_option('-r', '--remote_dir', dest='remote_dir', default=REMOTE_DIR,
help='Directory on the server to copy files to.')
parser.add_option('-s', '--server', dest='server', default=SFTP_SERVER,
help='Hostname of SFTP server to connect to.')
parser.add_option('-u', '--user', dest='user', default=SFTP_USER,
help='User name to connect to SFTP server with.')
parser.add_option('-v', '--verbose', dest='verbose',
default=False, action='store_true',
help='Increase level of logging detail.')
parser.set_usage(parser.format_help())
(options, args) = parser.parse_args()
log_level = logging.DEBUG if options.verbose else logging.INFO
log_format = '%(asctime)s %(levelname)s: %(message)s'
logging.basicConfig(level=log_level, format=log_format)
transport = paramiko.Transport((options.server, options.port))
key = paramiko.RSAKey.from_private_key_file(options.identity_key)
transport.connect(username=options.user, pkey=key)
sftp = paramiko.SFTPClient.from_transport(transport)
for local_file in args:
if not os.path.isfile(local_file):
logging.warning('%s is not a file. Skipping.', local_file)
continue
filename = os.path.basename(local_file)
if AlreadyUploaded(local_file):
logging.info('%s: Remote server has matching file of the same '
'name and size. Skipping upload.', filename)
else:
logging.info('Uploading: %s', local_file)
remote_file = os.path.join(options.remote_dir, filename)
callback = ProgressCallback if sys.stdout.isatty() else None
try:
sftp.put(local_file, remote_file, callback=callback)
except IOError:
logging.exception('Error uploading file to server as %s', remote_file)
continue
if options.delete_local and AlreadyUploaded(local_file):
try:
os.remove(local_file)
logging.info('Deleted local file: %s', local_file)
except OSError:
logging.exception('Unable to delete local file: %s', local_file)
continue
sftp.close()
transport.close()
if __name__ == '__main__':
main()