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:
parent
1cdd205eb7
commit
3f15879578
@ -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))
|
||||||
|
|
||||||
|
@ -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', ''),
|
||||||
|
Loading…
Reference in New Issue
Block a user