dar-backup 0.6.20.1__py3-none-any.whl → 0.7.1__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 +29 -0
- dar_backup/README.md +720 -260
- dar_backup/__about__.py +6 -1
- dar_backup/clean_log.py +2 -0
- dar_backup/cleanup.py +4 -1
- dar_backup/command_runner.py +2 -0
- dar_backup/config_settings.py +2 -0
- dar_backup/dar-backup.conf +4 -2
- dar_backup/dar-backup.conf.j2 +60 -0
- dar_backup/dar_backup.py +30 -11
- dar_backup/dar_backup_systemd.py +3 -0
- dar_backup/demo.py +154 -81
- dar_backup/demo_backup_def.j2 +62 -0
- dar_backup/exceptions.py +2 -0
- dar_backup/installer.py +149 -19
- dar_backup/manager.py +6 -3
- dar_backup/rich_progress.py +3 -0
- dar_backup/util.py +68 -8
- {dar_backup-0.6.20.1.dist-info → dar_backup-0.7.1.dist-info}/METADATA +722 -261
- dar_backup-0.7.1.dist-info/RECORD +25 -0
- {dar_backup-0.6.20.1.dist-info → dar_backup-0.7.1.dist-info}/entry_points.txt +1 -0
- dar_backup-0.6.20.1.dist-info/RECORD +0 -23
- {dar_backup-0.6.20.1.dist-info → dar_backup-0.7.1.dist-info}/WHEEL +0 -0
- {dar_backup-0.6.20.1.dist-info → dar_backup-0.7.1.dist-info}/licenses/LICENSE +0 -0
dar_backup/__about__.py
CHANGED
|
@@ -1 +1,6 @@
|
|
|
1
|
-
__version__ = "0.
|
|
1
|
+
__version__ = "0.7.1"
|
|
2
|
+
|
|
3
|
+
__license__ = '''Licensed under GNU GENERAL PUBLIC LICENSE v3, see the supplied file "LICENSE" for details.
|
|
4
|
+
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW, not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
5
|
+
See section 15 and section 16 in the supplied "LICENSE" file.'''
|
|
6
|
+
|
dar_backup/clean_log.py
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
|
+
# SPDX-License-Identifier: GPL-3.0-or-later
|
|
3
|
+
|
|
2
4
|
"""
|
|
3
5
|
clean-log.py source code is here: https://github.com/per2jensen/dar-backup/tree/main/v2/src/dar_backup/clean-log.py
|
|
4
6
|
This script is part of dar-backup, a backup solution for Linux using dar and systemd.
|
dar_backup/cleanup.py
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
|
+
# SPDX-License-Identifier: GPL-3.0-or-later
|
|
2
3
|
|
|
3
4
|
"""
|
|
4
5
|
cleanup.py source code is here: https://github.com/per2jensen/dar-backup
|
|
@@ -37,6 +38,7 @@ from dar_backup.util import setup_logging
|
|
|
37
38
|
from dar_backup.util import get_logger
|
|
38
39
|
from dar_backup.util import requirements
|
|
39
40
|
from dar_backup.util import show_version
|
|
41
|
+
from dar_backup.util import get_invocation_command_line
|
|
40
42
|
from dar_backup.util import print_aligned_settings
|
|
41
43
|
from dar_backup.util import backup_definition_completer, list_archive_completer
|
|
42
44
|
|
|
@@ -222,6 +224,7 @@ def main():
|
|
|
222
224
|
start_msgs.append(("cleanup.py:", about.__version__))
|
|
223
225
|
|
|
224
226
|
logger.info(f"START TIME: {start_time}")
|
|
227
|
+
logger.debug(f"Command line: {get_invocation_command_line()}")
|
|
225
228
|
logger.debug(f"`args`:\n{args}")
|
|
226
229
|
logger.debug(f"`config_settings`:\n{config_settings}")
|
|
227
230
|
|
|
@@ -234,7 +237,7 @@ def main():
|
|
|
234
237
|
args.verbose and start_msgs.append(("--cleanup-specific-archives:", args.cleanup_specific_archives))
|
|
235
238
|
|
|
236
239
|
dangerous_keywords = ["--cleanup", "_FULL_"] # TODO: add more dangerous keywords
|
|
237
|
-
print_aligned_settings(start_msgs, highlight_keywords=dangerous_keywords)
|
|
240
|
+
print_aligned_settings(start_msgs, highlight_keywords=dangerous_keywords, quiet=not args.verbose)
|
|
238
241
|
|
|
239
242
|
# run PREREQ scripts
|
|
240
243
|
requirements('PREREQ', config_settings)
|
dar_backup/command_runner.py
CHANGED
dar_backup/config_settings.py
CHANGED
dar_backup/dar-backup.conf
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
# SPDX-License-Identifier: GPL-3.0-or-later
|
|
2
|
+
|
|
1
3
|
# This config file is intended to demo `dar-backup`.
|
|
2
4
|
#
|
|
3
|
-
# The `
|
|
5
|
+
# The `demo` application puts this file in ~/.config/dar-backup/dar-backup.conf
|
|
4
6
|
|
|
5
7
|
[MISC]
|
|
6
8
|
LOGFILE_LOCATION = ~/dar-backup/dar-backup.log
|
|
@@ -13,7 +15,7 @@ NO_FILES_VERIFICATION = 5
|
|
|
13
15
|
COMMAND_TIMEOUT_SECS = 86400
|
|
14
16
|
|
|
15
17
|
[DIRECTORIES]
|
|
16
|
-
BACKUP_DIR =
|
|
18
|
+
BACKUP_DIR = @@BACKUP_DIR@@
|
|
17
19
|
BACKUP.D_DIR = ~/.config/dar-backup/backup.d/
|
|
18
20
|
TEST_RESTORE_DIR = ~/dar-backup/restore/
|
|
19
21
|
# Optional parameter
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
|
|
2
|
+
# SPDX-License-Identifier: GPL-3.0-or-later
|
|
3
|
+
|
|
4
|
+
# ------------------------------------------------------------------------
|
|
5
|
+
# Demo of a `dar-backup` configuration file
|
|
6
|
+
# This file was generated by dar-backup's `demo` program.
|
|
7
|
+
#
|
|
8
|
+
{%- if opts_dict | length > 0 %}
|
|
9
|
+
# Options given to the `demo` program:
|
|
10
|
+
{% endif %}
|
|
11
|
+
{%- if opts_dict.ROOT_DIR -%}
|
|
12
|
+
# --root-dir : {{ opts_dict.ROOT_DIR }}
|
|
13
|
+
{% endif %}
|
|
14
|
+
{%- if opts_dict.DIR_TO_BACKUP -%}
|
|
15
|
+
# --dir-to-backup : {{ opts_dict.DIR_TO_BACKUP }}
|
|
16
|
+
{% endif -%}
|
|
17
|
+
{%- if opts_dict.BACKUP_DIR -%}
|
|
18
|
+
# --backup-dir : {{ opts_dict.BACKUP_DIR }}
|
|
19
|
+
{% endif %}
|
|
20
|
+
#
|
|
21
|
+
# Variables used to generate this file:
|
|
22
|
+
# =====================================
|
|
23
|
+
{% for k,v in vars_map|dictsort %}# {{ k }} : {{ v }}
|
|
24
|
+
{% endfor -%}
|
|
25
|
+
# ------------------------------------------------------------------------
|
|
26
|
+
|
|
27
|
+
[MISC]
|
|
28
|
+
LOGFILE_LOCATION = {{ vars_map.DAR_BACKUP_DIR -}}/dar-backup.log
|
|
29
|
+
MAX_SIZE_VERIFICATION_MB = 2
|
|
30
|
+
MIN_SIZE_VERIFICATION_MB = 0
|
|
31
|
+
NO_FILES_VERIFICATION = 1
|
|
32
|
+
# timeout in seconds for backup, test, restore and par2 operations
|
|
33
|
+
# The author has such `dar` tasks running for 10-15 hours on the yearly backups, so a value of 24 hours is used.
|
|
34
|
+
# If a timeout is not specified when using the CommandRunner, a default timeout of 30 secs is used.
|
|
35
|
+
COMMAND_TIMEOUT_SECS = 86400
|
|
36
|
+
|
|
37
|
+
[DIRECTORIES]
|
|
38
|
+
BACKUP_DIR = {{ vars_map.BACKUP_DIR }}
|
|
39
|
+
BACKUP.D_DIR = {{ vars_map.BACKUP_D_DIR }}
|
|
40
|
+
TEST_RESTORE_DIR = {{ vars_map.TEST_RESTORE_DIR }}
|
|
41
|
+
# Optional parameter
|
|
42
|
+
# If you want to store the catalog database away from the BACKUP_DIR, use the MANAGER_DB_DIR variable.
|
|
43
|
+
#MANAGER_DB_DIR = /some/where/else/
|
|
44
|
+
|
|
45
|
+
[AGE]
|
|
46
|
+
# DIFF and INCR backups are kept for a configured number of days, then deleted by the `cleanuo`
|
|
47
|
+
# age settings are in days
|
|
48
|
+
DIFF_AGE = 100
|
|
49
|
+
INCR_AGE = 40
|
|
50
|
+
|
|
51
|
+
[PAR2]
|
|
52
|
+
ERROR_CORRECTION_PERCENT = 5
|
|
53
|
+
ENABLED = True
|
|
54
|
+
|
|
55
|
+
[PREREQ]
|
|
56
|
+
#SCRIPT_1 = <pre-script 1>
|
|
57
|
+
|
|
58
|
+
[POSTREQ]
|
|
59
|
+
#SCRIPT_1 = <post-script 1>
|
|
60
|
+
|
dar_backup/dar_backup.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
|
-
|
|
2
|
+
# SPDX-License-Identifier: GPL-3.0-or-later
|
|
3
3
|
"""
|
|
4
4
|
installer.py source code is here: https://github.com/per2jensen/dar-backup/tree/main/v2/src/dar_backup/installer.py
|
|
5
5
|
This script is part of dar-backup, a backup solution for Linux using dar and systemd.
|
|
@@ -51,6 +51,7 @@ from dar_backup.util import BackupError
|
|
|
51
51
|
from dar_backup.util import RestoreError
|
|
52
52
|
from dar_backup.util import requirements
|
|
53
53
|
from dar_backup.util import show_version
|
|
54
|
+
from dar_backup.util import get_invocation_command_line
|
|
54
55
|
from dar_backup.util import get_binary_info
|
|
55
56
|
from dar_backup.util import print_aligned_settings
|
|
56
57
|
from dar_backup.util import backup_definition_completer, list_archive_completer
|
|
@@ -683,12 +684,29 @@ INCR back of a single backup definition in backup.d
|
|
|
683
684
|
|
|
684
685
|
|
|
685
686
|
--selection
|
|
686
|
-
--selection takes dar selection parameters between a pair of `"`.
|
|
687
687
|
|
|
688
|
-
|
|
689
|
-
|
|
688
|
+
--selection takes dar file selection options inside a quoted string.
|
|
689
|
+
|
|
690
|
+
💡 Shell quoting matters! Always wrap the entire selection string in double quotes to avoid shell splitting.
|
|
691
|
+
|
|
692
|
+
✅ Use: --selection="-I '*.NEF'"
|
|
693
|
+
❌ Avoid: --selection "-I '*.NEF'" → may break due to how your shell parses it.
|
|
694
|
+
|
|
695
|
+
Examples:
|
|
696
|
+
1)
|
|
697
|
+
select file names with "Z50_" in file names:
|
|
698
|
+
python3 dar-backup.py --restore <name of dar archive> --selection="-I '*Z50_*'"
|
|
699
|
+
2)
|
|
700
|
+
Filter out *.xmp files:
|
|
701
|
+
python3 dar-backup.py --restore <name of dar archive> --selection="-X '*.xmp'"
|
|
702
|
+
|
|
703
|
+
3)
|
|
704
|
+
Include all files in a directory:
|
|
705
|
+
python3 dar-backup.py --restore <name of dar archive> --selection="-g 'path/to/a/dir'"
|
|
690
706
|
|
|
691
|
-
|
|
707
|
+
4)
|
|
708
|
+
Exclude a directory:
|
|
709
|
+
python3 dar-backup.py --restore <name of dar archive> --selection="-P 'path/to/a/dir'"
|
|
692
710
|
|
|
693
711
|
See dar documentation on file selection: http://dar.linux.free.fr/doc/man/dar.html#COMMANDS%20AND%20OPTIONS
|
|
694
712
|
"""
|
|
@@ -764,7 +782,7 @@ def main():
|
|
|
764
782
|
parser.add_argument('--darrc', type=str, help='Optional path to .darrc')
|
|
765
783
|
parser.add_argument('-l', '--list', action='store_true', help="List available archives.").completer = list_archive_completer
|
|
766
784
|
parser.add_argument('--list-contents', help="List the contents of the specified archive.").completer = list_archive_completer
|
|
767
|
-
parser.add_argument('--selection', help="
|
|
785
|
+
parser.add_argument('--selection', type=str, help="Selection string to pass to 'dar', e.g. --selection=\"-I '*.NEF'\"")
|
|
768
786
|
# parser.add_argument('-r', '--restore', nargs=1, type=str, help="Restore specified archive.")
|
|
769
787
|
parser.add_argument('-r', '--restore', type=str, help="Restore specified archive.").completer = list_archive_completer
|
|
770
788
|
parser.add_argument('--restore-dir', type=str, help="Directory to restore files to.")
|
|
@@ -847,6 +865,7 @@ def main():
|
|
|
847
865
|
start_time=int(time())
|
|
848
866
|
start_msgs.append(('dar-backup.py:', about.__version__))
|
|
849
867
|
logger.info(f"START TIME: {start_time}")
|
|
868
|
+
logger.debug(f"Command line: {get_invocation_command_line()}")
|
|
850
869
|
logger.debug(f"{'`Args`:\n'}{args}")
|
|
851
870
|
logger.debug(f"{'`Config_settings`:\n'}{config_settings}")
|
|
852
871
|
dar_properties = get_binary_info(command='dar')
|
|
@@ -858,9 +877,9 @@ def main():
|
|
|
858
877
|
start_msgs.append(('Config file:', os.path.abspath(args.config_file)))
|
|
859
878
|
start_msgs.append((".darrc location:", args.darrc))
|
|
860
879
|
|
|
861
|
-
args.
|
|
862
|
-
args.
|
|
863
|
-
args.
|
|
880
|
+
args.full_backup and start_msgs.append(("Type of backup:", "FULL"))
|
|
881
|
+
args.differential_backup and start_msgs.append(("Type of backup:", "DIFF"))
|
|
882
|
+
args.incremental_backup and start_msgs.append(("Type of backup:", "INCR"))
|
|
864
883
|
args.verbose and args.backup_definition and start_msgs.append(("Backup definition:", args.backup_definition))
|
|
865
884
|
if args.alternate_reference_archive:
|
|
866
885
|
args.verbose and start_msgs.append(("Alternate ref archive:", args.alternate_reference_archive))
|
|
@@ -874,8 +893,8 @@ def main():
|
|
|
874
893
|
args.verbose and start_msgs.append(("PAR2 enabled:", config_settings.par2_enabled))
|
|
875
894
|
args.verbose and start_msgs.append(("--do-not-compare:", args.do_not_compare))
|
|
876
895
|
|
|
877
|
-
|
|
878
|
-
print_aligned_settings(start_msgs)
|
|
896
|
+
highlight_keywords = ["--do-not", "alternate"] # TODO: add more dangerous keywords
|
|
897
|
+
print_aligned_settings(start_msgs, quiet=not args.verbose, highlight_keywords=highlight_keywords)
|
|
879
898
|
|
|
880
899
|
# sanity check
|
|
881
900
|
if args.backup_definition and not os.path.exists(os.path.join(config_settings.backup_d_dir, args.backup_definition)):
|
dar_backup/dar_backup_systemd.py
CHANGED
dar_backup/demo.py
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
|
+
# SPDX-License-Identifier: GPL-3.0-or-later
|
|
3
|
+
|
|
2
4
|
"""
|
|
3
|
-
|
|
5
|
+
demo.py source code is here: https://github.com/per2jensen/dar-backup/tree/main/v2/src/dar_backup/demo.py
|
|
4
6
|
This script is part of dar-backup, a backup solution for Linux using dar and systemd.
|
|
5
7
|
|
|
6
8
|
Licensed under GNU GENERAL PUBLIC LICENSE v3, see the supplied file "LICENSE" for details.
|
|
@@ -10,7 +12,9 @@ not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
|
10
12
|
See section 15 and section 16 in the supplied "LICENSE" file
|
|
11
13
|
|
|
12
14
|
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.
|
|
15
|
+
It is non-destructive and will not overwrite any existing files or directories under --override is used.
|
|
16
|
+
|
|
17
|
+
User can set ROOT_DIR, DIR_TO_BACKUP and BACKUP_DIR (destination for backups) via optins to override defaults.
|
|
14
18
|
"""
|
|
15
19
|
|
|
16
20
|
import argparse
|
|
@@ -19,120 +23,189 @@ import shutil
|
|
|
19
23
|
import sys
|
|
20
24
|
|
|
21
25
|
from . import __about__ as about
|
|
26
|
+
from . import util
|
|
27
|
+
|
|
28
|
+
from jinja2 import Environment, FileSystemLoader
|
|
22
29
|
from pathlib import Path
|
|
30
|
+
from typing import Dict, Tuple
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
CONFIG_DIR = util.normalize_dir(util.expand_path("~/.config/dar-backup"))
|
|
34
|
+
DAR_BACKUP_DIR = util.normalize_dir(util.expand_path("~/dar-backup"))
|
|
35
|
+
|
|
23
36
|
|
|
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
37
|
|
|
28
|
-
|
|
29
|
-
|
|
38
|
+
def check_directories(args, vars_map: Dict[str,str]) -> bool:
|
|
39
|
+
"""
|
|
40
|
+
Check if the directories exist and create them if they don't.
|
|
30
41
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
42
|
+
Returns:
|
|
43
|
+
bool: True if the directories were created successfully, False otherwise.
|
|
44
|
+
"""
|
|
45
|
+
result = True
|
|
46
|
+
for key in ("DAR_BACKUP_DIR","BACKUP_DIR","TEST_RESTORE_DIR","CONFIG_DIR","BACKUP_D_DIR"):
|
|
47
|
+
path = Path(vars_map[key])
|
|
48
|
+
if path.exists() and not args.override:
|
|
49
|
+
print(f"Directory '{path}' already exists")
|
|
50
|
+
result = False
|
|
51
|
+
return result
|
|
36
52
|
|
|
37
|
-
# Switch to ordered selection mode, which means that the following options
|
|
38
|
-
# will be considered top to bottom
|
|
39
|
-
-am
|
|
40
53
|
|
|
41
|
-
|
|
42
|
-
|
|
54
|
+
def generate_file(args, template: str, file_path: Path, vars_map: Dict[str, str], opts_dict: Dict[str, str]) -> bool:
|
|
55
|
+
"""
|
|
56
|
+
Generate a file using a Jinja2 template.
|
|
43
57
|
|
|
44
|
-
|
|
45
|
-
|
|
58
|
+
Args:
|
|
59
|
+
args: Command line arguments.
|
|
60
|
+
template (str): The name of the template file.
|
|
61
|
+
file_path (Path): The path where the generated file will be saved.
|
|
62
|
+
vars_map (Dict[str, str]): A dictionary containing variables for the template.
|
|
63
|
+
opts_dict (Dict[str, str]): A dictionary containing options given by user.
|
|
46
64
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
65
|
+
Returns:
|
|
66
|
+
bool: True if the file was generated successfully, False otherwise.
|
|
67
|
+
"""
|
|
68
|
+
current_script_dir = os.path.dirname(os.path.abspath(__file__))
|
|
69
|
+
env = Environment(loader=FileSystemLoader(current_script_dir))
|
|
70
|
+
tpl = env.get_template(template)
|
|
71
|
+
rendered = tpl.render(vars_map = vars_map, opts_dict = opts_dict)
|
|
72
|
+
if rendered is None:
|
|
73
|
+
print(f"Error: Template '{template}' could not be rendered.")
|
|
74
|
+
return False
|
|
75
|
+
if os.path.exists(file_path) and not args.override:
|
|
76
|
+
print(f"Error: File '{file_path}' already exists. Use --override to overwrite.")
|
|
77
|
+
return False
|
|
78
|
+
file_path.parent.mkdir(parents=True, exist_ok=True)
|
|
79
|
+
file_path.write_text(rendered)
|
|
80
|
+
print(f"File generated at '{file_path}'")
|
|
51
81
|
|
|
52
|
-
# compression level
|
|
53
|
-
-z5
|
|
54
82
|
|
|
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
83
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
84
|
+
def setup_dicts(args, vars_map: Dict[str, str]) -> Tuple[Dict[str, str], Dict[str, str]]:
|
|
85
|
+
"""
|
|
86
|
+
Override various entries in the dictionaries for jinja templating with user input.
|
|
87
|
+
|
|
88
|
+
Returns:
|
|
89
|
+
Tuple[Dict[str, str], Dict[str, str]]: A tuple containing the vars_map and opts_dict dictionaries.
|
|
90
|
+
"""
|
|
91
|
+
opts_dict = {}
|
|
92
|
+
if args.root_dir:
|
|
93
|
+
opts_dict["ROOT_DIR"] = args.root_dir
|
|
94
|
+
if args.dir_to_backup:
|
|
95
|
+
opts_dict["DIR_TO_BACKUP"] = args.dir_to_backup
|
|
96
|
+
if args.backup_dir:
|
|
97
|
+
opts_dict["BACKUP_DIR"] = args.backup_dir
|
|
98
|
+
|
|
99
|
+
for key, value in opts_dict.items():
|
|
100
|
+
vars_map[key] = value
|
|
101
|
+
|
|
102
|
+
return vars_map, opts_dict
|
|
65
103
|
|
|
66
104
|
|
|
67
105
|
def main():
|
|
68
106
|
parser = argparse.ArgumentParser(
|
|
69
|
-
description="Set up `dar-backup` on your system.",
|
|
107
|
+
description="Set up demo configuration for `dar-backup` on your system.",
|
|
70
108
|
)
|
|
71
109
|
parser.add_argument(
|
|
72
110
|
"-i", "--install",
|
|
73
111
|
action="store_true",
|
|
74
|
-
help="Deploy
|
|
112
|
+
help="Deploy demo config files and directories. Will not overwrite existing files or directories unless --override is used."
|
|
113
|
+
)
|
|
114
|
+
req = parser.add_argument_group(
|
|
115
|
+
'These options must be used together'
|
|
116
|
+
)
|
|
117
|
+
req.add_argument(
|
|
118
|
+
"--root-dir",
|
|
119
|
+
type=str,
|
|
120
|
+
help="Specify the root directory for the backup."
|
|
121
|
+
)
|
|
122
|
+
req.add_argument(
|
|
123
|
+
"--dir-to-backup",
|
|
124
|
+
type=str,
|
|
125
|
+
help="Directory to backup, relative to the root directory."
|
|
126
|
+
)
|
|
127
|
+
parser.add_argument(
|
|
128
|
+
"--backup-dir",
|
|
129
|
+
type=str,
|
|
130
|
+
help="Directory where backups and redundancy files are put"
|
|
131
|
+
)
|
|
132
|
+
parser.add_argument(
|
|
133
|
+
"--override",
|
|
134
|
+
action="store_true",
|
|
135
|
+
help="By default, the script will not overwrite existing files or directories. Use this option to override this behavior."
|
|
75
136
|
)
|
|
76
137
|
parser.add_argument(
|
|
77
138
|
"-v", "--version",
|
|
78
139
|
action="version",
|
|
79
|
-
version=f"%(prog)s version {about.__version__}, {
|
|
140
|
+
version=f"%(prog)s version {about.__version__}, {about.__license__}"
|
|
141
|
+
)
|
|
142
|
+
parser.add_argument(
|
|
143
|
+
"-g", "--generate",
|
|
144
|
+
action="store_true",
|
|
145
|
+
help="Generate config files and put them in /tmp/."
|
|
80
146
|
)
|
|
81
147
|
|
|
82
148
|
args = parser.parse_args()
|
|
83
149
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
150
|
+
group = [args.root_dir, args.dir_to_backup]
|
|
151
|
+
if any(group) and not all(group):
|
|
152
|
+
parser.error(
|
|
153
|
+
"Options --root-dir, --dir-to-backup must all be specified together."
|
|
154
|
+
)
|
|
155
|
+
exit(1)
|
|
156
|
+
|
|
157
|
+
args.root_dir = util.normalize_dir(util.expand_path(args.root_dir)) if args.root_dir else None
|
|
158
|
+
args.backup_dir = util.normalize_dir(util.expand_path(args.backup_dir)) if args.backup_dir else None
|
|
159
|
+
args.dir_to_backup = util.normalize_dir(util.expand_path(args.dir_to_backup)) if args.dir_to_backup else None
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
vars_map = {
|
|
164
|
+
# dar-backup.conf variables
|
|
165
|
+
"CONFIG_DIR" : CONFIG_DIR,
|
|
166
|
+
"DAR_BACKUP_DIR" : DAR_BACKUP_DIR,
|
|
167
|
+
"BACKUP_DIR" : os.path.join(DAR_BACKUP_DIR, "backups"),
|
|
168
|
+
"BACKUP_D_DIR" : os.path.join(CONFIG_DIR, "backup.d"),
|
|
169
|
+
"TEST_RESTORE_DIR" : os.path.join(DAR_BACKUP_DIR, "restore"),
|
|
170
|
+
# backup definition variables
|
|
171
|
+
"ROOT_DIR" : util.normalize_dir(util.expand_path("$HOME")),
|
|
172
|
+
"DIR_TO_BACKUP" : ".config/dar-backup",
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
vars_map, opts_dict = setup_dicts(args, vars_map)
|
|
177
|
+
|
|
178
|
+
if args.generate:
|
|
179
|
+
print("Generating backup definition file...")
|
|
180
|
+
vars_map["DAR_BACKUP_DIR"] = "/tmp"
|
|
181
|
+
args.override = True
|
|
182
|
+
generate_file(args, "demo_backup_def.j2", Path("/tmp/dar-backup/backup.d/demo"), vars_map, opts_dict)
|
|
183
|
+
vars_map["CONFIG_DIR"] = "/tmp"
|
|
184
|
+
generate_file(args, "dar-backup.conf.j2", Path("/tmp/dar-backup.conf"), vars_map, opts_dict)
|
|
185
|
+
elif args.install:
|
|
186
|
+
if not check_directories(args, vars_map):
|
|
187
|
+
print("Error: One or more directories already exist.\nSpecify non-existent directories or use --override to overwrite.")
|
|
94
188
|
sys.exit(1)
|
|
95
189
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
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)
|
|
190
|
+
Path(vars_map["DAR_BACKUP_DIR"]).mkdir(parents=True, exist_ok=True)
|
|
191
|
+
Path(vars_map["BACKUP_DIR"]).mkdir(parents=True, exist_ok=True)
|
|
192
|
+
Path(vars_map["TEST_RESTORE_DIR"]).mkdir(parents=True, exist_ok=True)
|
|
193
|
+
Path(vars_map["CONFIG_DIR"]).mkdir(parents=True, exist_ok=True)
|
|
194
|
+
Path(vars_map["BACKUP_D_DIR"]).mkdir(parents=True, exist_ok=True)
|
|
195
|
+
print(f"Directories created.")
|
|
196
|
+
|
|
197
|
+
generate_file(args, "demo_backup_def.j2", Path(vars_map["BACKUP_D_DIR"]).joinpath("demo"), vars_map, opts_dict)
|
|
198
|
+
generate_file(args, "dar-backup.conf.j2", Path(vars_map["CONFIG_DIR"]).joinpath("dar-backup.conf"), vars_map, opts_dict)
|
|
128
199
|
|
|
129
|
-
print("1. Now run `manager --create` to create the catalog database.")
|
|
200
|
+
print("1. Now run `manager --create-db` to create the catalog database.")
|
|
130
201
|
print("2. Then you can run `dar-backup --full-backup` to create a backup.")
|
|
131
202
|
print("3. List backups with `dar-backup --list`")
|
|
132
203
|
print("4. List contents of a backup with `dar-backup --list-contents <backup-name>`")
|
|
204
|
+
else:
|
|
205
|
+
parser.print_help()
|
|
206
|
+
sys.exit(1)
|
|
133
207
|
|
|
134
208
|
sys.exit(0)
|
|
135
209
|
|
|
136
|
-
|
|
137
210
|
if __name__ == "__main__":
|
|
138
211
|
main()
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
|
|
2
|
+
# SPDX-License-Identifier: GPL-3.0-or-later
|
|
3
|
+
|
|
4
|
+
# ------------------------------------------------------------------------
|
|
5
|
+
# Demo of a `dar-backup` definition file
|
|
6
|
+
# This file was generated by dar-backup's `demo` program.
|
|
7
|
+
#
|
|
8
|
+
{%- if opts_dict | length > 0 %}
|
|
9
|
+
# Options given to the `demo` program:
|
|
10
|
+
{% endif %}
|
|
11
|
+
{%- if opts_dict.ROOT_DIR -%}
|
|
12
|
+
# --root-dir : {{ opts_dict.ROOT_DIR }}
|
|
13
|
+
{% endif %}
|
|
14
|
+
{%- if opts_dict.DIR_TO_BACKUP -%}
|
|
15
|
+
# --dir-to-backup : {{ opts_dict.DIR_TO_BACKUP }}
|
|
16
|
+
{% endif -%}
|
|
17
|
+
{%- if opts_dict.BACKUP_DIR -%}
|
|
18
|
+
# --backup-dir : {{ opts_dict.BACKUP_DIR }}
|
|
19
|
+
{% endif %}
|
|
20
|
+
#
|
|
21
|
+
# Variables used to generate this file:
|
|
22
|
+
# =====================================
|
|
23
|
+
{% for k,v in vars_map|dictsort %}# {{ k }} : {{ v }}
|
|
24
|
+
{% endfor -%}
|
|
25
|
+
# ------------------------------------------------------------------------
|
|
26
|
+
|
|
27
|
+
# Switch to ordered selection mode, which means that the following options
|
|
28
|
+
# will be considered top to bottom
|
|
29
|
+
-am
|
|
30
|
+
|
|
31
|
+
# Backup Root dir
|
|
32
|
+
{%- if vars_map.ROOT_DIR %}
|
|
33
|
+
-R {{ vars_map.ROOT_DIR }}
|
|
34
|
+
{% endif -%}
|
|
35
|
+
|
|
36
|
+
{% if vars_map.DIR_TO_BACKUP %}
|
|
37
|
+
# Directories to backup below the Root dir
|
|
38
|
+
-g {{ vars_map.DIR_TO_BACKUP }}
|
|
39
|
+
|
|
40
|
+
# This is an example of exclusion of a `.private` directory inside the
|
|
41
|
+
# directory that is backed up
|
|
42
|
+
-P {{ vars_map.DIR_TO_BACKUP }}/.private
|
|
43
|
+
{%- else %}
|
|
44
|
+
# Examples of directories to exclude below the Root dir
|
|
45
|
+
-P mnt
|
|
46
|
+
-P .cache
|
|
47
|
+
{% endif %}
|
|
48
|
+
|
|
49
|
+
# compression level
|
|
50
|
+
-z5
|
|
51
|
+
|
|
52
|
+
# no overwrite, if you rerun a backup, 'dar' halts and asks what to do
|
|
53
|
+
# as `dar-backup` gives the `-Q` option to `dar`, the net effect of `-n` and `-Q` is
|
|
54
|
+
# that `dar` will quit and not overwrite the existing backup
|
|
55
|
+
-n
|
|
56
|
+
|
|
57
|
+
# size of each slice in the archive (10G is 10 Gigabytes)
|
|
58
|
+
--slice 10G
|
|
59
|
+
|
|
60
|
+
# bypass directores marked as cache directories
|
|
61
|
+
# http://dar.linux.free.fr/doc/Features.html
|
|
62
|
+
--cache-directory-tagging
|