2021-11-11 18:31:28 +00:00
|
|
|
#!/usr/bin/env python3
|
2021-11-16 16:14:01 +00:00
|
|
|
"""Backups management module"""
|
|
|
|
import json
|
2021-12-02 15:06:23 +00:00
|
|
|
import os
|
2021-11-16 16:14:01 +00:00
|
|
|
import subprocess
|
2021-12-02 15:06:23 +00:00
|
|
|
from flask import current_app
|
2021-12-01 14:51:38 +00:00
|
|
|
from flask_restful import Resource, reqparse
|
2021-11-11 18:31:28 +00:00
|
|
|
|
|
|
|
from selfprivacy_api.resources.services import api
|
2021-12-01 14:51:38 +00:00
|
|
|
from selfprivacy_api.utils import WriteUserData
|
2021-11-11 18:31:28 +00:00
|
|
|
|
2021-11-16 16:14:01 +00:00
|
|
|
|
2021-11-11 18:31:28 +00:00
|
|
|
class ListAllBackups(Resource):
|
2021-11-16 16:14:01 +00:00
|
|
|
"""List all restic backups"""
|
|
|
|
|
2021-11-11 18:31:28 +00:00
|
|
|
def get(self):
|
2021-11-16 16:14:01 +00:00
|
|
|
"""
|
|
|
|
Get all restic backups
|
|
|
|
---
|
|
|
|
tags:
|
|
|
|
- Backups
|
|
|
|
security:
|
|
|
|
- bearerAuth: []
|
|
|
|
responses:
|
|
|
|
200:
|
|
|
|
description: A list of snapshots
|
|
|
|
400:
|
|
|
|
description: Bad request
|
|
|
|
401:
|
|
|
|
description: Unauthorized
|
|
|
|
"""
|
2021-12-02 15:06:23 +00:00
|
|
|
bucket = current_app.config["B2_BUCKET"]
|
2021-11-16 16:14:01 +00:00
|
|
|
backup_listing_command = [
|
|
|
|
"restic",
|
|
|
|
"-r",
|
2021-12-02 15:06:23 +00:00
|
|
|
f"rclone:backblaze:{bucket}/sfbackup",
|
2021-11-16 16:14:01 +00:00
|
|
|
"snapshots",
|
|
|
|
"--json",
|
|
|
|
]
|
|
|
|
|
2021-12-02 20:08:25 +00:00
|
|
|
init_command = [
|
|
|
|
"restic",
|
|
|
|
"-r",
|
|
|
|
f"rclone:backblaze:{bucket}/sfbackup",
|
|
|
|
"init",
|
|
|
|
]
|
|
|
|
|
2021-11-16 16:14:01 +00:00
|
|
|
with subprocess.Popen(
|
|
|
|
backup_listing_command,
|
|
|
|
shell=False,
|
2021-11-11 18:31:28 +00:00
|
|
|
stdout=subprocess.PIPE,
|
|
|
|
stderr=subprocess.STDOUT,
|
2021-11-16 16:14:01 +00:00
|
|
|
) as backup_listing_process_descriptor:
|
2021-12-02 20:08:25 +00:00
|
|
|
snapshots_list = backup_listing_process_descriptor.communicate()[0].decode(
|
|
|
|
"utf-8"
|
|
|
|
)
|
2021-11-11 18:31:28 +00:00
|
|
|
|
2021-12-02 15:06:23 +00:00
|
|
|
try:
|
2021-12-02 20:08:25 +00:00
|
|
|
json.loads(snapshots_list)
|
2021-12-02 15:06:23 +00:00
|
|
|
except ValueError:
|
2021-12-02 20:08:25 +00:00
|
|
|
if "Is there a repository at the following location?" in snapshots_list:
|
|
|
|
subprocess.call(init_command)
|
|
|
|
return {"error": "Initializating"}, 500
|
|
|
|
return {"error": snapshots_list}, 500
|
|
|
|
return json.loads(snapshots_list)
|
2021-11-11 18:31:28 +00:00
|
|
|
|
|
|
|
|
2021-11-16 10:02:36 +00:00
|
|
|
class AsyncCreateBackup(Resource):
|
2021-11-16 16:14:01 +00:00
|
|
|
"""Create a new restic backup"""
|
|
|
|
|
2021-11-11 18:31:28 +00:00
|
|
|
def put(self):
|
2021-11-16 16:14:01 +00:00
|
|
|
"""
|
|
|
|
Initiate a new restic backup
|
|
|
|
---
|
|
|
|
tags:
|
|
|
|
- Backups
|
|
|
|
security:
|
|
|
|
- bearerAuth: []
|
|
|
|
responses:
|
|
|
|
200:
|
|
|
|
description: Backup creation has started
|
|
|
|
400:
|
|
|
|
description: Bad request
|
|
|
|
401:
|
|
|
|
description: Unauthorized
|
|
|
|
"""
|
2021-12-02 15:06:23 +00:00
|
|
|
bucket = current_app.config["B2_BUCKET"]
|
|
|
|
|
2021-11-16 16:14:01 +00:00
|
|
|
backup_command = [
|
|
|
|
"restic",
|
|
|
|
"-r",
|
2021-12-02 15:06:23 +00:00
|
|
|
f"rclone:backblaze:{bucket}/sfbackup",
|
2021-11-16 16:14:01 +00:00
|
|
|
"--verbose",
|
|
|
|
"--json",
|
|
|
|
"backup",
|
|
|
|
"/var",
|
|
|
|
]
|
|
|
|
|
|
|
|
with open("/tmp/backup.log", "w", encoding="utf-8") as log_file:
|
|
|
|
subprocess.Popen(
|
|
|
|
backup_command, shell=False, stdout=log_file, stderr=subprocess.STDOUT
|
|
|
|
)
|
2021-11-11 18:31:28 +00:00
|
|
|
|
|
|
|
return {
|
|
|
|
"status": 0,
|
|
|
|
"message": "Backup creation has started",
|
|
|
|
}
|
|
|
|
|
2021-11-16 16:14:01 +00:00
|
|
|
|
2021-11-16 10:02:36 +00:00
|
|
|
class CheckBackupStatus(Resource):
|
2021-11-16 16:14:01 +00:00
|
|
|
"""Check current backup status"""
|
|
|
|
|
2021-11-16 10:02:36 +00:00
|
|
|
def get(self):
|
|
|
|
"""
|
2021-11-16 16:14:01 +00:00
|
|
|
Get backup status
|
|
|
|
---
|
|
|
|
tags:
|
|
|
|
- Backups
|
|
|
|
security:
|
|
|
|
- bearerAuth: []
|
|
|
|
responses:
|
|
|
|
200:
|
|
|
|
description: Backup status
|
|
|
|
400:
|
|
|
|
description: Bad request
|
|
|
|
401:
|
|
|
|
description: Unauthorized
|
|
|
|
"""
|
|
|
|
backup_status_check_command = ["tail", "-1", "/tmp/backup.log"]
|
2021-11-16 10:02:36 +00:00
|
|
|
|
2021-12-02 15:06:23 +00:00
|
|
|
# If the log file does not exists
|
|
|
|
if os.path.exists("/tmp/backup.log") is False:
|
|
|
|
return {"message_type": "not_started", "message": "Backup not started"}
|
|
|
|
|
2021-11-16 16:14:01 +00:00
|
|
|
with subprocess.Popen(
|
|
|
|
backup_status_check_command,
|
|
|
|
shell=False,
|
|
|
|
stdout=subprocess.PIPE,
|
|
|
|
stderr=subprocess.STDOUT,
|
|
|
|
) as backup_status_check_process_descriptor:
|
|
|
|
backup_process_status = (
|
|
|
|
backup_status_check_process_descriptor.communicate()[0].decode("utf-8")
|
|
|
|
)
|
2021-11-16 10:02:36 +00:00
|
|
|
|
|
|
|
try:
|
2021-11-16 16:14:01 +00:00
|
|
|
json.loads(backup_process_status)
|
2021-11-16 10:02:36 +00:00
|
|
|
except ValueError:
|
2021-12-02 15:06:23 +00:00
|
|
|
return {"message_type": "error", "message": backup_process_status}
|
|
|
|
return json.loads(backup_process_status)
|
2021-11-16 10:02:36 +00:00
|
|
|
|
2021-11-11 18:31:28 +00:00
|
|
|
|
2021-11-25 10:31:18 +00:00
|
|
|
class AsyncRestoreBackup(Resource):
|
|
|
|
"""Trigger backup restoration process"""
|
|
|
|
|
|
|
|
def put(self):
|
|
|
|
"""
|
|
|
|
Start backup restoration
|
|
|
|
---
|
|
|
|
tags:
|
|
|
|
- Backups
|
|
|
|
security:
|
|
|
|
- bearerAuth: []
|
2021-12-02 15:06:23 +00:00
|
|
|
parameters:
|
|
|
|
- in: body
|
|
|
|
required: true
|
|
|
|
name: backup
|
|
|
|
description: Backup to restore
|
|
|
|
schema:
|
|
|
|
type: object
|
|
|
|
required:
|
|
|
|
- backupId
|
|
|
|
properties:
|
|
|
|
backupId:
|
|
|
|
type: string
|
2021-11-25 10:31:18 +00:00
|
|
|
responses:
|
|
|
|
200:
|
|
|
|
description: Backup restoration process started
|
|
|
|
400:
|
|
|
|
description: Bad request
|
|
|
|
401:
|
|
|
|
description: Unauthorized
|
|
|
|
"""
|
2021-12-02 15:06:23 +00:00
|
|
|
parser = reqparse.RequestParser()
|
|
|
|
parser.add_argument("backupId", type=str, required=True)
|
|
|
|
args = parser.parse_args()
|
|
|
|
bucket = current_app.config["B2_BUCKET"]
|
|
|
|
backup_id = args["backupId"]
|
|
|
|
|
2021-11-30 21:53:39 +00:00
|
|
|
backup_restoration_command = [
|
|
|
|
"restic",
|
|
|
|
"-r",
|
2021-12-02 15:06:23 +00:00
|
|
|
f"rclone:backblaze:{bucket}/sfbackup",
|
|
|
|
"restore",
|
|
|
|
backup_id,
|
|
|
|
"--target",
|
|
|
|
"/var",
|
2021-11-30 21:53:39 +00:00
|
|
|
"--json",
|
|
|
|
]
|
2021-11-25 10:31:18 +00:00
|
|
|
|
2021-12-02 15:06:23 +00:00
|
|
|
with open("/tmp/backup.log", "w", encoding="utf-8") as log_file:
|
|
|
|
subprocess.Popen(
|
2021-11-25 10:31:18 +00:00
|
|
|
backup_restoration_command,
|
|
|
|
shell=False,
|
2021-12-02 15:06:23 +00:00
|
|
|
stdout=log_file,
|
|
|
|
stderr=subprocess.STDOUT,
|
|
|
|
)
|
2021-11-30 21:53:39 +00:00
|
|
|
|
2021-12-02 15:06:23 +00:00
|
|
|
return {"status": 0, "message": "Backup restoration procedure started"}
|
2021-11-25 10:31:18 +00:00
|
|
|
|
|
|
|
|
2021-12-01 14:51:38 +00:00
|
|
|
class BackblazeConfig(Resource):
|
|
|
|
"""Backblaze config"""
|
|
|
|
|
|
|
|
def put(self):
|
|
|
|
"""
|
|
|
|
Set the new key for backblaze
|
|
|
|
---
|
|
|
|
tags:
|
|
|
|
- Backups
|
|
|
|
security:
|
|
|
|
- bearerAuth: []
|
|
|
|
parameters:
|
|
|
|
- in: body
|
|
|
|
required: true
|
|
|
|
name: backblazeSettings
|
|
|
|
description: New Backblaze settings
|
|
|
|
schema:
|
|
|
|
type: object
|
|
|
|
required:
|
|
|
|
- accountId
|
|
|
|
- accountKey
|
|
|
|
- bucket
|
|
|
|
properties:
|
|
|
|
accountId:
|
|
|
|
type: string
|
|
|
|
accountKey:
|
|
|
|
type: string
|
|
|
|
bucket:
|
|
|
|
type: string
|
|
|
|
responses:
|
|
|
|
200:
|
|
|
|
description: New Backblaze settings
|
|
|
|
400:
|
|
|
|
description: Bad request
|
|
|
|
401:
|
|
|
|
description: Unauthorized
|
|
|
|
"""
|
|
|
|
parser = reqparse.RequestParser()
|
|
|
|
parser.add_argument("accountId", type=str, required=True)
|
|
|
|
parser.add_argument("accountKey", type=str, required=True)
|
|
|
|
parser.add_argument("bucket", type=str, required=True)
|
|
|
|
args = parser.parse_args()
|
|
|
|
|
|
|
|
with WriteUserData() as data:
|
|
|
|
data["backblaze"]["accountId"] = args["accountId"]
|
|
|
|
data["backblaze"]["accountKey"] = args["accountKey"]
|
|
|
|
data["backblaze"]["bucket"] = args["bucket"]
|
|
|
|
|
|
|
|
return "New Backblaze settings saved"
|
|
|
|
|
|
|
|
|
2021-11-11 18:31:28 +00:00
|
|
|
api.add_resource(ListAllBackups, "/restic/backup/list")
|
2021-11-16 08:25:44 +00:00
|
|
|
api.add_resource(AsyncCreateBackup, "/restic/backup/create")
|
2021-11-16 10:02:36 +00:00
|
|
|
api.add_resource(CheckBackupStatus, "/restic/backup/status")
|
2021-11-25 10:31:18 +00:00
|
|
|
api.add_resource(AsyncRestoreBackup, "/restic/backup/restore")
|
2021-12-01 14:51:38 +00:00
|
|
|
api.add_resource(BackblazeConfig, "/restic/backblaze/config")
|