Skip to content

Commit

Permalink
Implement restore-backup-content
Browse files Browse the repository at this point in the history
- Create and set-up permissions of "Restored content" folder, if
  missing.
- Remove previously restored contents.
- Restore the backup, capturing Restic's progress percentage.
  • Loading branch information
DavidePrincipi committed Nov 14, 2024
1 parent ad07d0c commit 586867b
Show file tree
Hide file tree
Showing 3 changed files with 155 additions and 0 deletions.
68 changes: 68 additions & 0 deletions imageroot/actions/restore-backup-content/50restore_backup_content
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
#!/usr/bin/env python3

#
# Copyright (C) 2024 Nethesis S.r.l.
# SPDX-License-Identifier: GPL-3.0-or-later
#

import json
import sys
import os
import subprocess
import agent
import hashlib

request = json.load(sys.stdin)

content_basename = os.path.basename(request['content'])
content_path = os.path.dirname(request['content'])
destroot = request.get("destroot", "Restored folder")

pre_script = """
set -e
share="${1:?share param missing}"
destroot="${2:?destroot param missing}"
content_basename="${3:?content_basename param is missing}"
# SDDL ACL. World read-only permissions. SYstem and Builtin Admins (BA) have full access:
destroot_acl='O:LAG:DUD:AI(A;OICIID;0x001f01ff;;;SY)(A;OICIID;0x001f01ff;;;BA)(A;OICIID;0x001301bf;;;WD)'
cd "${SAMBA_SHARES_DIR:?}/${share}"
if [ ! -e "${destroot}" ]; then
mkdir -vp "${destroot}"
chmod -c u+rwx,g+srwx,a+rx "${destroot}"
chown -c root:users "${destroot}"
samba-tool ntacl set "${destroot_acl}" "${destroot}"
fi
# Drop any existing content
rm -rf "${destroot}/${content_basename}"
"""
pre_cmd = ['podman', 'exec', '-i', 'samba-dc', 'bash', '-s', request['share'], destroot, content_basename]
subprocess.run(pre_cmd, input=pre_script, stdout=sys.stderr, text=True).check_returncode()

podman_args = ["--workdir=/srv"] + agent.agent.get_state_volume_args()
restic_args = [
"restore",
"--json",
f"{request['snapshot']}:volumes/shares/{request['share']}/{content_path}",
f"--include={content_basename}",
f"--include={content_basename}/**",
f"--target=volumes/shares/{request['share']}/{destroot}"
]
set_restore_progress_value = agent.get_progress_callback(2, 97)
restic_cmd, restic_env = agent.prepare_restic_command(agent.redis_connect(), request["destination"], request["repopath"], podman_args, restic_args)
with subprocess.Popen(restic_cmd, env=restic_env, stdout=subprocess.PIPE, text=True, errors='replace') as prestore:
while True:
line = prestore.stdout.readline()
if not line:
break
try:
omessage = json.loads(line)
if omessage['message_type'] == 'status':
fpercent = float(omessage['percent_done'])
set_restore_progress_value(int(fpercent * 100))
except Exception as ex:
print(agent.SD_WARNING+"Error decoding restic json progress", ex, file=sys.stderr)

json.dump({
"request": request,
"last_restic_message": omessage,
}, fp=sys.stdout)
51 changes: 51 additions & 0 deletions imageroot/actions/restore-backup-content/validate-input.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "restore-backup-content input",
"$id": "http://schema.nethserver.org/mail/restore-backup-content-input.json",
"description": "Extract content from a backup snapshot",
"examples": [
{
"snapshot": "b9ae143be9a5cf86fccff4bd7907296a3feb9f904457e3d521f215c5445cdac7",
"destination": "86d1a8ac-ef89-557a-8e19-8582ab86b7c4",
"repopath": "samba/8efb6625-e70f-4a5f-9cb5-2836096d5054",
"share": "pub",
"content": "Clienti/StudioV"
}
],
"type": "object",
"required": [
"destination",
"repopath",
"snapshot",
"share",
"content"
],
"properties": {
"destination": {
"type": "string",
"description": "The UUID of the backup destination where the Restic repository resides."
},
"repopath": {
"type": "string",
"description": "Restic repository path, under the backup destination"
},
"snapshot": {
"type": "string",
"description": "Restic snapshot ID to restore"
},
"share": {
"type": "string",
"pattern": "^[^/\\\\:><\"|?*]+$",
"description": "Seek the paths of this Samba share"
},
"destroot": {
"type": "string",
"pattern": "^[^/\\\\:><\"|?*]+$",
"description": "Name of a share root-level directory where content is restored. Existing content is removed before restoring the backup."
},
"content": {
"type": "string",
"description": "Content path to restore"
}
}
}
36 changes: 36 additions & 0 deletions imageroot/actions/restore-backup-content/validate-output.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "restore-backup-content output",
"$id": "http://schema.nethserver.org/mail/restore-backup-content-output.json",
"description": "Extract content from a backup snapshot",
"examples": [
{
"request": {
"content": "Clienti/StudioV",
"destination": "86d1a8ac-ef89-557a-8e19-8582ab86b7c4",
"repopath": "samba/8efb6625-e70f-4a5f-9cb5-2836096d5054",
"share": "pub",
"snapshot": "b9ae143be9a5cf86fccff4bd7907296a3feb9f904457e3d521f215c5445cdac7"
},
"last_restic_message": {
"message_type": "summary",
"seconds_elapsed": 14,
"total_files": 2165,
"files_restored": 2165,
"total_bytes": 717166163,
"bytes_restored": 717166163
}
}
],
"type": "object",
"properties": {
"request": {
"type": "object",
"description": "Original request object"
},
"last_restic_message": {
"type": "object",
"description": "Last JSON message from Restic"
}
}
}

0 comments on commit 586867b

Please sign in to comment.