backup/restore: better error detection for --paranoid-mode

Xterm doesn't preserve exit code of the process running inside. This
means, the whole xterm always exits with 0, even if qvm-backup-restore
failed.
Fix this by printing the exit code at the end to the log and then extract
that last line from the log on the calling side. This way we can also
distinguish whether qvm-backup-restore or xterm failed.
This commit is contained in:
Marek Marczykowski-Górecki 2020-08-05 04:25:30 +02:00
orang tua 0c73e83627
melakukan 1660a1cbf6
Tidak diketahui kunci yang ditemukan di database signature
GPG Key ID: 063938BA42CFA724
2 mengubah file dengan 29 tambahan dan 8 penghapusan

Melihat File

@ -135,8 +135,8 @@ class RestoreInDisposableVM:
self.backup_log_path = '/var/tmp/backup-restore.log' self.backup_log_path = '/var/tmp/backup-restore.log'
self.terminal_app = ('xterm', '-hold', '-title', 'Backup restore', '-e', self.terminal_app = ('xterm', '-hold', '-title', 'Backup restore', '-e',
'/bin/sh', '-c', '/bin/sh', '-c',
'exec "$0" "$@" 2>&1 | tee {}'.format( '("$0" "$@" 2>&1; echo exit code: $?) | tee {}'.
self.backup_log_path)) format(self.backup_log_path))
if args.auto_close: if args.auto_close:
# filter-out '-hold' # filter-out '-hold'
self.terminal_app = tuple(a for a in self.terminal_app self.terminal_app = tuple(a for a in self.terminal_app
@ -285,17 +285,37 @@ class RestoreInDisposableVM:
self.args.pass_file) self.args.pass_file)
args = self.prepare_inner_args() args = self.prepare_inner_args()
self.dispvm.tags.add(self.dispvm_tag) self.dispvm.tags.add(self.dispvm_tag)
# TODO: better error detection
self.dispvm.run_with_args(*self.terminal_app, self.dispvm.run_with_args(*self.terminal_app,
'qvm-backup-restore', *args, 'qvm-backup-restore', *args,
stdout=subprocess.DEVNULL, stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL) stderr=subprocess.DEVNULL)
return self.extract_log() backup_log = self.extract_log()
last_line = backup_log.splitlines()[-1]
if not last_line.startswith(b'exit code:'):
raise qubesadmin.exc.BackupRestoreError(
'qvm-backup-restore did not reported exit code',
backup_log=backup_log)
try:
exit_code = int(last_line.split()[-1])
except ValueError:
raise qubesadmin.exc.BackupRestoreError(
'qvm-backup-restore reported unexpected exit code',
backup_log=backup_log)
if exit_code == 127:
raise qubesadmin.exc.QubesException(
'qvm-backup-restore tool '
'missing in {} template, install qubes-core-admin-client '
'package there'.format(self.dispvm.template.template.name)
)
if exit_code != 0:
raise qubesadmin.exc.BackupRestoreError(
'qvm-backup-restore failed with {}'.format(exit_code),
backup_log=backup_log)
return backup_log
except subprocess.CalledProcessError as e: except subprocess.CalledProcessError as e:
if e.returncode == 127: if e.returncode == 127:
raise qubesadmin.exc.QubesException( raise qubesadmin.exc.QubesException(
'qvm-backup-restore tool or {} ' '{} missing in {} template, install it there '
'missing in {} template, install qubes-core-admin-client '
'package there'.format(self.terminal_app[0], 'package there'.format(self.terminal_app[0],
self.dispvm.template.template.name) self.dispvm.template.template.name)
) )

Melihat File

@ -311,6 +311,7 @@ class TC_00_RestoreInDispVM(qubesadmin.tests.QubesTestCase):
'prepare_inner_args', 'extract_log', 'finalize_tags'] 'prepare_inner_args', 'extract_log', 'finalize_tags']
for m in methods: for m in methods:
setattr(obj, m, unittest.mock.Mock()) setattr(obj, m, unittest.mock.Mock())
obj.extract_log.return_value = b'Some logs\nexit code: 0\n'
obj.transfer_pass_file = unittest.mock.Mock() obj.transfer_pass_file = unittest.mock.Mock()
obj.prepare_inner_args.return_value = ['args'] obj.prepare_inner_args.return_value = ['args']
obj.terminal_app = ('terminal',) obj.terminal_app = ('terminal',)
@ -348,6 +349,7 @@ class TC_00_RestoreInDispVM(qubesadmin.tests.QubesTestCase):
'transfer_pass_file'] 'transfer_pass_file']
for m in methods: for m in methods:
setattr(obj, m, unittest.mock.Mock()) setattr(obj, m, unittest.mock.Mock())
obj.extract_log.return_value = b'Some logs\nexit code: 0\n'
obj.prepare_inner_args.return_value = ['args'] obj.prepare_inner_args.return_value = ['args']
obj.terminal_app = ('terminal',) obj.terminal_app = ('terminal',)
obj.dispvm = unittest.mock.Mock() obj.dispvm = unittest.mock.Mock()
@ -382,12 +384,11 @@ class TC_00_RestoreInDispVM(qubesadmin.tests.QubesTestCase):
'prepare_inner_args', 'extract_log', 'finalize_tags'] 'prepare_inner_args', 'extract_log', 'finalize_tags']
for m in methods: for m in methods:
setattr(obj, m, unittest.mock.Mock()) setattr(obj, m, unittest.mock.Mock())
obj.extract_log.return_value = b'Some error\nexit code: 1\n'
obj.transfer_pass_file = unittest.mock.Mock() obj.transfer_pass_file = unittest.mock.Mock()
obj.prepare_inner_args.return_value = ['args'] obj.prepare_inner_args.return_value = ['args']
obj.terminal_app = ('terminal',) obj.terminal_app = ('terminal',)
obj.dispvm = unittest.mock.Mock() obj.dispvm = unittest.mock.Mock()
obj.dispvm.run_with_args.side_effect = subprocess.CalledProcessError(
1, cmd='cmd')
with tempfile.NamedTemporaryFile() as tmp: with tempfile.NamedTemporaryFile() as tmp:
with unittest.mock.patch('qubesadmin.backup.dispvm.LOCKFILE', with unittest.mock.patch('qubesadmin.backup.dispvm.LOCKFILE',
tmp.name): tmp.name):