2015-12-26 21:38:18 -08:00
|
|
|
#!/usr/bin/env python3
|
|
|
|
|
|
|
|
import traceback
|
|
|
|
import os
|
2017-05-27 12:08:31 -07:00
|
|
|
from sys import exit, stdout
|
2015-12-26 21:38:18 -08:00
|
|
|
from os.path import join as pathjoin
|
2017-05-27 12:08:31 -07:00
|
|
|
from os.path import exists, getsize
|
|
|
|
from common.cgi import parse_qs, parse_auth, start_response
|
|
|
|
from common.datadb import DATADB_ROOT, DATADB_DIR_TIMESTAMP_FORMAT
|
2017-05-24 23:05:52 -07:00
|
|
|
from datetime import datetime
|
2015-12-26 21:38:18 -08:00
|
|
|
|
|
|
|
|
|
|
|
def get_backup_dir(backup_name):
|
|
|
|
"""
|
2017-05-24 23:05:52 -07:00
|
|
|
Get the absolute local path to a backup or raise an exception if none exists. When getting a backup, sort folder
|
|
|
|
names (they're timestamps) and return newest.
|
2015-12-26 21:38:18 -08:00
|
|
|
:returns: str absolute path to backup seq /0/
|
|
|
|
"""
|
2017-05-24 23:05:52 -07:00
|
|
|
backups_dir = pathjoin(DATADB_ROOT, backup_name, 'data')
|
2017-05-27 12:08:31 -07:00
|
|
|
|
2017-05-24 23:05:52 -07:00
|
|
|
if not exists(backups_dir):
|
2015-12-26 21:38:18 -08:00
|
|
|
raise Exception("Backup does not exist")
|
2017-05-24 23:05:52 -07:00
|
|
|
|
|
|
|
dirs = sorted([datetime.strptime(d, DATADB_DIR_TIMESTAMP_FORMAT) for d in os.listdir(backups_dir)])
|
|
|
|
|
|
|
|
return os.path.join(backups_dir, dirs[-1].strftime(DATADB_DIR_TIMESTAMP_FORMAT), 'data')
|
2015-12-26 21:38:18 -08:00
|
|
|
|
|
|
|
|
2015-12-29 22:34:46 -08:00
|
|
|
def handle_head(backup_name):
|
|
|
|
try:
|
2017-05-27 12:08:31 -07:00
|
|
|
# backup_path = get_backup_dir(backup_name)
|
|
|
|
# TODO appropriate content-length for HEAD
|
2015-12-29 22:34:46 -08:00
|
|
|
start_response(extra_headers=['Content-length: 0'])
|
|
|
|
except:
|
|
|
|
start_response(status_code=("404", "Not Found",), extra_headers=['Content-length: 0'])
|
|
|
|
exit(0)
|
|
|
|
|
|
|
|
|
2015-12-26 21:38:18 -08:00
|
|
|
def handle_get_rsync(backup_name):
|
|
|
|
"""
|
|
|
|
Prints the absolute path an rsync backup should pull from
|
|
|
|
"""
|
|
|
|
backup_path = get_backup_dir(backup_name)
|
2017-05-27 12:08:31 -07:00
|
|
|
|
2015-12-26 21:38:18 -08:00
|
|
|
start_response()
|
2017-05-27 12:08:31 -07:00
|
|
|
print(backup_path + '/')
|
2015-12-26 21:38:18 -08:00
|
|
|
|
|
|
|
|
|
|
|
def handle_get_archive(backup_name):
|
|
|
|
"""
|
|
|
|
Returns .tar.gz data to the browser
|
|
|
|
"""
|
|
|
|
backup_path = pathjoin(get_backup_dir(backup_name), 'backup.tar.gz')
|
2017-05-27 12:08:31 -07:00
|
|
|
|
2015-12-26 21:38:18 -08:00
|
|
|
with open(backup_path, 'rb') as f:
|
2017-05-27 12:08:31 -07:00
|
|
|
start_response(content_type="application/x-gzip",
|
|
|
|
extra_headers=["Content-length: %s" % getsize(backup_path),
|
|
|
|
"Content-Disposition: attachment; filename=\"backup.tar.gz\""])
|
2015-12-26 21:38:18 -08:00
|
|
|
while True:
|
|
|
|
data = f.read(8192)
|
|
|
|
if not data:
|
|
|
|
break
|
|
|
|
stdout.buffer.write(data)
|
|
|
|
exit(0)
|
|
|
|
|
|
|
|
|
|
|
|
def handle_req():
|
|
|
|
"""
|
|
|
|
Parse http query parameters and act accordingly.
|
|
|
|
"""
|
|
|
|
params = parse_qs()
|
2017-05-27 12:08:31 -07:00
|
|
|
|
2015-12-26 21:38:18 -08:00
|
|
|
for param_name in ["proto", "name"]:
|
2017-05-27 12:08:31 -07:00
|
|
|
if param_name not in params:
|
2015-12-26 21:38:18 -08:00
|
|
|
raise Exception("Missing parameter: %s" % param_name)
|
2017-05-27 12:08:31 -07:00
|
|
|
|
2015-12-26 21:38:18 -08:00
|
|
|
if os.environ['REQUEST_METHOD'] == "GET" and params["proto"] == "rsync":
|
|
|
|
# Should return absolute local path to latest backup dir
|
|
|
|
handle_get_rsync(params["name"])
|
2017-05-27 12:08:31 -07:00
|
|
|
|
2015-12-26 21:38:18 -08:00
|
|
|
elif os.environ['REQUEST_METHOD'] == "GET" and params["proto"] == "archive":
|
|
|
|
# Should respond by transferring tar.gz data
|
|
|
|
handle_get_archive(params["name"])
|
2017-05-27 12:08:31 -07:00
|
|
|
|
2015-12-29 22:34:46 -08:00
|
|
|
elif os.environ['REQUEST_METHOD'] == "HEAD":
|
|
|
|
# Respond with 200 or 404 depending if backup exists
|
|
|
|
# TODO: deeper inspection so the headers can be flushed out
|
|
|
|
handle_head(params["name"])
|
2017-05-27 12:08:31 -07:00
|
|
|
|
2015-12-26 21:38:18 -08:00
|
|
|
else:
|
|
|
|
raise Exception("Invalid request. Params: %s" % params)
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
try:
|
|
|
|
handle_req()
|
|
|
|
except Exception as e:
|
|
|
|
start_response(status_code=("500", "Internal server error"))
|
2017-05-27 12:08:31 -07:00
|
|
|
|
2015-12-26 21:38:18 -08:00
|
|
|
tb = traceback.format_exc()
|
|
|
|
print(tb)
|
|
|
|
|