Skip to content

Commit 23c4767

Browse files
Distinguish different errors in case borg check failed. By @real-yfprojects (#1163)
1 parent 4b5b7e2 commit 23c4767

File tree

3 files changed

+87
-18
lines changed

3 files changed

+87
-18
lines changed

src/vorta/application.py

Lines changed: 68 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,26 @@
11
import logging
22
import os
33
import sys
4+
from typing import Any, Dict, List, Tuple
45

56
from PyQt5 import QtCore
67
from PyQt5.QtWidgets import QMessageBox
78

9+
from vorta.borg.break_lock import BorgBreakJob
810
from vorta.borg.create import BorgCreateJob
11+
from vorta.borg.jobs_manager import JobsManager
912
from vorta.borg.version import BorgVersionJob
10-
from vorta.borg.break_lock import BorgBreakJob
11-
from vorta.config import TEMP_DIR, PROFILE_BOOTSTRAP_FILE
13+
from vorta.config import PROFILE_BOOTSTRAP_FILE, TEMP_DIR
1214
from vorta.i18n import init_translations, translate
13-
from vorta.store.models import BackupProfileModel, SettingsModel
14-
from vorta.store.connection import cleanup_db
15+
from vorta.notifications import VortaNotifications
16+
from vorta.profile_export import ProfileExport
1517
from vorta.qt_single_application import QtSingleApplication
1618
from vorta.scheduler import VortaScheduler
17-
from vorta.borg.jobs_manager import JobsManager
19+
from vorta.store.connection import cleanup_db
20+
from vorta.store.models import BackupProfileModel, SettingsModel
1821
from vorta.tray_menu import TrayMenu
1922
from vorta.utils import borg_compat, parse_args
2023
from vorta.views.main_window import MainWindow
21-
from vorta.notifications import VortaNotifications
22-
from vorta.profile_export import ProfileExport
2324

2425
logger = logging.getLogger(__name__)
2526

@@ -39,7 +40,7 @@ class VortaApp(QtSingleApplication):
3940
backup_cancelled_event = QtCore.pyqtSignal()
4041
backup_log_event = QtCore.pyqtSignal(str, dict)
4142
backup_progress_event = QtCore.pyqtSignal(str)
42-
check_failed_event = QtCore.pyqtSignal(str)
43+
check_failed_event = QtCore.pyqtSignal(dict)
4344

4445
def __init__(self, args_raw, single_app=False):
4546
super().__init__(APP_ID, args_raw)
@@ -288,11 +289,62 @@ def bootstrap_profile(self, bootstrap_file=PROFILE_BOOTSTRAP_FILE):
288289
default_profile = BackupProfileModel(name='Default')
289290
default_profile.save()
290291

291-
def check_failed_response(self, repo_url):
292-
msg = QMessageBox()
293-
msg.setIcon(QMessageBox.Critical)
294-
msg.setStandardButtons(QMessageBox.Ok)
295-
msg.setWindowTitle(self.tr('Repo Check Failed'))
296-
msg.setText(self.tr('Repository data check for repo %s failed') % repo_url)
297-
msg.setInformativeText(self.tr('Repair or recreate the repository soon to avoid missing data.'))
298-
msg.exec()
292+
def check_failed_response(self, result: Dict[str, Any]):
293+
"""
294+
Process the signal that a repo consistency check failed.
295+
296+
Displays a `QMessageBox` with an error message depending on the
297+
return code of the `BorgJob`.
298+
299+
Parameters
300+
----------
301+
repo_url : str
302+
The url of the repo of concern
303+
"""
304+
# extract data from the params for the borg job
305+
repo_url = result['params']['repo_url']
306+
returncode = result['returncode']
307+
errors: List[Tuple[int, str]] = result['errors']
308+
error_message = errors[0][1] if errors else ''
309+
310+
# Switch over returncodes
311+
if returncode == 0:
312+
# No fail
313+
logger.warning(
314+
'VortaApp.check_failed_response was called with returncode 0')
315+
elif returncode == 130:
316+
# Keyboard interupt
317+
pass
318+
else: # Real error
319+
# Create QMessageBox
320+
msg = QMessageBox()
321+
msg.setIcon(QMessageBox.Icon.Critical) # changed for warning
322+
msg.setStandardButtons(QMessageBox.Ok)
323+
msg.setWindowTitle(self.tr('Repo Check Failed'))
324+
325+
if returncode == 1:
326+
# warning
327+
msg.setIcon(QMessageBox.Icon.Warning)
328+
text = self.tr('Borg exited with a warning message. See logs for details.')
329+
infotext = error_message
330+
elif returncode > 128:
331+
# 128+N - killed by signal N (e.g. 137 == kill -9)
332+
signal = returncode - 128
333+
text = (
334+
self.tr('Repository data check for repo was killed by signal %s.')
335+
% (signal)
336+
)
337+
infotext = self.tr('The process running the check job got a kill signal. Try again.')
338+
else:
339+
# Real error
340+
text = (
341+
self.tr('Repository data check for repo %s failed. Error code %s')
342+
% (repo_url, returncode)
343+
)
344+
infotext = error_message + '\n'
345+
infotext += self.tr('Consider repairing or recreating the repository soon to avoid missing data.')
346+
347+
msg.setText(text)
348+
msg.setInformativeText(infotext)
349+
# Display messagebox
350+
msg.exec()

src/vorta/borg/borg_job.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,7 @@ def run(self):
214214

215215
p = Popen(self.cmd, stdout=PIPE, stderr=PIPE, bufsize=1, universal_newlines=True,
216216
env=self.env, cwd=self.cwd, start_new_session=True)
217+
error_messages = [] # List of error messages included in the result
217218

218219
self.process = p
219220

@@ -250,6 +251,11 @@ def read_async(fd):
250251
f'{parsed["levelname"]}: {parsed["message"]}', context)
251252
level_int = getattr(logging, parsed["levelname"])
252253
logger.log(level_int, parsed["message"])
254+
255+
if level_int >= logging.WARNING:
256+
# Append log to list of error messages
257+
error_messages.append((level_int, parsed["message"]))
258+
253259
elif parsed['type'] == 'file_status':
254260
self.app.backup_log_event.emit(f'{parsed["path"]} ({parsed["status"]})', {})
255261
elif parsed['type'] == 'archive_progress':
@@ -275,6 +281,7 @@ def read_async(fd):
275281
'params': self.params,
276282
'returncode': self.process.returncode,
277283
'cmd': self.cmd,
284+
'errors': error_messages,
278285
}
279286
stdout = ''.join(stdout)
280287

src/vorta/borg/check.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from typing import Any, Dict
2+
13
from .borg_job import BorgJob
24

35

@@ -7,12 +9,20 @@ def started_event(self):
79
self.app.backup_started_event.emit()
810
self.app.backup_progress_event.emit(self.tr('Starting consistency check...'))
911

10-
def finished_event(self, result):
12+
def finished_event(self, result: Dict[str, Any]):
13+
"""
14+
Process that the job terminated with the given results.
15+
16+
Parameters
17+
----------
18+
result : Dict[str, Any]
19+
The (json-like) dictionary containing the job results.
20+
"""
1121
self.app.backup_finished_event.emit(result)
1222
self.result.emit(result)
1323
if result['returncode'] != 0:
1424
self.app.backup_progress_event.emit(self.tr('Repo check failed. See logs for details.'))
15-
self.app.check_failed_event.emit(self.params['repo_url'])
25+
self.app.check_failed_event.emit(result)
1626
else:
1727
self.app.backup_progress_event.emit(self.tr('Check completed.'))
1828

0 commit comments

Comments
 (0)