Source code for penchy.deploy
"""
Library which abstracts SFTP and FTP connections
.. moduleauthor:: Fabian Hirschmann <fabian@hirschmann.email>
:copyright: PenchY Developers 2011-2012, see AUTHORS
:license: MIT License, see LICENSE
"""
import os
import logging
import shutil
from contextlib import contextmanager
from penchy import is_server
if is_server:
import ftplib
import paramiko
log = logging.getLogger(__name__)
[docs]class Deploy(object):
"""
Base class from which all deployment methods must
inherit from.
"""
def __init__(self, hostname, username, password, port=None):
"""
:param hostname: hostname of the host
:type hostname: str
:param username: username on the host
:type username: str
:param password: password on the host
:type password: str
:param port: port of the service
:type port: int
"""
self.hostname = hostname
self.username = username
self.password = password
self.port = port
[docs] def connect(self):
"""
Establish connection to this host.
"""
raise NotImplementedError("connect must be implemented")
[docs] def disconnect(self):
"""
Disconnect from this host.
"""
raise NotImplementedError("disconnect must be implemented")
[docs] def put(self, local, remote):
"""
Upload a file to this host.
:param local: file to upload
:type local: str
:param remote: destination to upload to
:type remote: str
"""
raise NotImplementedError("put must be implemented")
@property
def connected(self):
"""
Indicates if we are connected to the host.
"""
raise NotImplementedError("connected must be implemented")
@contextmanager
[docs] def connection_required(self):
"""
Contextmanager to make sure we are connected before
uploading to this host.
"""
if not self.connected:
self.connect()
yield
if self.connected:
self.disconnect()
[docs]class FTPDeploy(Deploy):
"""
Provides communication with a FTP Server.
"""
def __init__(self, *args, **kwargs):
"""
:param hostname: hostname of the host
:type hostname: str
:param username: username on the host
:type username: str
:param password: password on the host
:type password: str
:param port: port of the service (defaults to 21)
:type port: int
:param tls: use TLS support as described in RFC 4217 (defaults to True)
:type tls: boolean
:param passive: ftp passive mode (defaults to True)
:type passive: boolean
"""
tls = kwargs.pop('tls') if 'tls' in kwargs else False
passive = kwargs.pop('passive') if 'passive' in kwargs else True
super(FTPDeploy, self).__init__(*args, **kwargs)
self.conn = ftplib.FTP_TLS() if tls else ftplib.FTP()
self.conn.set_pasv(passive)
self._connected = False
def connect(self):
self.conn.connect(self.hostname, self.port or 21)
self.conn.login(self.username, self.password)
self._connected = True
def put(self, local, remote):
self.conn.cwd(os.path.dirname(remote))
with open(local, 'rb') as upload:
self.conn.storbinary('STOR %s' % os.path.basename(remote), upload)
def disconnect(self):
if self.conn:
self.conn.quit()
self._connected = False
@property
def connected(self):
return self._connected
[docs]class SFTPDeploy(Deploy):
"""
Provides communication with a SFTP Server.
"""
def __init__(self, *args, **kwargs):
"""
:param hostname: hostname of the host
:type hostname: str
:param username: username on the host
:type username: str
:param keyfile: private key to use (defaults to system keyfile)
:type keyfile: str
:param password: password on the host
can be empty for passphraseless
public key authentication or set
to the passphrase of a key
:type password: str
:param port: port of the service (defaults to 22)
:type port: int
"""
self.keyfile = kwargs.pop('keyfile') if 'keyfile' in kwargs else None
super(SFTPDeploy, self).__init__(*args, **kwargs)
self.ssh = None
self.sftp = None
def connect(self):
self.ssh = paramiko.SSHClient()
self.ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
if not self.keyfile:
self.ssh.load_system_host_keys()
self.ssh.connect(self.hostname, username=self.username,
port=self.port or 22, password=self.password,
key_filename=self.keyfile)
self.sftp = self.ssh.open_sftp()
def put(self, local, remote):
log.debug('Uploading %s to %s' % (local, remote))
self.sftp.put(local, remote)
def disconnect(self):
self.sftp.close()
self.ssh.close()
@property
def connected(self):
if not self.ssh:
return False
transport = self.ssh.get_transport()
if transport and transport.is_active():
return True
return False
[docs]class CopyDeploy(Deploy):
"""
Allows you to copy files locally.
"""
def __init__(self):
super(CopyDeploy, self).__init__(None, None, None)
def connect(self):
pass
def disconnect(self):
pass
def put(self, local, remote):
shutil.copyfile(local, remote)
@property
def connected(self):
return True