dar-backup 0.6.18__py3-none-any.whl → 0.6.20__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- dar_backup/Changelog.md +31 -0
- dar_backup/README.md +49 -14
- dar_backup/__about__.py +1 -1
- dar_backup/clean_log.py +14 -7
- dar_backup/cleanup.py +29 -24
- dar_backup/command_runner.py +59 -9
- dar_backup/config_settings.py +83 -53
- dar_backup/dar-backup.conf +4 -0
- dar_backup/dar_backup.py +56 -35
- dar_backup/demo.py +138 -0
- dar_backup/exceptions.py +3 -0
- dar_backup/installer.py +49 -129
- dar_backup/manager.py +214 -90
- dar_backup/util.py +345 -3
- {dar_backup-0.6.18.dist-info → dar_backup-0.6.20.dist-info}/METADATA +51 -15
- dar_backup-0.6.20.dist-info/RECORD +23 -0
- {dar_backup-0.6.18.dist-info → dar_backup-0.6.20.dist-info}/entry_points.txt +1 -1
- dar_backup-0.6.18.dist-info/RECORD +0 -21
- {dar_backup-0.6.18.dist-info → dar_backup-0.6.20.dist-info}/WHEEL +0 -0
- {dar_backup-0.6.18.dist-info → dar_backup-0.6.20.dist-info}/licenses/LICENSE +0 -0
dar_backup/dar_backup.py
CHANGED
|
@@ -14,6 +14,8 @@ This script can be used to control `dar` to backup parts of or the whole system.
|
|
|
14
14
|
"""
|
|
15
15
|
|
|
16
16
|
|
|
17
|
+
|
|
18
|
+
import argcomplete
|
|
17
19
|
import argparse
|
|
18
20
|
import filecmp
|
|
19
21
|
|
|
@@ -35,8 +37,10 @@ from sys import stderr
|
|
|
35
37
|
from sys import argv
|
|
36
38
|
from sys import version_info
|
|
37
39
|
from time import time
|
|
40
|
+
from rich.console import Console
|
|
41
|
+
from rich.text import Text
|
|
38
42
|
from threading import Event
|
|
39
|
-
from typing import List
|
|
43
|
+
from typing import List, Tuple
|
|
40
44
|
|
|
41
45
|
from . import __about__ as about
|
|
42
46
|
from dar_backup.config_settings import ConfigSettings
|
|
@@ -46,12 +50,17 @@ from dar_backup.util import get_logger
|
|
|
46
50
|
from dar_backup.util import BackupError
|
|
47
51
|
from dar_backup.util import RestoreError
|
|
48
52
|
from dar_backup.util import requirements
|
|
53
|
+
from dar_backup.util import show_version
|
|
54
|
+
from dar_backup.util import get_binary_info
|
|
55
|
+
from dar_backup.util import print_aligned_settings
|
|
56
|
+
from dar_backup.util import backup_definition_completer, list_archive_completer
|
|
49
57
|
|
|
50
58
|
from dar_backup.command_runner import CommandRunner
|
|
51
59
|
from dar_backup.command_runner import CommandResult
|
|
52
60
|
|
|
53
61
|
from dar_backup.rich_progress import show_log_driven_bar
|
|
54
62
|
|
|
63
|
+
from argcomplete.completers import FilesCompleter
|
|
55
64
|
|
|
56
65
|
logger = None
|
|
57
66
|
runner = None
|
|
@@ -312,7 +321,8 @@ def verify(args: argparse.Namespace, backup_file: str, backup_definition: str, c
|
|
|
312
321
|
if filecmp.cmp(os.path.join(config_settings.test_restore_dir, restored_file_path.lstrip("/")), os.path.join(root_path, restored_file_path.lstrip("/")), shallow=False):
|
|
313
322
|
args.verbose and logger.info(f"Success: file '{restored_file_path}' matches the original")
|
|
314
323
|
else:
|
|
315
|
-
|
|
324
|
+
result = False
|
|
325
|
+
logger.error(f"Failure: file '{restored_file_path}' did not match the original")
|
|
316
326
|
except PermissionError:
|
|
317
327
|
result = False
|
|
318
328
|
logger.exception(f"Permission error while comparing files, continuing....")
|
|
@@ -523,6 +533,10 @@ def perform_backup(args: argparse.Namespace, config_settings: ConfigSettings, ba
|
|
|
523
533
|
|
|
524
534
|
# Perform backup
|
|
525
535
|
backup_result = generic_backup(backup_type, command, backup_file, backup_definition_path, args.darrc, config_settings, args)
|
|
536
|
+
if not isinstance(backup_result, list) or not all(isinstance(i, tuple) and len(i) == 2 for i in backup_result):
|
|
537
|
+
logger.error("Unexpected return format from generic_backup")
|
|
538
|
+
backup_result = [("Unexpected return format from generic_backup", 1)]
|
|
539
|
+
|
|
526
540
|
results.extend(backup_result)
|
|
527
541
|
|
|
528
542
|
logger.info("Starting verification...")
|
|
@@ -637,14 +651,6 @@ def filter_darrc_file(darrc_path):
|
|
|
637
651
|
|
|
638
652
|
|
|
639
653
|
|
|
640
|
-
def show_version():
|
|
641
|
-
script_name = os.path.basename(argv[0])
|
|
642
|
-
print(f"{script_name} {about.__version__}")
|
|
643
|
-
print(f"dar-backup.py source code is here: https://github.com/per2jensen/dar-backup")
|
|
644
|
-
print('''Licensed under GNU GENERAL PUBLIC LICENSE v3, see the supplied file "LICENSE" for details.
|
|
645
|
-
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW, not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
646
|
-
See section 15 and section 16 in the supplied "LICENSE" file.''')
|
|
647
|
-
|
|
648
654
|
|
|
649
655
|
def show_examples():
|
|
650
656
|
examples = """
|
|
@@ -752,15 +758,15 @@ def main():
|
|
|
752
758
|
parser.add_argument('-F', '--full-backup', action='store_true', help="Perform a full backup.")
|
|
753
759
|
parser.add_argument('-D', '--differential-backup', action='store_true', help="Perform differential backup.")
|
|
754
760
|
parser.add_argument('-I', '--incremental-backup', action='store_true', help="Perform incremental backup.")
|
|
755
|
-
parser.add_argument('-d', '--backup-definition', help="Specific 'recipe' to select directories and files.")
|
|
756
|
-
parser.add_argument('--alternate-reference-archive', help="DIFF or INCR compared to specified archive.")
|
|
761
|
+
parser.add_argument('-d', '--backup-definition', help="Specific 'recipe' to select directories and files.").completer = backup_definition_completer
|
|
762
|
+
parser.add_argument('--alternate-reference-archive', help="DIFF or INCR compared to specified archive.").completer = list_archive_completer
|
|
757
763
|
parser.add_argument('-c', '--config-file', type=str, help="Path to 'dar-backup.conf'", default='~/.config/dar-backup/dar-backup.conf')
|
|
758
764
|
parser.add_argument('--darrc', type=str, help='Optional path to .darrc')
|
|
759
|
-
parser.add_argument('-l', '--list', action='store_true', help="List available archives.")
|
|
760
|
-
parser.add_argument('--list-contents', help="List the contents of the specified archive.")
|
|
765
|
+
parser.add_argument('-l', '--list', action='store_true', help="List available archives.").completer = list_archive_completer
|
|
766
|
+
parser.add_argument('--list-contents', help="List the contents of the specified archive.").completer = list_archive_completer
|
|
761
767
|
parser.add_argument('--selection', help="dar file selection for listing/restoring specific files/directories.")
|
|
762
768
|
# parser.add_argument('-r', '--restore', nargs=1, type=str, help="Restore specified archive.")
|
|
763
|
-
parser.add_argument('-r', '--restore', type=str, help="Restore specified archive.")
|
|
769
|
+
parser.add_argument('-r', '--restore', type=str, help="Restore specified archive.").completer = list_archive_completer
|
|
764
770
|
parser.add_argument('--restore-dir', type=str, help="Directory to restore files to.")
|
|
765
771
|
parser.add_argument('--verbose', action='store_true', help="Print various status messages to screen")
|
|
766
772
|
parser.add_argument('--suppress-dar-msg', action='store_true', help="cancel dar options in .darrc: -vt, -vs, -vd, -vf and -va")
|
|
@@ -773,6 +779,8 @@ def main():
|
|
|
773
779
|
parser.add_argument("--changelog", action="store_true", help="Print Changelog.md to stdout and exit.")
|
|
774
780
|
parser.add_argument("--changelog-pretty", action="store_true", help="Print Changelog.md to stdout with Markdown styling and exit.")
|
|
775
781
|
parser.add_argument('-v', '--version', action='store_true', help="Show version and license information.")
|
|
782
|
+
|
|
783
|
+
argcomplete.autocomplete(parser)
|
|
776
784
|
args = parser.parse_args()
|
|
777
785
|
|
|
778
786
|
if args.version:
|
|
@@ -834,32 +842,40 @@ def main():
|
|
|
834
842
|
args.darrc = filter_darrc_file(args.darrc)
|
|
835
843
|
logger.debug(f"Filtered .darrc file: {args.darrc}")
|
|
836
844
|
|
|
845
|
+
start_msgs: List[Tuple[str, str]] = []
|
|
846
|
+
|
|
837
847
|
start_time=int(time())
|
|
838
|
-
|
|
839
|
-
logger.info(f"dar-backup.py started, version: {about.__version__}")
|
|
848
|
+
start_msgs.append(('dar-backup.py:', about.__version__))
|
|
840
849
|
logger.info(f"START TIME: {start_time}")
|
|
841
|
-
logger.debug(f"`
|
|
842
|
-
logger.debug(f"`
|
|
850
|
+
logger.debug(f"{'`Args`:\n'}{args}")
|
|
851
|
+
logger.debug(f"{'`Config_settings`:\n'}{config_settings}")
|
|
852
|
+
dar_properties = get_binary_info(command='dar')
|
|
853
|
+
start_msgs.append(('dar path:', dar_properties['path']))
|
|
854
|
+
start_msgs.append(('dar version:', dar_properties['version']))
|
|
843
855
|
|
|
844
856
|
file_dir = os.path.normpath(os.path.dirname(__file__))
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
args.verbose and args.
|
|
850
|
-
args.verbose and args.
|
|
857
|
+
start_msgs.append(('Script directory:', os.path.abspath(file_dir)))
|
|
858
|
+
start_msgs.append(('Config file:', os.path.abspath(args.config_file)))
|
|
859
|
+
start_msgs.append((".darrc location:", args.darrc))
|
|
860
|
+
|
|
861
|
+
args.verbose and args.full_backup and start_msgs.append(("Type of backup:", "FULL"))
|
|
862
|
+
args.verbose and args.differential_backup and start_msgs.append(("Type of backup:", "DIFF"))
|
|
863
|
+
args.verbose and args.incremental_backup and start_msgs.append(("Type of backup:", "INCR"))
|
|
864
|
+
args.verbose and args.backup_definition and start_msgs.append(("Backup definition:", args.backup_definition))
|
|
851
865
|
if args.alternate_reference_archive:
|
|
852
|
-
args.verbose and (
|
|
853
|
-
args.verbose and (
|
|
854
|
-
args.verbose and (
|
|
866
|
+
args.verbose and start_msgs.append(("Alternate ref archive:", args.alternate_reference_archive))
|
|
867
|
+
args.verbose and start_msgs.append(("Backup.d dir:", config_settings.backup_d_dir))
|
|
868
|
+
args.verbose and start_msgs.append(("Backup dir:", config_settings.backup_dir))
|
|
855
869
|
|
|
856
870
|
restore_dir = args.restore_dir if args.restore_dir else config_settings.test_restore_dir
|
|
857
|
-
args.verbose and (
|
|
871
|
+
args.verbose and start_msgs.append(("Restore dir:", restore_dir))
|
|
872
|
+
|
|
873
|
+
args.verbose and start_msgs.append(("Logfile location:", config_settings.logfile_location))
|
|
874
|
+
args.verbose and start_msgs.append(("PAR2 enabled:", config_settings.par2_enabled))
|
|
875
|
+
args.verbose and start_msgs.append(("--do-not-compare:", args.do_not_compare))
|
|
858
876
|
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
args.verbose and (print(f"PAR2 enabled: {config_settings.par2_enabled}"))
|
|
862
|
-
args.verbose and (print(f"--do-not-compare: {args.do_not_compare}"))
|
|
877
|
+
dangerous_keywords = ["--do-not", "alternate"] # TODO: add more dangerous keywords
|
|
878
|
+
print_aligned_settings(start_msgs)
|
|
863
879
|
|
|
864
880
|
# sanity check
|
|
865
881
|
if args.backup_definition and not os.path.exists(os.path.join(config_settings.backup_d_dir, args.backup_definition)):
|
|
@@ -921,12 +937,17 @@ def main():
|
|
|
921
937
|
else:
|
|
922
938
|
logger.error(f"not correct result type: {result}, which must be a tuple (<msg>, <exit_code>)")
|
|
923
939
|
i=i+1
|
|
940
|
+
|
|
941
|
+
console = Console()
|
|
924
942
|
if error:
|
|
925
|
-
args.verbose
|
|
943
|
+
if args.verbose:
|
|
944
|
+
console.print(Text("Errors encountered", style="bold red"))
|
|
926
945
|
exit(1)
|
|
927
946
|
else:
|
|
928
|
-
args.verbose
|
|
947
|
+
if args.verbose:
|
|
948
|
+
console.print(Text("Success: all backups completed", style="bold green"))
|
|
929
949
|
exit(0)
|
|
950
|
+
|
|
930
951
|
|
|
931
952
|
if __name__ == "__main__":
|
|
932
953
|
main()
|
dar_backup/demo.py
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
installer.py source code is here: https://github.com/per2jensen/dar-backup/tree/main/v2/src/dar_backup/installer.py
|
|
4
|
+
This script is part of dar-backup, a backup solution for Linux using dar and systemd.
|
|
5
|
+
|
|
6
|
+
Licensed under GNU GENERAL PUBLIC LICENSE v3, see the supplied file "LICENSE" for details.
|
|
7
|
+
|
|
8
|
+
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW,
|
|
9
|
+
not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
10
|
+
See section 15 and section 16 in the supplied "LICENSE" file
|
|
11
|
+
|
|
12
|
+
This script can be used to configure dar-backup on your system.
|
|
13
|
+
It is non-destructive and will not overwrite any existing files or directories.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
import argparse
|
|
17
|
+
import os
|
|
18
|
+
import shutil
|
|
19
|
+
import sys
|
|
20
|
+
|
|
21
|
+
from . import __about__ as about
|
|
22
|
+
from pathlib import Path
|
|
23
|
+
|
|
24
|
+
LICENSE = '''Licensed under GNU GENERAL PUBLIC LICENSE v3, see the supplied file "LICENSE" for details.
|
|
25
|
+
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW, not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
26
|
+
See section 15 and section 16 in the supplied "LICENSE" file.'''
|
|
27
|
+
|
|
28
|
+
CONFIG_DIR = os.path.expanduser("~/.config/dar-backup")
|
|
29
|
+
DAR_BACKUP_DIR = os.path.expanduser("~/dar-backup/")
|
|
30
|
+
|
|
31
|
+
BACKUP_DEFINITION = '''
|
|
32
|
+
# Demo of a `dar-backup` definition file
|
|
33
|
+
# This back definition file configures a backup of ~/.config/dar-backup
|
|
34
|
+
# `dar-backup` puts the backups in ~/dar-backup/backups
|
|
35
|
+
# ------------------------------------------------------------------------
|
|
36
|
+
|
|
37
|
+
# Switch to ordered selection mode, which means that the following options
|
|
38
|
+
# will be considered top to bottom
|
|
39
|
+
-am
|
|
40
|
+
|
|
41
|
+
# Backup Root dir
|
|
42
|
+
-R @@HOME_DIR@@
|
|
43
|
+
|
|
44
|
+
# Directories to backup below the Root dir
|
|
45
|
+
-g .config/dar-backup
|
|
46
|
+
|
|
47
|
+
# Examples of directories to exclude below the Root dir
|
|
48
|
+
-P mnt
|
|
49
|
+
-P .private
|
|
50
|
+
-P .cache
|
|
51
|
+
|
|
52
|
+
# compression level
|
|
53
|
+
-z5
|
|
54
|
+
|
|
55
|
+
# no overwrite, if you rerun a backup, 'dar' halts and asks what to do
|
|
56
|
+
-n
|
|
57
|
+
|
|
58
|
+
# size of each slice in the archive
|
|
59
|
+
--slice 10G
|
|
60
|
+
|
|
61
|
+
# bypass directores marked as cache directories
|
|
62
|
+
# http://dar.linux.free.fr/doc/Features.html
|
|
63
|
+
--cache-directory-tagging
|
|
64
|
+
'''
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def main():
|
|
68
|
+
parser = argparse.ArgumentParser(
|
|
69
|
+
description="Set up `dar-backup` on your system.",
|
|
70
|
+
)
|
|
71
|
+
parser.add_argument(
|
|
72
|
+
"-i", "--install",
|
|
73
|
+
action="store_true",
|
|
74
|
+
help="Deploy a simple config file, use ~/dar-backup/ for log file, archives and restore tests."
|
|
75
|
+
)
|
|
76
|
+
parser.add_argument(
|
|
77
|
+
"-v", "--version",
|
|
78
|
+
action="version",
|
|
79
|
+
version=f"%(prog)s version {about.__version__}, {LICENSE}"
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
args = parser.parse_args()
|
|
83
|
+
|
|
84
|
+
if args.install:
|
|
85
|
+
errors = []
|
|
86
|
+
if os.path.exists(CONFIG_DIR):
|
|
87
|
+
errors.append(f"Config directory '{CONFIG_DIR}' already exists.")
|
|
88
|
+
if os.path.exists(DAR_BACKUP_DIR):
|
|
89
|
+
errors.append(f"Directory '{DAR_BACKUP_DIR}' already exists.")
|
|
90
|
+
|
|
91
|
+
if errors:
|
|
92
|
+
for error in errors:
|
|
93
|
+
print(f"Error: {error}")
|
|
94
|
+
sys.exit(1)
|
|
95
|
+
|
|
96
|
+
try:
|
|
97
|
+
os.makedirs(DAR_BACKUP_DIR, exist_ok=False)
|
|
98
|
+
os.makedirs(os.path.join(DAR_BACKUP_DIR, "backups"), exist_ok=False)
|
|
99
|
+
os.makedirs(os.path.join(DAR_BACKUP_DIR, "restore"), exist_ok=False)
|
|
100
|
+
os.makedirs(CONFIG_DIR, exist_ok=False)
|
|
101
|
+
os.makedirs(os.path.join(CONFIG_DIR, "backup.d"), exist_ok=False)
|
|
102
|
+
print(f"Directories created: `{DAR_BACKUP_DIR}` and `{CONFIG_DIR}`")
|
|
103
|
+
|
|
104
|
+
script_dir = Path(__file__).parent
|
|
105
|
+
source_file = script_dir / "dar-backup.conf"
|
|
106
|
+
destination_file = Path(CONFIG_DIR) / "dar-backup.conf"
|
|
107
|
+
|
|
108
|
+
try:
|
|
109
|
+
shutil.copy2(source_file, destination_file)
|
|
110
|
+
print(f"Config file deployed to {destination_file}")
|
|
111
|
+
except Exception as e:
|
|
112
|
+
print(f"Error: Could not copy config file: {e}")
|
|
113
|
+
sys.exit(1)
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
backup_definition = BACKUP_DEFINITION.replace("@@HOME_DIR@@", os.path.expanduser("~"))
|
|
117
|
+
|
|
118
|
+
try:
|
|
119
|
+
with open(os.path.join(CONFIG_DIR, "backup.d", "default"), "w") as f:
|
|
120
|
+
f.write(backup_definition)
|
|
121
|
+
print(f"Default backup definition file deployed to {os.path.join(CONFIG_DIR, 'backup.d', 'default')}")
|
|
122
|
+
except Exception as e:
|
|
123
|
+
print(f"Error: Could not write default backup definition: {e}")
|
|
124
|
+
sys.exit(1)
|
|
125
|
+
except Exception as e:
|
|
126
|
+
print(f"Installation failed: {e}")
|
|
127
|
+
sys.exit(1)
|
|
128
|
+
|
|
129
|
+
print("1. Now run `manager --create` to create the catalog database.")
|
|
130
|
+
print("2. Then you can run `dar-backup --full-backup` to create a backup.")
|
|
131
|
+
print("3. List backups with `dar-backup --list`")
|
|
132
|
+
print("4. List contents of a backup with `dar-backup --list-contents <backup-name>`")
|
|
133
|
+
|
|
134
|
+
sys.exit(0)
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
if __name__ == "__main__":
|
|
138
|
+
main()
|
dar_backup/exceptions.py
ADDED
dar_backup/installer.py
CHANGED
|
@@ -1,138 +1,58 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
"""
|
|
3
|
-
installer.py source code is here: https://github.com/per2jensen/dar-backup/tree/main/v2/src/dar_backup/installer.py
|
|
4
|
-
This script is part of dar-backup, a backup solution for Linux using dar and systemd.
|
|
5
|
-
|
|
6
|
-
Licensed under GNU GENERAL PUBLIC LICENSE v3, see the supplied file "LICENSE" for details.
|
|
7
|
-
|
|
8
|
-
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW,
|
|
9
|
-
not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
10
|
-
See section 15 and section 16 in the supplied "LICENSE" file
|
|
11
|
-
|
|
12
|
-
This script can be used to configure dar-backup on your system.
|
|
13
|
-
It is non-destructive and will not overwrite any existing files or directories.
|
|
14
|
-
"""
|
|
15
|
-
|
|
16
1
|
import argparse
|
|
17
2
|
import os
|
|
18
|
-
import shutil
|
|
19
|
-
import sys
|
|
20
|
-
|
|
21
|
-
from . import __about__ as about
|
|
22
3
|
from pathlib import Path
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
#
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
# Backup Root dir
|
|
42
|
-
-R @@HOME_DIR@@
|
|
43
|
-
|
|
44
|
-
# Directories to backup below the Root dir
|
|
45
|
-
-g .config/dar-backup
|
|
46
|
-
|
|
47
|
-
# Examples of directories to exclude below the Root dir
|
|
48
|
-
-P mnt
|
|
49
|
-
-P .private
|
|
50
|
-
-P .cache
|
|
51
|
-
|
|
52
|
-
# compression level
|
|
53
|
-
-z5
|
|
54
|
-
|
|
55
|
-
# no overwrite, if you rerun a backup, 'dar' halts and asks what to do
|
|
56
|
-
-n
|
|
57
|
-
|
|
58
|
-
# size of each slice in the archive
|
|
59
|
-
--slice 10G
|
|
60
|
-
|
|
61
|
-
# bypass directores marked as cache directories
|
|
62
|
-
# http://dar.linux.free.fr/doc/Features.html
|
|
63
|
-
--cache-directory-tagging
|
|
64
|
-
'''
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
def main():
|
|
68
|
-
parser = argparse.ArgumentParser(
|
|
69
|
-
description="Set up `dar-backup` on your system.",
|
|
70
|
-
)
|
|
71
|
-
parser.add_argument(
|
|
72
|
-
"-i", "--install",
|
|
73
|
-
action="store_true",
|
|
74
|
-
help="Deploy a simple config file, use ~/dar-backup/ for log file, archives and restore tests."
|
|
75
|
-
)
|
|
76
|
-
parser.add_argument(
|
|
77
|
-
"-v", "--version",
|
|
78
|
-
action="version",
|
|
79
|
-
version=f"%(prog)s version {about.__version__}, {LICENSE}"
|
|
4
|
+
from dar_backup.config_settings import ConfigSettings
|
|
5
|
+
from dar_backup.util import setup_logging, get_logger
|
|
6
|
+
from dar_backup.command_runner import CommandRunner
|
|
7
|
+
from dar_backup.manager import create_db
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def run_installer(config_file: str, create_db_flag: bool):
|
|
11
|
+
config_file = os.path.expanduser(os.path.expandvars(config_file))
|
|
12
|
+
config_settings = ConfigSettings(config_file)
|
|
13
|
+
|
|
14
|
+
# Set up logging based on the config's specified log file
|
|
15
|
+
command_log = config_settings.logfile_location.replace("dar-backup.log", "dar-backup-commands.log")
|
|
16
|
+
logger = setup_logging(
|
|
17
|
+
config_settings.logfile_location,
|
|
18
|
+
command_log,
|
|
19
|
+
log_level="info",
|
|
20
|
+
log_stdout=True,
|
|
80
21
|
)
|
|
81
|
-
|
|
22
|
+
command_logger = get_logger(command_output_logger=True)
|
|
23
|
+
runner = CommandRunner(logger=logger, command_logger=command_logger)
|
|
24
|
+
|
|
25
|
+
# Create directories listed in config
|
|
26
|
+
for attr in ["backup_dir", "test_restore_dir", "backup_d_dir", "manager_db_dir"]:
|
|
27
|
+
path = getattr(config_settings, attr, None)
|
|
28
|
+
if path:
|
|
29
|
+
dir_path = Path(path).expanduser()
|
|
30
|
+
if not dir_path.exists():
|
|
31
|
+
dir_path.mkdir(parents=True, exist_ok=True)
|
|
32
|
+
print(f"Created directory: {dir_path}")
|
|
33
|
+
else:
|
|
34
|
+
print(f"Directory already exists: {dir_path}")
|
|
35
|
+
|
|
36
|
+
# Optionally create databases
|
|
37
|
+
if create_db_flag:
|
|
38
|
+
for file in os.listdir(config_settings.backup_d_dir):
|
|
39
|
+
backup_def = os.path.basename(file)
|
|
40
|
+
print(f"Creating catalog for: {backup_def}")
|
|
41
|
+
result = create_db(backup_def, config_settings, logger)
|
|
42
|
+
if result == 0:
|
|
43
|
+
print(f"✔️ Catalog created (or already existed): {backup_def}")
|
|
44
|
+
else:
|
|
45
|
+
print(f"❌ Failed to create catalog: {backup_def}")
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def installer_main():
|
|
49
|
+
parser = argparse.ArgumentParser(description="dar-backup installer")
|
|
50
|
+
parser.add_argument("--config", required=True, help="Path to config file")
|
|
51
|
+
parser.add_argument("--create-db", action="store_true", help="Create catalog databases")
|
|
82
52
|
args = parser.parse_args()
|
|
83
53
|
|
|
84
|
-
|
|
85
|
-
errors = []
|
|
86
|
-
if os.path.exists(CONFIG_DIR):
|
|
87
|
-
errors.append(f"Config directory '{CONFIG_DIR}' already exists.")
|
|
88
|
-
if os.path.exists(DAR_BACKUP_DIR):
|
|
89
|
-
errors.append(f"Directory '{DAR_BACKUP_DIR}' already exists.")
|
|
90
|
-
|
|
91
|
-
if errors:
|
|
92
|
-
for error in errors:
|
|
93
|
-
print(f"Error: {error}")
|
|
94
|
-
sys.exit(1)
|
|
95
|
-
|
|
96
|
-
try:
|
|
97
|
-
os.makedirs(DAR_BACKUP_DIR, exist_ok=False)
|
|
98
|
-
os.makedirs(os.path.join(DAR_BACKUP_DIR, "backups"), exist_ok=False)
|
|
99
|
-
os.makedirs(os.path.join(DAR_BACKUP_DIR, "restore"), exist_ok=False)
|
|
100
|
-
os.makedirs(CONFIG_DIR, exist_ok=False)
|
|
101
|
-
os.makedirs(os.path.join(CONFIG_DIR, "backup.d"), exist_ok=False)
|
|
102
|
-
print(f"Directories created: `{DAR_BACKUP_DIR}` and `{CONFIG_DIR}`")
|
|
103
|
-
|
|
104
|
-
script_dir = Path(__file__).parent
|
|
105
|
-
source_file = script_dir / "dar-backup.conf"
|
|
106
|
-
destination_file = Path(CONFIG_DIR) / "dar-backup.conf"
|
|
107
|
-
|
|
108
|
-
try:
|
|
109
|
-
shutil.copy2(source_file, destination_file)
|
|
110
|
-
print(f"Config file deployed to {destination_file}")
|
|
111
|
-
except Exception as e:
|
|
112
|
-
print(f"Error: Could not copy config file: {e}")
|
|
113
|
-
sys.exit(1)
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
backup_definition = BACKUP_DEFINITION.replace("@@HOME_DIR@@", os.path.expanduser("~"))
|
|
117
|
-
|
|
118
|
-
try:
|
|
119
|
-
with open(os.path.join(CONFIG_DIR, "backup.d", "default"), "w") as f:
|
|
120
|
-
f.write(backup_definition)
|
|
121
|
-
print(f"Default backup definition file deployed to {os.path.join(CONFIG_DIR, 'backup.d', 'default')}")
|
|
122
|
-
except Exception as e:
|
|
123
|
-
print(f"Error: Could not write default backup definition: {e}")
|
|
124
|
-
sys.exit(1)
|
|
125
|
-
except Exception as e:
|
|
126
|
-
print(f"Installation failed: {e}")
|
|
127
|
-
sys.exit(1)
|
|
128
|
-
|
|
129
|
-
print("1. Now run `manager --create` to create the catalog database.")
|
|
130
|
-
print("2. Then you can run `dar-backup --full-backup` to create a backup.")
|
|
131
|
-
print("3. List backups with `dar-backup --list`")
|
|
132
|
-
print("4. List contents of a backup with `dar-backup --list-contents <backup-name>`")
|
|
133
|
-
|
|
134
|
-
sys.exit(0)
|
|
54
|
+
run_installer(args.config, args.create_db)
|
|
135
55
|
|
|
136
56
|
|
|
137
57
|
if __name__ == "__main__":
|
|
138
|
-
|
|
58
|
+
installer_main()
|