selfprivacy-rest-api/selfprivacy_api/resources/services/restic.py

267 lines
7.4 KiB
Python
Raw Normal View History

2021-11-11 20:31:28 +02:00
#!/usr/bin/env python3
2021-11-16 18:14:01 +02:00
"""Backups management module"""
import json
2021-12-02 17:06:23 +02:00
import os
2021-11-16 18:14:01 +02:00
import subprocess
2021-12-02 17:06:23 +02:00
from flask import current_app
2021-12-01 16:51:38 +02:00
from flask_restful import Resource, reqparse
2021-11-11 20:31:28 +02:00
from selfprivacy_api.resources.services import api
2021-12-01 16:51:38 +02:00
from selfprivacy_api.utils import WriteUserData
2021-11-11 20:31:28 +02:00
2021-11-16 18:14:01 +02:00
2021-11-11 20:31:28 +02:00
class ListAllBackups(Resource):
2021-11-16 18:14:01 +02:00
"""List all restic backups"""
2021-11-11 20:31:28 +02:00
def get(self):
2021-11-16 18:14:01 +02: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 17:06:23 +02:00
bucket = current_app.config["B2_BUCKET"]
2021-11-16 18:14:01 +02:00
backup_listing_command = [
"restic",
"-r",
2021-12-02 17:06:23 +02:00
f"rclone:backblaze:{bucket}/sfbackup",
2021-11-16 18:14:01 +02:00
"snapshots",
"--json",
]
2021-12-02 22:08:25 +02:00
init_command = [
"restic",
"-r",
f"rclone:backblaze:{bucket}/sfbackup",
"init",
]
2021-11-16 18:14:01 +02:00
with subprocess.Popen(
backup_listing_command,
shell=False,
2021-11-11 20:31:28 +02:00
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
2021-11-16 18:14:01 +02:00
) as backup_listing_process_descriptor:
2021-12-02 22:08:25 +02:00
snapshots_list = backup_listing_process_descriptor.communicate()[0].decode(
"utf-8"
)
2021-11-11 20:31:28 +02:00
2021-12-02 17:06:23 +02:00
try:
2021-12-02 22:08:25 +02:00
json.loads(snapshots_list)
2021-12-02 17:06:23 +02:00
except ValueError:
2021-12-02 22:08:25 +02: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 20:31:28 +02:00
class AsyncCreateBackup(Resource):
2021-11-16 18:14:01 +02:00
"""Create a new restic backup"""
2021-11-11 20:31:28 +02:00
def put(self):
2021-11-16 18:14:01 +02: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 17:06:23 +02:00
bucket = current_app.config["B2_BUCKET"]
2021-11-16 18:14:01 +02:00
backup_command = [
"restic",
"-r",
2021-12-02 17:06:23 +02:00
f"rclone:backblaze:{bucket}/sfbackup",
2021-11-16 18:14:01 +02: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 20:31:28 +02:00
return {
"status": 0,
"message": "Backup creation has started",
}
2021-11-16 18:14:01 +02:00
class CheckBackupStatus(Resource):
2021-11-16 18:14:01 +02:00
"""Check current backup status"""
def get(self):
"""
2021-11-16 18:14:01 +02: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-12-02 17:06:23 +02: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 18:14:01 +02: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")
)
try:
2021-11-16 18:14:01 +02:00
json.loads(backup_process_status)
except ValueError:
2021-12-02 17:06:23 +02:00
return {"message_type": "error", "message": backup_process_status}
return json.loads(backup_process_status)
2021-11-11 20:31:28 +02:00
2021-11-25 12:31:18 +02:00
class AsyncRestoreBackup(Resource):
"""Trigger backup restoration process"""
def put(self):
"""
Start backup restoration
---
tags:
- Backups
security:
- bearerAuth: []
2021-12-02 17:06:23 +02:00
parameters:
- in: body
required: true
name: backup
description: Backup to restore
schema:
type: object
required:
- backupId
properties:
backupId:
type: string
2021-11-25 12:31:18 +02:00
responses:
200:
description: Backup restoration process started
400:
description: Bad request
401:
description: Unauthorized
"""
2021-12-02 17:06:23 +02: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 23:53:39 +02:00
backup_restoration_command = [
"restic",
"-r",
2021-12-02 17:06:23 +02:00
f"rclone:backblaze:{bucket}/sfbackup",
"restore",
backup_id,
"--target",
"/var",
2021-11-30 23:53:39 +02:00
"--json",
]
2021-11-25 12:31:18 +02:00
2021-12-02 17:06:23 +02:00
with open("/tmp/backup.log", "w", encoding="utf-8") as log_file:
subprocess.Popen(
2021-11-25 12:31:18 +02:00
backup_restoration_command,
shell=False,
2021-12-02 17:06:23 +02:00
stdout=log_file,
stderr=subprocess.STDOUT,
)
2021-11-30 23:53:39 +02:00
2021-12-02 17:06:23 +02:00
return {"status": 0, "message": "Backup restoration procedure started"}
2021-11-25 12:31:18 +02:00
2021-12-01 16:51:38 +02: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 20:31:28 +02:00
api.add_resource(ListAllBackups, "/restic/backup/list")
2021-11-16 10:25:44 +02:00
api.add_resource(AsyncCreateBackup, "/restic/backup/create")
api.add_resource(CheckBackupStatus, "/restic/backup/status")
2021-11-25 12:31:18 +02:00
api.add_resource(AsyncRestoreBackup, "/restic/backup/restore")
2021-12-01 16:51:38 +02:00
api.add_resource(BackblazeConfig, "/restic/backblaze/config")