dar-backup 0.6.14__py3-none-any.whl → 0.6.15__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/__about__.py +1 -1
- dar_backup/dar_backup.py +42 -25
- dar_backup/util.py +1 -1
- {dar_backup-0.6.14.dist-info → dar_backup-0.6.15.dist-info}/METADATA +1 -1
- {dar_backup-0.6.14.dist-info → dar_backup-0.6.15.dist-info}/RECORD +8 -8
- {dar_backup-0.6.14.dist-info → dar_backup-0.6.15.dist-info}/WHEEL +0 -0
- {dar_backup-0.6.14.dist-info → dar_backup-0.6.15.dist-info}/entry_points.txt +0 -0
- {dar_backup-0.6.14.dist-info → dar_backup-0.6.15.dist-info}/licenses/LICENSE +0 -0
dar_backup/__about__.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "0.6.
|
|
1
|
+
__version__ = "0.6.15"
|
dar_backup/dar_backup.py
CHANGED
|
@@ -34,7 +34,7 @@ from dar_backup.util import RestoreError
|
|
|
34
34
|
|
|
35
35
|
logger = None
|
|
36
36
|
|
|
37
|
-
def generic_backup(type: str, command: List[str], backup_file: str, backup_definition: str, darrc: str, config_settings: ConfigSettings, args: argparse.Namespace):
|
|
37
|
+
def generic_backup(type: str, command: List[str], backup_file: str, backup_definition: str, darrc: str, config_settings: ConfigSettings, args: argparse.Namespace) -> List[str]:
|
|
38
38
|
"""
|
|
39
39
|
Performs a backup using the 'dar' command.
|
|
40
40
|
|
|
@@ -54,10 +54,14 @@ def generic_backup(type: str, command: List[str], backup_file: str, backup_defin
|
|
|
54
54
|
config_settings (ConfigSettings): An instance of the ConfigSettings class.
|
|
55
55
|
|
|
56
56
|
|
|
57
|
-
|
|
58
57
|
Raises:
|
|
59
|
-
BackupError: If an error occurs during the backup process.
|
|
58
|
+
BackupError: If an error leading to a bad backup occurs during the backup process.
|
|
59
|
+
|
|
60
|
+
Returns:
|
|
61
|
+
List of tuples (<msg>, <exit_code>) of errors not considered critical enough for raising an exception
|
|
60
62
|
"""
|
|
63
|
+
result: List[tuple] = []
|
|
64
|
+
|
|
61
65
|
logger.info(f"===> Starting {type} backup for {backup_definition}")
|
|
62
66
|
logger.info(f"Running command: {' '.join(map(shlex.quote, command))}")
|
|
63
67
|
try:
|
|
@@ -75,7 +79,11 @@ def generic_backup(type: str, command: List[str], backup_file: str, backup_defin
|
|
|
75
79
|
if command_result.returncode == 0:
|
|
76
80
|
logger.info(f"Catalog for archive '{backup_file}' added successfully to its manager.")
|
|
77
81
|
else:
|
|
78
|
-
|
|
82
|
+
msg = f"Catalog for archive '{backup_file}' not added."
|
|
83
|
+
logger.error(msg)
|
|
84
|
+
result.append((msg, 1))
|
|
85
|
+
|
|
86
|
+
return result
|
|
79
87
|
|
|
80
88
|
except subprocess.CalledProcessError as e:
|
|
81
89
|
logger.error(f"Backup command failed: {e}")
|
|
@@ -83,7 +91,7 @@ def generic_backup(type: str, command: List[str], backup_file: str, backup_defin
|
|
|
83
91
|
except Exception as e:
|
|
84
92
|
logger.exception(f"Unexpected error during backup")
|
|
85
93
|
raise BackupError(f"Unexpected error during backup: {e}") from e
|
|
86
|
-
|
|
94
|
+
|
|
87
95
|
|
|
88
96
|
|
|
89
97
|
def find_files_with_paths(xml_doc: str):
|
|
@@ -184,7 +192,7 @@ def verify(args: argparse.Namespace, backup_file: str, backup_definition: str, c
|
|
|
184
192
|
"""
|
|
185
193
|
result = True
|
|
186
194
|
command = ['dar', '-t', backup_file, '-Q']
|
|
187
|
-
logger.
|
|
195
|
+
logger.debug(f"Running command: {' '.join(map(shlex.quote, command))}")
|
|
188
196
|
process = run_command(command, config_settings.command_timeout_secs)
|
|
189
197
|
if process.returncode == 0:
|
|
190
198
|
logger.info("Archive integrity test passed.")
|
|
@@ -208,8 +216,9 @@ def verify(args: argparse.Namespace, backup_file: str, backup_definition: str, c
|
|
|
208
216
|
root_path = None
|
|
209
217
|
for line in backup_definition_content:
|
|
210
218
|
line = line.strip()
|
|
211
|
-
|
|
212
|
-
|
|
219
|
+
match = re.match(r'^\s*-R\s+(.*)', line)
|
|
220
|
+
if match:
|
|
221
|
+
root_path = match.group(1).strip()
|
|
213
222
|
break
|
|
214
223
|
if root_path is None:
|
|
215
224
|
msg = f"No Root (-R) path found in the backup definition file: '{backup_definition}', restore verification skipped"
|
|
@@ -223,15 +232,15 @@ def verify(args: argparse.Namespace, backup_file: str, backup_definition: str, c
|
|
|
223
232
|
random_files = random.sample(files, no_files_verification)
|
|
224
233
|
for restored_file_path in random_files:
|
|
225
234
|
try:
|
|
226
|
-
logger.info(f"Restoring file: '{restored_file_path}' from backup to: '{config_settings.test_restore_dir}' for file comparing")
|
|
235
|
+
args.verbose and logger.info(f"Restoring file: '{restored_file_path}' from backup to: '{config_settings.test_restore_dir}' for file comparing")
|
|
227
236
|
command = ['dar', '-x', backup_file, '-g', restored_file_path.lstrip("/"), '-R', config_settings.test_restore_dir, '-Q', '-B', args.darrc, 'restore-options']
|
|
228
|
-
logger.info(f"Running command: {' '.join(map(shlex.quote, command))}")
|
|
237
|
+
args.verbose and logger.info(f"Running command: {' '.join(map(shlex.quote, command))}")
|
|
229
238
|
process = run_command(command, config_settings.command_timeout_secs)
|
|
230
239
|
if process.returncode != 0:
|
|
231
240
|
raise Exception(str(process))
|
|
232
241
|
|
|
233
242
|
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):
|
|
234
|
-
logger.info(f"Success: file '{restored_file_path}' matches the original")
|
|
243
|
+
args.verbose and logger.info(f"Success: file '{restored_file_path}' matches the original")
|
|
235
244
|
else:
|
|
236
245
|
raise BackupError(f"Failure: file '{restored_file_path}' did not match the original")
|
|
237
246
|
except PermissionError:
|
|
@@ -295,11 +304,11 @@ def get_backed_up_files(backup_name: str, backup_dir: str):
|
|
|
295
304
|
Returns:
|
|
296
305
|
list: A list of file paths for all backed up files in the DAR archive.
|
|
297
306
|
"""
|
|
298
|
-
logger.debug(f"Getting backed up files from DAR archive
|
|
307
|
+
logger.debug(f"Getting backed up files in xml from DAR archive: '{backup_name}'")
|
|
299
308
|
backup_path = os.path.join(backup_dir, backup_name)
|
|
300
309
|
try:
|
|
301
310
|
command = ['dar', '-l', backup_path, '-am', '-as', "-Txml" , '-Q']
|
|
302
|
-
logger.
|
|
311
|
+
logger.debug(f"Running command: {' '.join(map(shlex.quote, command))}")
|
|
303
312
|
command_result = run_command(command)
|
|
304
313
|
# Parse the XML data
|
|
305
314
|
file_paths = find_files_with_paths(command_result.stdout)
|
|
@@ -383,7 +392,7 @@ def perform_backup(args: argparse.Namespace, config_settings: ConfigSettings, ba
|
|
|
383
392
|
backup_type: Type of backup (FULL, DIFF, INCR).
|
|
384
393
|
|
|
385
394
|
Returns:
|
|
386
|
-
|
|
395
|
+
List[tuples] - each tuple consists of (<str message>, <exit code>)
|
|
387
396
|
"""
|
|
388
397
|
backup_definitions = []
|
|
389
398
|
results: List[tuple] = []
|
|
@@ -443,7 +452,8 @@ def perform_backup(args: argparse.Namespace, config_settings: ConfigSettings, ba
|
|
|
443
452
|
command = create_backup_command(backup_type, backup_file, args.darrc, backup_definition_path, latest_base_backup)
|
|
444
453
|
|
|
445
454
|
# Perform backup
|
|
446
|
-
generic_backup(backup_type, command, backup_file, backup_definition_path, args.darrc, config_settings, args)
|
|
455
|
+
backup_result = generic_backup(backup_type, command, backup_file, backup_definition_path, args.darrc, config_settings, args)
|
|
456
|
+
results.extend(backup_result)
|
|
447
457
|
|
|
448
458
|
logger.info("Starting verification...")
|
|
449
459
|
verify_result = verify(args, backup_file, backup_definition_path, config_settings)
|
|
@@ -458,7 +468,7 @@ def perform_backup(args: argparse.Namespace, config_settings: ConfigSettings, ba
|
|
|
458
468
|
logger.info("par2 files completed successfully.")
|
|
459
469
|
|
|
460
470
|
except Exception as e:
|
|
461
|
-
results.
|
|
471
|
+
results.append((repr(e), 1))
|
|
462
472
|
logger.exception(f"Error during {backup_type} backup process, continuing to next backup definition.")
|
|
463
473
|
|
|
464
474
|
logger.trace(f"perform_backup() results[]: {results}")
|
|
@@ -519,8 +529,14 @@ def filter_darrc_file(darrc_path):
|
|
|
519
529
|
The filtered version is stored in a uniquely named file in the home directory of the user running the script.
|
|
520
530
|
The file permissions are set to 440.
|
|
521
531
|
|
|
522
|
-
:
|
|
523
|
-
|
|
532
|
+
Params:
|
|
533
|
+
darrc_path: Path to the original .darrc file.
|
|
534
|
+
|
|
535
|
+
Raises:
|
|
536
|
+
RuntimeError if something went wrong
|
|
537
|
+
|
|
538
|
+
Returns:
|
|
539
|
+
Path to the filtered .darrc file.
|
|
524
540
|
"""
|
|
525
541
|
# Define options to filter out
|
|
526
542
|
options_to_remove = {"-vt", "-vs", "-vd", "-vf", "-va"}
|
|
@@ -631,8 +647,8 @@ def requirements(type: str, config_setting: ConfigSettings):
|
|
|
631
647
|
script = config_setting.config[type][key]
|
|
632
648
|
try:
|
|
633
649
|
result = subprocess.run(script, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, shell=True, check=True)
|
|
634
|
-
logger.
|
|
635
|
-
logger.
|
|
650
|
+
logger.debug(f"{type} {key}: '{script}' run, return code: {result.returncode}")
|
|
651
|
+
logger.debug(f"{type} stdout:\n{result.stdout}")
|
|
636
652
|
if result.returncode != 0:
|
|
637
653
|
logger.error(f"{type} stderr:\n{result.stderr}")
|
|
638
654
|
raise RuntimeError(f"{type} {key}: '{script}' failed, return code: {result.returncode}")
|
|
@@ -711,9 +727,9 @@ def main():
|
|
|
711
727
|
exit(127)
|
|
712
728
|
|
|
713
729
|
if args.suppress_dar_msg:
|
|
714
|
-
logger.info("Suppressing dar messages: -vt, -vs, -vd, -vf
|
|
730
|
+
logger.info("Suppressing dar messages, do not use options: -vt, -vs, -vd, -vf, -va")
|
|
715
731
|
args.darrc = filter_darrc_file(args.darrc)
|
|
716
|
-
logger.debug(f"Filtered .darrc file
|
|
732
|
+
logger.debug(f"Filtered .darrc file: {args.darrc}")
|
|
717
733
|
|
|
718
734
|
start_time=int(time())
|
|
719
735
|
logger.info(f"=====================================")
|
|
@@ -781,12 +797,13 @@ def main():
|
|
|
781
797
|
end_time=int(time())
|
|
782
798
|
logger.info(f"END TIME: {end_time}")
|
|
783
799
|
# Clean up
|
|
784
|
-
if
|
|
800
|
+
if os.path.exists(args.darrc) and os.path.dirname(args.darrc) == os.path.expanduser("~"):
|
|
785
801
|
if args.darrc.startswith("filtered_darrc_"):
|
|
786
802
|
os.remove(args.darrc)
|
|
787
|
-
logger.
|
|
803
|
+
logger.debug(f"Removed filtered .darrc: {args.darrc}")
|
|
788
804
|
|
|
789
805
|
|
|
806
|
+
# Determine exit code
|
|
790
807
|
error = False
|
|
791
808
|
logger.debug(f"results[]: {results}")
|
|
792
809
|
if results:
|
|
@@ -795,7 +812,7 @@ def main():
|
|
|
795
812
|
if isinstance(result, tuple) and len(result) == 2:
|
|
796
813
|
msg, exit_code = result
|
|
797
814
|
logger.debug(f"exit code: {exit_code}, msg: {msg}")
|
|
798
|
-
if exit_code
|
|
815
|
+
if exit_code > 0:
|
|
799
816
|
error = True
|
|
800
817
|
args.verbose and print(msg)
|
|
801
818
|
else:
|
dar_backup/util.py
CHANGED
|
@@ -72,7 +72,7 @@ def setup_logging(log_file: str, command_output_log_file: str, log_level: str =
|
|
|
72
72
|
stdout_handler = logging.StreamHandler(sys.stdout)
|
|
73
73
|
stdout_handler.setFormatter(formatter)
|
|
74
74
|
logger.addHandler(stdout_handler)
|
|
75
|
-
secondary_logger.addHandler(stdout_handler)
|
|
75
|
+
#secondary_logger.addHandler(stdout_handler)
|
|
76
76
|
|
|
77
77
|
return logger
|
|
78
78
|
except Exception as e:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dar-backup
|
|
3
|
-
Version: 0.6.
|
|
3
|
+
Version: 0.6.15
|
|
4
4
|
Summary: A script to do full, differential and incremental backups using dar. Some files are restored from the backups during verification, after which par2 redundancy files are created. The script also has a cleanup feature to remove old backups and par2 files.
|
|
5
5
|
Project-URL: Homepage, https://github.com/per2jensen/dar-backup/tree/main/v2
|
|
6
6
|
Project-URL: Changelog, https://github.com/per2jensen/dar-backup/blob/main/v2/Changelog.md
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
dar_backup/.darrc,sha256=-aerqivZmOsW_XBCh9IfbYTUvw0GkzDSr3Vx4GcNB1g,2113
|
|
2
|
-
dar_backup/__about__.py,sha256=
|
|
2
|
+
dar_backup/__about__.py,sha256=9pegTLVQP2o3JePGrLB6muzXif64umv9ihLnS7LsH8E,22
|
|
3
3
|
dar_backup/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
4
|
dar_backup/clean_log.py,sha256=cGhtKYnQJ2ceNQfw5XcCln_WNBasbmlfhO3kRydjDNk,5196
|
|
5
5
|
dar_backup/cleanup.py,sha256=1g2si9-jPEL8T4OKaiGSKFGsF6rWh-Ke1-zQHE7HaPc,11703
|
|
6
6
|
dar_backup/config_settings.py,sha256=uicCq6FnpxPFzbv7xfYSXNnQf1tfLk1Z3VIO9M71fsE,4659
|
|
7
7
|
dar_backup/dar-backup.conf,sha256=-wXqP4vj5TS7cCfMJN1nbk-1Sqkq00Tg22ySQXynUF4,902
|
|
8
|
-
dar_backup/dar_backup.py,sha256=
|
|
8
|
+
dar_backup/dar_backup.py,sha256=TrYMYNbQ9jWabWGc7GA5p2ezprLqMUu9O0zt6CQ1QSA,37867
|
|
9
9
|
dar_backup/installer.py,sha256=ehp4KSgTc8D9Edsyve5v3NY2MuDbuTFYQQPgou8woV8,4331
|
|
10
10
|
dar_backup/manager.py,sha256=sQl0xdWwBgui11S9Ekg0hOSC4gt89nz_Z8Bt8IPXCDw,21640
|
|
11
|
-
dar_backup/util.py,sha256=
|
|
12
|
-
dar_backup-0.6.
|
|
13
|
-
dar_backup-0.6.
|
|
14
|
-
dar_backup-0.6.
|
|
15
|
-
dar_backup-0.6.
|
|
16
|
-
dar_backup-0.6.
|
|
11
|
+
dar_backup/util.py,sha256=F6U-e-WugxCxLPVoiWsM6_YO8VrDw1wdgGvtnGnig2I,12279
|
|
12
|
+
dar_backup-0.6.15.dist-info/METADATA,sha256=JwTNDXCElF0elUKBhwjrDUASEkpLIjgdtfEML5NvXUY,72750
|
|
13
|
+
dar_backup-0.6.15.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
14
|
+
dar_backup-0.6.15.dist-info/entry_points.txt,sha256=Z7P5BUbhtJxo8_nB9qNIMay2eGDbsMKB3Fjwv3GMa4g,202
|
|
15
|
+
dar_backup-0.6.15.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
|
16
|
+
dar_backup-0.6.15.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|