Skip to content

Commit

Permalink
Merge branch 'feature-v2' into develop
Browse files Browse the repository at this point in the history
  • Loading branch information
caleb531 committed Jan 9, 2016
2 parents 4e9e508 + 96ed0a9 commit d171a13
Show file tree
Hide file tree
Showing 13 changed files with 766 additions and 566 deletions.
2 changes: 2 additions & 0 deletions swb/config/example.ini
Expand Up @@ -21,3 +21,5 @@ compressor = bzip2 -v
decompressor = bzip2 -d
# The maximum number of local backups to keep
max_local_backups = 3
# Whether to backup the entire site directory in addition to the database
full_backup = no
251 changes: 148 additions & 103 deletions swb/local.py
Expand Up @@ -18,50 +18,11 @@
remote_driver_path = os.path.join(program_dir, 'remote.py')


# Parse command line arguments passed to the local driver
def parse_cli_args():

parser = argparse.ArgumentParser()

parser.add_argument(
'--quiet',
'-q',
action='store_true',
help='silences stdout and stderr')

parser.add_argument(
'config_path',
help='the path to a configuration file (.ini)')

parser.add_argument(
'--restore',
'-r',
help='the path to a compressed backup file from which to restore')

parser.add_argument(
'--force',
'-f',
action='store_true',
help='bypasses the confirmation prompt when restoring from backup')

cli_args = parser.parse_args()
return cli_args


# Parse configuration files at given paths into object
def parse_config(config_path):

config = configparser.RawConfigParser()
config.read(config_path)

return config


# Create intermediate directories in local backup path if necessary
def create_dir_structure(path):
def create_dir_structure(local_backup_path):

try:
os.makedirs(os.path.dirname(path))
os.makedirs(os.path.dirname(local_backup_path))
except OSError:
pass

Expand All @@ -77,18 +38,18 @@ def quote_arg(arg):

if hasattr(shlex, 'quote'):
# shlex.quote was introduced in v3.3
quoted_arg = shlex.quote(arg)
quoted_arg = shlex.quote(str(arg))
else:
# pipes.quote is deprecated, but use it if shlex.quote is unavailable
quoted_arg = pipes.quote(arg)
quoted_arg = pipes.quote(str(arg))

quoted_arg = unquote_home_dir(quoted_arg)
return quoted_arg


# Connect to remote via SSH and execute remote script
def exec_on_remote(ssh_user, ssh_hostname, ssh_port, action, action_args,
*, stdout, stderr):
def exec_on_remote(ssh_user, ssh_hostname, ssh_port, *,
action, action_args, stdout, stderr):

