1
0
mirror of https://github.com/mail-in-a-box/mailinabox.git synced 2024-11-22 02:17:26 +00:00

remove global variables in backup.py

This commit is contained in:
Joshua Tauberer 2015-08-09 16:56:33 +00:00
parent 1cdd205eb7
commit 3f15879578
2 changed files with 39 additions and 40 deletions

View File

@ -14,23 +14,14 @@ import rtyaml
from utils import exclusive_process, load_environment, shell, wait_for_service from utils import exclusive_process, load_environment, shell, wait_for_service
# Root folder
backup_root = os.path.join(load_environment()["STORAGE_ROOT"], 'backup')
# Default settings
# min_age_in_days is the minimum amount of days a backup will be kept before
# it is eligble to be removed. Backups might be kept much longer if there's no
# new full backup yet.
default_config = {
"min_age_in_days": 3,
"target": "file://" + os.path.join(backup_root, 'encrypted')
}
def backup_status(env): def backup_status(env):
# Root folder
backup_root = os.path.join(env["STORAGE_ROOT"], 'backup')
# What is the current status of backups? # What is the current status of backups?
# Query duplicity to get a list of all backups. # Query duplicity to get a list of all backups.
# Use the number of volumes to estimate the size. # Use the number of volumes to estimate the size.
config = get_backup_config() config = get_backup_config(env)
now = datetime.datetime.now(dateutil.tz.tzlocal()) now = datetime.datetime.now(dateutil.tz.tzlocal())
backups = { } backups = { }
@ -63,12 +54,11 @@ def backup_status(env):
"/usr/bin/duplicity", "/usr/bin/duplicity",
"collection-status", "collection-status",
"--archive-dir", backup_cache_dir, "--archive-dir", backup_cache_dir,
"--log-file", os.path.join(backup_root, "duplicity_status"),
"--gpg-options", "--cipher-algo=AES256", "--gpg-options", "--cipher-algo=AES256",
"--log-fd", "1", "--log-fd", "1",
config["target"], config["target"],
], ],
get_env()) get_env(env))
# Split multi line string into list # Split multi line string into list
collection_status = collection_status.split('\n') collection_status = collection_status.split('\n')
@ -147,23 +137,24 @@ def should_force_full(env):
# (I love for/else blocks. Here it's just to show off.) # (I love for/else blocks. Here it's just to show off.)
return True return True
def get_passphrase(): def get_passphrase(env):
# Get the encryption passphrase. secret_key.txt is 2048 random # Get the encryption passphrase. secret_key.txt is 2048 random
# bits base64-encoded and with line breaks every 65 characters. # bits base64-encoded and with line breaks every 65 characters.
# gpg will only take the first line of text, so sanity check that # gpg will only take the first line of text, so sanity check that
# that line is long enough to be a reasonable passphrase. It # that line is long enough to be a reasonable passphrase. It
# only needs to be 43 base64-characters to match AES256's key # only needs to be 43 base64-characters to match AES256's key
# length of 32 bytes. # length of 32 bytes.
backup_root = os.path.join(env["STORAGE_ROOT"], 'backup')
with open(os.path.join(backup_root, 'secret_key.txt')) as f: with open(os.path.join(backup_root, 'secret_key.txt')) as f:
passphrase = f.readline().strip() passphrase = f.readline().strip()
if len(passphrase) < 43: raise Exception("secret_key.txt's first line is too short!") if len(passphrase) < 43: raise Exception("secret_key.txt's first line is too short!")
return passphrase return passphrase
def get_env(): def get_env(env):
config = get_backup_config() config = get_backup_config(env)
env = { "PASSPHRASE" : get_passphrase() } env = { "PASSPHRASE" : get_passphrase(env) }
if get_target_type(config) == 's3': if get_target_type(config) == 's3':
env["AWS_ACCESS_KEY_ID"] = config["target_user"] env["AWS_ACCESS_KEY_ID"] = config["target_user"]
@ -179,7 +170,8 @@ def perform_backup(full_backup):
env = load_environment() env = load_environment()
exclusive_process("backup") exclusive_process("backup")
config = get_backup_config() config = get_backup_config(env)
backup_root = os.path.join(env["STORAGE_ROOT"], 'backup')
backup_cache_dir = os.path.join(backup_root, 'cache') backup_cache_dir = os.path.join(backup_root, 'cache')
backup_dir = os.path.join(backup_root, 'encrypted') backup_dir = os.path.join(backup_root, 'encrypted')
@ -234,7 +226,7 @@ def perform_backup(full_backup):
config["target"], config["target"],
"--allow-source-mismatch" "--allow-source-mismatch"
], ],
get_env()) get_env(env))
finally: finally:
# Start services again. # Start services again.
shell('check_call', ["/usr/sbin/service", "dovecot", "start"]) shell('check_call', ["/usr/sbin/service", "dovecot", "start"])
@ -254,7 +246,7 @@ def perform_backup(full_backup):
"--force", "--force",
config["target"] config["target"]
], ],
get_env()) get_env(env))
# From duplicity's manual: # From duplicity's manual:
# "This should only be necessary after a duplicity session fails or is # "This should only be necessary after a duplicity session fails or is
@ -268,7 +260,7 @@ def perform_backup(full_backup):
"--force", "--force",
config["target"] config["target"]
], ],
get_env()) get_env(env))
# Change ownership of backups to the user-data user, so that the after-bcakup # Change ownership of backups to the user-data user, so that the after-bcakup
# script can access them. # script can access them.
@ -282,7 +274,7 @@ def perform_backup(full_backup):
if os.path.exists(post_script): if os.path.exists(post_script):
shell('check_call', shell('check_call',
['su', env['STORAGE_USER'], '-c', post_script, config["target"]], ['su', env['STORAGE_USER'], '-c', post_script, config["target"]],
env=get_env()) env=get_env(env))
# Our nightly cron job executes system status checks immediately after this # Our nightly cron job executes system status checks immediately after this
# backup. Since it checks that dovecot and postfix are running, block for a # backup. Since it checks that dovecot and postfix are running, block for a
@ -293,7 +285,8 @@ def perform_backup(full_backup):
def run_duplicity_verification(): def run_duplicity_verification():
env = load_environment() env = load_environment()
config = get_backup_config() backup_root = os.path.join(env["STORAGE_ROOT"], 'backup')
config = get_backup_config(env)
backup_cache_dir = os.path.join(backup_root, 'cache') backup_cache_dir = os.path.join(backup_root, 'cache')
shell('check_call', [ shell('check_call', [
@ -305,11 +298,11 @@ def run_duplicity_verification():
"--exclude", backup_root, "--exclude", backup_root,
config["target"], config["target"],
env["STORAGE_ROOT"], env["STORAGE_ROOT"],
], get_env()) ], get_env(env))
def backup_set_custom(target, target_user, target_pass, min_age): def backup_set_custom(env, target, target_user, target_pass, min_age):
config = get_backup_config() config = get_backup_config(env)
# min_age must be an int # min_age must be an int
if isinstance(min_age, str): if isinstance(min_age, str):
@ -320,23 +313,29 @@ def backup_set_custom(target, target_user, target_pass, min_age):
config["target_pass"] = target_pass config["target_pass"] = target_pass
config["min_age_in_days"] = min_age config["min_age_in_days"] = min_age
write_backup_config(config) write_backup_config(env, config)
return "Updated backup config" return "Updated backup config"
def get_backup_config(): def get_backup_config(env):
try: backup_root = os.path.join(env["STORAGE_ROOT"], 'backup')
config = rtyaml.load(open(os.path.join(backup_root, 'custom.yaml')))
if not isinstance(config, dict): raise ValueError() # caught below
except:
return default_config
merged_config = default_config.copy() config = {
merged_config.update(config) "min_age_in_days": 3,
"target": "file://" + os.path.join(backup_root, 'encrypted'),
}
try:
custom_config = rtyaml.load(open(os.path.join(backup_root, 'custom.yaml')))
if not isinstance(custom_config, dict): raise ValueError() # caught below
config.update(custom_config)
except:
pass
return config return config
def write_backup_config(newconfig): def write_backup_config(env, newconfig):
backup_root = os.path.join(env["STORAGE_ROOT"], 'backup')
with open(os.path.join(backup_root, 'custom.yaml'), "w") as f: with open(os.path.join(backup_root, 'custom.yaml'), "w") as f:
f.write(rtyaml.dump(newconfig)) f.write(rtyaml.dump(newconfig))

View File

@ -406,13 +406,13 @@ def backup_status():
@authorized_personnel_only @authorized_personnel_only
def backup_get_custom(): def backup_get_custom():
from backup import get_backup_config from backup import get_backup_config
return json_response(get_backup_config()) return json_response(get_backup_config(env))
@app.route('/system/backup/config', methods=["POST"]) @app.route('/system/backup/config', methods=["POST"])
@authorized_personnel_only @authorized_personnel_only
def backup_set_custom(): def backup_set_custom():
from backup import backup_set_custom from backup import backup_set_custom
return json_response(backup_set_custom( return json_response(backup_set_custom(env,
request.form.get('target', ''), request.form.get('target', ''),
request.form.get('target_user', ''), request.form.get('target_user', ''),
request.form.get('target_pass', ''), request.form.get('target_pass', ''),