dar-backup 0.6.0__py3-none-any.whl → 0.6.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/__about__.py CHANGED
@@ -1 +1 @@
1
- __version__ = "0.6.0"
1
+ __version__ = "0.6.1"
dar_backup/dar_backup.py CHANGED
@@ -64,23 +64,18 @@ def backup(backup_file: str, backup_definition: str, darrc: str, config_setting
64
64
  logger.info(f"Running command: {' '.join(map(shlex.quote, command))}")
65
65
  try:
66
66
  process = run_command(command, config_settings.command_timeout_secs)
67
- stdout, stderr = process.stdout, process.stderr
67
+ logger.info("Back from run_command")
68
68
  if process.returncode == 0:
69
69
  logger.info("FULL backup completed successfully.")
70
70
  elif process.returncode == 5:
71
71
  logger.warning("Backup completed with some files not backed up, this can happen if files are changed/deleted during the backup.")
72
72
  else:
73
- logger.error("dar return code: ", process.returncode)
74
- logger.error("stdout: ", stdout)
75
- logger.error("stderr: ", stderr)
76
- raise Exception(stderr)
73
+ raise Exception(str(process))
77
74
  except subprocess.CalledProcessError as e:
78
75
  logger.error(f"Backup command failed: {e}")
79
76
  raise BackupError(f"Backup command failed: {e}") from e
80
77
  except Exception as e:
81
78
  logger.exception(f"Unexpected error during backup")
82
- logger.error("stderr:", stderr)
83
- logger.error("stdout: ", stdout)
84
79
  raise BackupError(f"Unexpected error during backup: {e}") from e
85
80
 
86
81
 
@@ -120,16 +115,12 @@ def differential_backup(backup_file: str, backup_definition: str, base_backup_fi
120
115
  logger.info(f"Running command: {' '.join(map(shlex.quote, command))}")
121
116
  try:
122
117
  process = run_command(command)
123
- stdout, stderr = process.stdout, process.stderr
124
118
  if process.returncode == 0:
125
119
  logger.info("DIFF backup completed successfully.")
126
120
  elif process.returncode == 5:
127
121
  logger.warning("Backup completed with some files not backed up, this can happen if files are changed/deleted during the backup.")
128
122
  else:
129
- logger.error("dar return code: ", process.returncode)
130
- logger.error("stdout: ", stdout)
131
- logger.error("stderr: ", stderr)
132
- raise Exception(stderr)
123
+ raise Exception(str(process))
133
124
  except subprocess.CalledProcessError as e:
134
125
  logger.error(f"Differential backup command failed: {e}")
135
126
  raise DifferentialBackupError(f"Differential backup command failed: {e}") from e
@@ -174,22 +165,17 @@ def incremental_backup(backup_file: str, backup_definition: str, last_backup_fil
174
165
  logger.info(f"Running command: {' '.join(map(shlex.quote, command))}")
175
166
  try:
176
167
  process = run_command(command, config_settings.command_timeout_secs)
177
- stdout, stderr = process.stdout, process.stderr
178
168
  if process.returncode == 0:
179
169
  logger.info("INCR backup completed successfully.")
180
170
  elif process.returncode == 5:
181
171
  logger.warning("Backup completed with some files not backed up, this can happen if files are changed/deleted during the backup.")
182
172
  else:
183
- logger.error("dar return code: ", process.returncode)
184
- logger.error("stdout: ", stdout)
185
- logger.error("stderr: ", stderr)
186
- raise Exception(stderr)
173
+ raise Exception(str(process))
187
174
  except subprocess.CalledProcessError as e:
188
175
  logger.error(f"Incremental backup command failed: {e}")
189
176
  raise IncrementalBackupError(f"Incremental backup command failed: {e}") from e
190
177
  except Exception as e:
191
178
  logger.exception(f"Unexpected error during incremental backup")
192
- logger.error("Exception details:", exc_info=True)
193
179
  raise IncrementalBackupError(f"Unexpected error during incremental backup: {e}") from e
194
180
 
195
181
 
@@ -286,11 +272,10 @@ def verify(args: argparse.Namespace, backup_file: str, backup_definition: str, c
286
272
  command = ['dar', '-t', backup_file, '-Q']
287
273
  logger.info(f"Running command: {' '.join(map(shlex.quote, command))}")
288
274
  process = run_command(command, config_settings.command_timeout_secs)
289
- stdout, stderr = process.stdout, process.stderr
290
275
  if process.returncode == 0:
291
276
  logger.info("Archive integrity test passed.")
292
277
  else:
293
- raise Exception(stderr)
278
+ raise Exception(str(process))
294
279
 
295
280
  if args.do_not_compare:
296
281
  return result
@@ -325,10 +310,8 @@ def verify(args: argparse.Namespace, backup_file: str, backup_definition: str, c
325
310
  command = ['dar', '-x', backup_file, '-g', restored_file_path.lstrip("/"), '-R', config_settings.test_restore_dir, '-O', '-Q']
326
311
  logger.info(f"Running command: {' '.join(map(shlex.quote, command))}")
327
312
  process = run_command(command, config_settings.command_timeout_secs)
328
- stdout, stderr = process.stdout, process.stderr
329
313
  if process.returncode != 0:
330
- logger.error(f"Restore failed, dar return code: {process.returncode}.")
331
- raise Exception(stderr)
314
+ raise Exception(str(process))
332
315
 
333
316
  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):
334
317
  logger.info(f"Success: file '{restored_file_path}' matches the original")
@@ -365,17 +348,13 @@ def restore_backup(backup_name: str, config_settings: ConfigSettings, restore_di
365
348
  logger.info(f"Running command: {' '.join(map(shlex.quote, command))}")
366
349
  try:
367
350
  process = run_command(command, config_settings.command_timeout_secs)
368
- stdout, stderr = process.stdout, process.stderr
351
+ if process.returncode == 0:
352
+ logger.info(f"Restore completed successfully to: '{restore_dir}'")
353
+ else:
354
+ raise Exception(str(process))
369
355
  except subprocess.CalledProcessError as e:
370
- logger.error("Exception details:", exc_info=True)
371
- logger.error(f"stdout: {stdout}")
372
- logger.error(f"stderr: {stderr}")
373
356
  raise RestoreError(f"Restore command failed: {e}") from e
374
357
  except Exception as e:
375
- logger.exception(f"Unexpected error during restore")
376
- logger.error("Exception details:", exc_info=True)
377
- logger.error(f"stdout: {stdout}")
378
- logger.error(f"stderr: {stderr}")
379
358
  raise RestoreError(f"Unexpected error during restore: {e}") from e
380
359
 
381
360
 
@@ -393,19 +372,24 @@ def get_backed_up_files(backup_name: str, backup_dir: str):
393
372
  """
394
373
  logger.debug(f"Getting backed up files from DAR archive in xml: '{backup_name}'")
395
374
  backup_path = os.path.join(backup_dir, backup_name)
396
- command = ['dar', '-l', backup_path, '-am', '-as', "-Txml" , '-Q']
397
- logger.info(f"Running command: {' '.join(map(shlex.quote, command))}")
398
- process = run_command(command)
399
- stdout,stderr = process.stdout, process.stderr
400
- # Parse the XML data
401
- root = ET.fromstring(stdout)
402
- output = None # help gc
403
- # Extract full paths and file size for all <File> elements
404
- file_paths = find_files_with_paths(root)
405
- root = None # help gc
406
- logger.trace(f"Backed up files in dar archive: '{backup_name}'")
407
- logger.trace(file_paths)
408
- return file_paths
375
+ try:
376
+ command = ['dar', '-l', backup_path, '-am', '-as', "-Txml" , '-Q']
377
+ logger.info(f"Running command: {' '.join(map(shlex.quote, command))}")
378
+ process = run_command(command)
379
+ # Parse the XML data
380
+ root = ET.fromstring(process.stdout)
381
+ output = None # help gc
382
+ # Extract full paths and file size for all <File> elements
383
+ file_paths = find_files_with_paths(root)
384
+ root = None # help gc
385
+ logger.trace(str(process))
386
+ logger.trace(file_paths)
387
+ return file_paths
388
+ except subprocess.CalledProcessError as e:
389
+ logger.error(f"Error listing backed up files from DAR archive: '{backup_name}'")
390
+ raise BackupError(f"Error listing backed up files from DAR archive: '{backup_name}'") from e
391
+ except Exception as e:
392
+ raise RuntimeError(f"Unexpected error listing backed up files from DAR archive: '{backup_name}'") from e
409
393
 
410
394
 
411
395
  def list_contents(backup_name, backup_dir, selection=None):
@@ -421,17 +405,26 @@ def list_contents(backup_name, backup_dir, selection=None):
421
405
  None
422
406
  """
423
407
  backup_path = os.path.join(backup_dir, backup_name)
424
- command = ['dar', '-l', backup_path, '-am', '-as', '-Q']
425
- if selection:
426
- selection_criteria = shlex.split(selection)
427
- command.extend(selection_criteria)
428
- logger.info(f"Running command: {' '.join(map(shlex.quote, command))}")
429
- process = run_command(command)
430
- stdout,stderr = process.stdout, process.stderr
408
+ try:
409
+ command = ['dar', '-l', backup_path, '-am', '-as', '-Q']
410
+ if selection:
411
+ selection_criteria = shlex.split(selection)
412
+ command.extend(selection_criteria)
413
+ logger.info(f"Running command: {' '.join(map(shlex.quote, command))}")
414
+ process = run_command(command)
415
+ stdout,stderr = process.stdout, process.stderr
416
+ if process.returncode != 0:
417
+ logger.error(f"Error listing contents of backup: '{backup_name}'")
418
+ raise subprocess.CalledProcessError(str(process))
419
+ for line in stdout.splitlines():
420
+ if "[--- REMOVED ENTRY ----]" in line or "[Saved]" in line:
421
+ print(line)
422
+ except subprocess.CalledProcessError as e:
423
+ logger.error(f"Error listing contents of backup: '{backup_name}'")
424
+ raise BackupError(f"Error listing contents of backup: '{backup_name}'") from e
425
+ except Exception as e:
426
+ raise RuntimeError(f"Unexpected error listing contents of backup: '{backup_name}'") from e
431
427
 
432
- for line in stdout.splitlines():
433
- if "[--- REMOVED ENTRY ----]" in line or "[Saved]" in line:
434
- print(line)
435
428
 
436
429
 
437
430
  def perform_backup(args: argparse.Namespace, config_settings: ConfigSettings, backup_type: str):
@@ -634,7 +627,7 @@ def requirements(type: str, config_setting: ConfigSettings):
634
627
  for key in sorted(config_setting.config[type].keys()):
635
628
  script = config_setting.config[type][key]
636
629
  try:
637
- result = subprocess.run(script, shell=True, check=True)
630
+ result = subprocess.run(script, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, shell=True, check=True)
638
631
  logger.info(f"{type} {key}: '{script}' run, return code: {result.returncode}")
639
632
  logger.info(f"{type} stdout:\n{result.stdout}")
640
633
  if result.returncode != 0:
@@ -642,9 +635,7 @@ def requirements(type: str, config_setting: ConfigSettings):
642
635
  raise RuntimeError(f"{type} {key}: '{script}' failed, return code: {result.returncode}")
643
636
  except subprocess.CalledProcessError as e:
644
637
  logger.error(f"Error executing {key}: '{script}': {e}")
645
- if result:
646
- logger.error(f"{type} stderr:\n{result.stderr}")
647
- raise e
638
+ raise e
648
639
 
649
640
 
650
641
  def main():
@@ -754,23 +745,18 @@ def main():
754
745
 
755
746
  requirements('POSTREQ', config_settings)
756
747
 
757
-
748
+ args.verbose and print("\033[1m\033[32mSUCCESS\033[0m No errors encountered")
749
+ sys.exit(0)
758
750
  except Exception as e:
759
751
  logger.exception("An error occurred")
760
752
  logger.error("Exception details:", exc_info=True)
761
-
762
- end_time=int(time())
763
- logger.info(f"END TIME: {end_time}")
764
-
765
- error_lines = extract_error_lines(config_settings.logfile_location, start_time, end_time)
766
- if len(error_lines) > 0:
767
753
  args.verbose and print("\033[1m\033[31mErrors\033[0m encountered")
768
- for line in error_lines:
769
- args.verbose and print(line)
770
- sys.exit(1)
771
- else:
772
- args.verbose and print("\033[1m\033[32mSUCCESS\033[0m No errors encountered")
773
- sys.exit(0)
754
+ sys.exit(1)
755
+ finally:
756
+ end_time=int(time())
757
+ logger.info(f"END TIME: {end_time}")
758
+
759
+ # error_lines = extract_error_lines(config_settings.logfile_location, start_time, end_time)
774
760
 
775
761
 
776
762
  if __name__ == "__main__":
dar_backup/util.py CHANGED
@@ -81,9 +81,6 @@ def setup_logging(log_file: str, log_level: str, log_to_stdout: bool=False, logg
81
81
  stdout_handler.setFormatter(formatter)
82
82
  logger.addHandler(stdout_handler)
83
83
 
84
- # logging.basicConfig(filename=log_file, level=level_used,
85
- # format='%(asctime)s - %(levelname)s - %(message)s')
86
-
87
84
  except Exception as e:
88
85
  print("logging not initialized, exiting.")
89
86
  traceback.print_exc()
@@ -103,6 +100,9 @@ class CommandResult(NamedTuple):
103
100
  timeout: int
104
101
  command: list[str]
105
102
 
103
+ def __str__(self):
104
+ return f"CommandResult: [Return Code: '{self.returncode}', Stdout: '{self.stdout}', Stderr: '{self.stderr}', Timeout: '{self.timeout}', Command: '{self.command}']"
105
+
106
106
 
107
107
  def run_command(command: list[str], timeout: int=30) -> typing.NamedTuple:
108
108
  """
@@ -125,23 +125,22 @@ def run_command(command: list[str], timeout: int=30) -> typing.NamedTuple:
125
125
  subprocess.TimeoutExpired: if the command execution times out (see `timeout` parameter).
126
126
  Exception: raise exceptions during command runs.
127
127
  """
128
-
128
+ stdout = None
129
+ stderr = None
129
130
  try:
130
- logger.info(f"Running command: {command}")
131
+ logger.debug(f"Running command: {command}")
131
132
  process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
132
- stdout, stderr = process.communicate(timeout=30) # Wait with timeout
133
+ stdout, stderr = process.communicate(timeout) # Wait with timeout
134
+ result = CommandResult(process=process, stdout=stdout, stderr=stderr, returncode=process.returncode, timeout=timeout, command=command)
135
+ logger.debug(f"Command result: {str(result)}")
133
136
  except subprocess.TimeoutExpired:
134
137
  process.terminate()
135
138
  logger.error(f"Command: '{command}' timed out and was terminated.")
139
+ raise
136
140
  except Exception as e:
137
141
  logger.error(f"Error running command: {command}", exc_info=True)
138
142
  raise
139
- finally:
140
- if not stdout:
141
- stdout = None
142
- if not stderr:
143
- stderr = None
144
- return CommandResult(process=process, stdout=stdout, stderr=stderr, returncode=process.returncode, timeout=timeout, command=command)
143
+ return result
145
144
 
146
145
 
147
146
  def extract_error_lines(log_file_path: str, start_time: str, end_time: str):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dar-backup
3
- Version: 0.6.0
3
+ Version: 0.6.1
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
6
6
  Project-URL: Issues, https://github.com/per2jensen/dar-backup/issues
@@ -0,0 +1,13 @@
1
+ dar_backup/.darrc,sha256=ggex9N6eETOS6u003_QRRJMeWbveQfkT1lDBt0XpU-I,2112
2
+ dar_backup/__about__.py,sha256=XvHFZM0padtrqitt9-p2enlBUGqc6vGvWNLx2iJv09g,21
3
+ dar_backup/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
+ dar_backup/cleanup.py,sha256=DgmxSUwKrLLIQuYSIY_yTRhIuOMgI6ivjlQuH4u3wX4,9057
5
+ dar_backup/config_settings.py,sha256=CBMUhLOOZ-x7CRdS3vBDk4TYaGqC4N1Ot8IMH-qPaI0,3617
6
+ dar_backup/dar_backup.py,sha256=xnDuqkpo8q_X1FLKID9I_s__FQozl5EIrwNHdIJAd_0,37525
7
+ dar_backup/manager.py,sha256=lkw1ZAIdxY7WedLPKZMnHpuq_QbjkUdcG61ooiwUYpo,10197
8
+ dar_backup/util.py,sha256=6lPCFHr3MDdaLWAW9EDMZ4jdL7pt8rki-5dOXcesmP8,8955
9
+ dar_backup-0.6.1.dist-info/METADATA,sha256=m3P_iVauaMno0fAhc4iJgO_DS9FobsqV5StQzUdU6wY,22496
10
+ dar_backup-0.6.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
11
+ dar_backup-0.6.1.dist-info/entry_points.txt,sha256=x9vnW-JEl8mpDJC69f_XBcn0mBSkV1U0cyvFV-NAP1g,126
12
+ dar_backup-0.6.1.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
13
+ dar_backup-0.6.1.dist-info/RECORD,,
@@ -1,13 +0,0 @@
1
- dar_backup/.darrc,sha256=ggex9N6eETOS6u003_QRRJMeWbveQfkT1lDBt0XpU-I,2112
2
- dar_backup/__about__.py,sha256=DS49q_bFynltwBtgneHYeVXTHLg5bjAOFWvTkv-jYmY,21
3
- dar_backup/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
- dar_backup/cleanup.py,sha256=DgmxSUwKrLLIQuYSIY_yTRhIuOMgI6ivjlQuH4u3wX4,9057
5
- dar_backup/config_settings.py,sha256=CBMUhLOOZ-x7CRdS3vBDk4TYaGqC4N1Ot8IMH-qPaI0,3617
6
- dar_backup/dar_backup.py,sha256=Kz_Gwe9t6vHGk590D3pbqDe1ptyrhLNpQWCU8oDcqos,37870
7
- dar_backup/manager.py,sha256=lkw1ZAIdxY7WedLPKZMnHpuq_QbjkUdcG61ooiwUYpo,10197
8
- dar_backup/util.py,sha256=ZYalRptXvSI9laLmHOe67WOOHoHF0CS4osE9NP1MDWA,8892
9
- dar_backup-0.6.0.dist-info/METADATA,sha256=-D3-rzNmkAMpN5SpMSeoveXuGQ1x8EncsdBxs0Lfx-s,22496
10
- dar_backup-0.6.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
11
- dar_backup-0.6.0.dist-info/entry_points.txt,sha256=x9vnW-JEl8mpDJC69f_XBcn0mBSkV1U0cyvFV-NAP1g,126
12
- dar_backup-0.6.0.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
13
- dar_backup-0.6.0.dist-info/RECORD,,