# Read remote script so as to pass contents to SSH session
with open(remote_driver_path, 'r') as remote_script:
Expand Down Expand Up @@ -116,8 +77,8 @@ def exec_on_remote(ssh_user, ssh_hostname, ssh_port, action, action_args,


# Transfer a file from remote to local (or vice-versa) using SCP
def transfer_file(ssh_user, ssh_hostname, ssh_port,
src_path, dest_path, action, *, stdout, stderr):
def transfer_file(ssh_user, ssh_hostname, ssh_port, *,
src_path, dest_path, action, stdout, stderr):

scp_args = ['scp', '-P {}'.format(ssh_port)]

Expand All @@ -139,34 +100,82 @@ def transfer_file(ssh_user, ssh_hostname, ssh_port,


# Execute remote backup script to create remote backup
def create_remote_backup(ssh_user, ssh_hostname, ssh_port,
def create_remote_backup(ssh_user, ssh_hostname, ssh_port, *,
wordpress_path, remote_backup_path,
backup_compressor, *, stdout, stderr):
backup_compressor, stdout, stderr):

exec_on_remote(ssh_user, ssh_hostname, ssh_port, 'back-up', [
wordpress_path,
backup_compressor,
remote_backup_path
], stdout=stdout, stderr=stderr)
exec_on_remote(
ssh_user=ssh_user,
ssh_hostname=ssh_hostname,
ssh_port=ssh_port,
action='back-up',
action_args=[
wordpress_path,
backup_compressor,
remote_backup_path
],
stdout=stdout, stderr=stderr)


# Download remote backup to local system
def download_remote_backup(ssh_user, ssh_hostname, ssh_port,
def download_remote_backup(ssh_user, ssh_hostname, ssh_port, *,
remote_backup_path, local_backup_path,
*, stdout, stderr):
stdout, stderr):

transfer_file(
ssh_user=ssh_user,
ssh_hostname=ssh_hostname,
ssh_port=ssh_port,
src_path=remote_backup_path,
dest_path=local_backup_path,
action='download',
stdout=stdout, stderr=stderr)


# Uploads the given local backup to the given remote destination
def upload_local_backup(ssh_user, ssh_hostname, ssh_port, *,
local_backup_path, remote_backup_path,
stdout, stderr):

transfer_file(
ssh_user=ssh_user,
ssh_hostname=ssh_hostname,
ssh_port=ssh_port,
src_path=local_backup_path,
dest_path=remote_backup_path,
action='upload',
stdout=stdout, stderr=stderr)


# Restores the local backup after upload to remote
def restore_remote_backup(ssh_user, ssh_hostname, ssh_port, *,
wordpress_path, remote_backup_path,
backup_decompressor, stdout, stderr):

transfer_file(ssh_user, ssh_hostname, ssh_port,
remote_backup_path, local_backup_path,
'download', stdout=stdout, stderr=stderr)
exec_on_remote(
ssh_user=ssh_user,
ssh_hostname=ssh_hostname,
ssh_port=ssh_port,
action='restore',
action_args=[
wordpress_path,
remote_backup_path,
backup_decompressor
],
stdout=stdout, stderr=stderr)


# Forcefully remove backup from remote
def purge_remote_backup(ssh_user, ssh_hostname, ssh_port,
remote_backup_path, *, stdout, stderr):
def purge_remote_backup(ssh_user, ssh_hostname, ssh_port, *,
remote_backup_path, stdout, stderr):

exec_on_remote(ssh_user, ssh_hostname, ssh_port,
'purge-backup', [remote_backup_path],
stdout=stdout, stderr=stderr)
exec_on_remote(
ssh_user=ssh_user,
ssh_hostname=ssh_hostname,
ssh_port=ssh_port,
action='purge-backup',
action_args=[remote_backup_path],
stdout=stdout, stderr=stderr)


# Retrieve a file's last modified time in seconds
Expand All @@ -193,7 +202,7 @@ def purge_empty_dirs(dir_path):


# Purge oldest backups to keep number of backups within specified limit
def purge_oldest_backups(local_backup_path, max_local_backups):
def purge_oldest_backups(local_backup_path, *, max_local_backups):

# Convert date format sequences to wildcards
local_backup_path = re.sub(r'%\-?[A-Za-z]', '*', local_backup_path)
Expand Down Expand Up @@ -224,66 +233,100 @@ def back_up(config, *, stdout=None, stderr=None):
config.get('paths', 'remote_backup'))

create_remote_backup(
config.get('ssh', 'user'),
config.get('ssh', 'hostname'),
config.get('ssh', 'port'),
config.get('paths', 'wordpress'),
expanded_remote_backup_path,
config.get('backup', 'compressor'),
ssh_user=config.get('ssh', 'user'),
ssh_hostname=config.get('ssh', 'hostname'),
ssh_port=config.get('ssh', 'port'),
wordpress_path=config.get('paths', 'wordpress'),
remote_backup_path=expanded_remote_backup_path,
backup_compressor=config.get('backup', 'compressor'),
stdout=stdout, stderr=stderr)

create_dir_structure(expanded_local_backup_path)
create_dir_structure(local_backup_path=expanded_local_backup_path)

download_remote_backup(
config.get('ssh', 'user'),
config.get('ssh', 'hostname'),
config.get('ssh', 'port'),
expanded_remote_backup_path,
expanded_local_backup_path,
ssh_user=config.get('ssh', 'user'),
ssh_hostname=config.get('ssh', 'hostname'),
ssh_port=config.get('ssh', 'port'),
remote_backup_path=expanded_remote_backup_path,
local_backup_path=expanded_local_backup_path,
stdout=stdout, stderr=stderr)

purge_remote_backup(
config.get('ssh', 'user'),
config.get('ssh', 'hostname'),
config.get('ssh', 'port'),
expanded_remote_backup_path,
ssh_user=config.get('ssh', 'user'),
ssh_hostname=config.get('ssh', 'hostname'),
ssh_port=config.get('ssh', 'port'),
remote_backup_path=expanded_remote_backup_path,
stdout=stdout, stderr=stderr)

if config.has_option('backup', 'max_local_backups'):
purge_oldest_backups(
config.get('paths', 'local_backup'),
config.getint('backup', 'max_local_backups'))
local_backup_path=config.get('paths', 'local_backup'),
max_local_backups=config.getint('backup', 'max_local_backups'))


# Restore the chosen database revision to the Wordpress install on remote
def restore(config, local_backup_path, *, stdout=None, stderr=None):
def restore(config, *, local_backup_path, stdout=None, stderr=None):

expanded_remote_backup_path = time.strftime(
config.get('paths', 'remote_backup'))

# Copy local backup to remote so it can be used for restoration
transfer_file(
config.get('ssh', 'user'),
config.get('ssh', 'hostname'),
config.get('ssh', 'port'),
local_backup_path,
expanded_remote_backup_path,
'upload',
upload_local_backup(
ssh_user=config.get('ssh', 'user'),
ssh_hostname=config.get('ssh', 'hostname'),
ssh_port=config.get('ssh', 'port'),
local_backup_path=local_backup_path,
remote_backup_path=expanded_remote_backup_path,
stdout=stdout, stderr=stderr)

action_args = [
config.get('paths', 'wordpress'),
expanded_remote_backup_path,
config.get('backup', 'decompressor')
]
exec_on_remote(
config.get('ssh', 'user'),
config.get('ssh', 'hostname'),
config.get('ssh', 'port'),
'restore', action_args,
restore_remote_backup(
ssh_user=config.get('ssh', 'user'),
ssh_hostname=config.get('ssh', 'hostname'),
ssh_port=config.get('ssh', 'port'),
wordpress_path=config.get('paths', 'wordpress'),
remote_backup_path=config.get('paths', 'remote_backup'),
backup_decompressor=config.get('backup', 'decompressor'),
stdout=stdout, stderr=stderr)


# Parse command line arguments passed to the local driver
def parse_cli_args():

parser = argparse.ArgumentParser()

parser.add_argument(
'--quiet',
'-q',
action='store_true',
help='silences stdout and stderr')

parser.add_argument(
'config_path',
help='the path to a configuration file (.ini)')

parser.add_argument(
'--restore',
'-r',
help='the path to a compressed backup file from which to restore')

parser.add_argument(
'--force',
'-f',
action='store_true',
help='bypasses the confirmation prompt when restoring from backup')

cli_args = parser.parse_args()
return cli_args


# Parse configuration files at given paths into object
def parse_config(config_path):

config = configparser.RawConfigParser()
config.read(config_path)

return config


def main():

cli_args = parse_cli_args()
Expand All @@ -304,7 +347,9 @@ def main():
answer = input('Do you want to continue? (y/n) ')
if not answer.lower().lstrip().startswith('y'):
raise Exception('User canceled. Aborting.')
restore(config, cli_args.restore, stdout=stdout, stderr=stderr)
restore(
config, local_backup_path=cli_args.restore,
stdout=stdout, stderr=stderr)
else:
back_up(config, stdout=stdout, stderr=stderr)

Expand Down

0 comments on commit d171a13

Please sign in to comment.