def test_040_register_backup_source(self):
        self.app.expected_calls[('dom0', 'admin.vm.List', None, None)] = (
            b'0\0dom0 class=AdminVM state=Running\n'
            b'fedora-25 class=TemplateVM state=Halted\n'
            b'backup-storage class=AppVM state=Running\n'
        )
        self.app.expected_service_calls[
            ('backup-storage', 'qubes.RegisterBackupLocation')] = \
            b'someid\nsomething that should not be read'
        self.app.expected_calls[
            ('backup-storage', 'admin.vm.tag.Set', 'backup-restore-storage',
             None)] = b'0\0'

        args = unittest.mock.Mock(backup_location='/backup/path',
                                  appvm='backup-storage')
        obj = RestoreInDisposableVM(self.app, args)
        obj.dispvm = unittest.mock.Mock(default_user='******')
        obj.register_backup_source()
        self.assertEqual(obj.storage_access_id, 'someid')
        self.assertEqual(self.app.service_calls, [
            ('backup-storage', 'qubes.RegisterBackupLocation',
             {'stdin':subprocess.PIPE, 'stdout':subprocess.PIPE}),
            ('backup-storage', 'qubes.RegisterBackupLocation', b'/backup/path\n'),
        ])
        self.assertAllCalled()
 def test_020_create_dispvm(self, mock_check_call):
     self.app.expected_calls[('dom0', 'admin.vm.List', None, None)] = (
         b'0\0dom0 class=AdminVM state=Running\n'
         b'fedora-25 class=TemplateVM state=Halted\n'
         b'testvm class=AppVM state=Running\n'
         b'mgmt-dvm class=AppVM state=Halted\n'
         # this should be only after creating...
         b'disp-backup-restore class=DispVM state=Halted\n'
     )
     self.app.expected_calls[
         ('dom0', 'admin.property.Get', 'management_dispvm', None)] =  \
         b'0\0default=False type=vm mgmt-dvm'
     self.app.expected_calls[
         ('dom0', 'admin.vm.Create.DispVM', 'mgmt-dvm',
          b'name=disp-backup-restore label=red')] = b'0\0'
     self.app.expected_calls[
         ('disp-backup-restore', 'admin.vm.property.Set', 'auto_cleanup',
          b'True')] =  \
         b'0\0'
     self.app.expected_calls[
         ('disp-backup-restore', 'admin.vm.feature.Set', 'tag-created-vm-with',
          b'backup-restore-in-progress')] =  \
         b'0\0'
     args = unittest.mock.Mock(appvm='dom0')
     obj = RestoreInDisposableVM(self.app, args)
     obj.create_dispvm()
     self.assertAllCalled()
 def test_070_sanitize_log(self):
     sanitized = RestoreInDisposableVM.sanitize_log(b'sample message')
     self.assertEqual(sanitized, b'sample message')
     sanitized = RestoreInDisposableVM.sanitize_log(
         b'sample message\nmultiline\n')
     self.assertEqual(sanitized, b'sample message\nmultiline\n')
     sanitized = RestoreInDisposableVM.sanitize_log(
         b'\033[0;33m\xff\xfe\x80')
     self.assertEqual(sanitized, b'.[0;33m...')
 def test_003_prepare_inner_args_auto_close(self):
     self.app.expected_calls[('dom0', 'admin.vm.List', None, None)] = (
         b'0\0dom0 class=AdminVM state=Running\n'
         b'fedora-25 class=TemplateVM state=Halted\n'
         b'testvm class=AppVM state=Running\n'
     )
     argv = ['--auto-close', '/backup/location']
     args = qvm_backup_restore.parser.parse_args(argv)
     print(repr(args))
     obj = RestoreInDisposableVM(self.app, args)
     obj.storage_access_id = 'abc'
     reconstructed_argv = obj.prepare_inner_args()
     expected_argv = ['--location-is-service', 'qubes.RestoreById+abc']
     self.assertEqual(expected_argv, reconstructed_argv)
    def test_060_finalize_tags(self, mock_date):
        self.app.expected_calls[('dom0', 'admin.vm.List', None, None)] = (
            b'0\0dom0 class=AdminVM state=Running\n'
            b'fedora-25 class=TemplateVM state=Halted\n'
            b'disp-backup-restore class=DispVM state=Running\n'
            b'restored1 class=AppVM state=Halted\n'
            b'restored2 class=AppVM state=Halted\n'
        )
        self.app.expected_calls[
            ('dom0', 'admin.vm.tag.Get', 'backup-restore-in-progress',
             None)] = b'0\x000'
        self.app.expected_calls[
            ('fedora-25', 'admin.vm.tag.Get', 'backup-restore-in-progress',
             None)] = b'0\x000'
        self.app.expected_calls[
            ('disp-backup-restore', 'admin.vm.tag.Get', 'backup-restore-in-progress',
             None)] = b'0\x000'
        self.app.expected_calls[
            ('restored1', 'admin.vm.tag.Get', 'backup-restore-in-progress',
             None)] = b'0\x001'
        self.app.expected_calls[
            ('restored1', 'admin.vm.tag.List', None, None)] = \
            b'0\0backup-restore-in-progress\n' \
            b'restored-from-backup-12345678\n' \
            b'created-by-disp-backup-restore\n'
        self.app.expected_calls[
            ('restored1', 'admin.vm.tag.Remove', 'backup-restore-in-progress',
             None)] = b'0\0'
        self.app.expected_calls[
            ('restored2', 'admin.vm.tag.Get', 'backup-restore-in-progress',
             None)] = b'0\x001'
        self.app.expected_calls[
            ('restored2', 'admin.vm.tag.List', None, None)] = \
            b'0\0backup-restore-in-progress\n' \
            b'created-by-disp-backup-restore\n'
        self.app.expected_calls[
            ('restored2', 'admin.vm.tag.Set',
             'restored-from-backup-at-2019-10-01',
             None)] = b'0\0'
        self.app.expected_calls[
            ('restored2', 'admin.vm.tag.Remove', 'backup-restore-in-progress',
             None)] = b'0\0'

        mock_date.today.return_value = datetime.date.fromisoformat('2019-10-01')
        mock_date.strftime.return_value = '2019-10-01'
        args = unittest.mock.Mock(backup_location='/backup/path',
                                  appvm=None)
        obj = RestoreInDisposableVM(self.app, args)
        obj.finalize_tags()
        self.assertAllCalled()
 def test_000_prepare_inner_args(self):
     self.app.expected_calls[('dom0', 'admin.vm.List', None, None)] = (
         b'0\0dom0 class=AdminVM state=Running\n'
         b'fedora-25 class=TemplateVM state=Halted\n'
         b'testvm class=AppVM state=Running\n'
     )
     argv = ['--verbose', '--skip-broken', '--skip-dom0-home',
             '--dest-vm', 'testvm',
             '--compression-filter', 'gzip', '/backup/location']
     args = qvm_backup_restore.parser.parse_args(argv)
     obj = RestoreInDisposableVM(self.app, args)
     obj.storage_access_id = 'abc'
     reconstructed_argv = obj.prepare_inner_args()
     expected_argv = argv[:-1] + \
                     ['--location-is-service', 'qubes.RestoreById+abc']
     self.assertCountEqual(expected_argv, reconstructed_argv)
 def test_080_extract_log(self):
     self.app.expected_calls[('dom0', 'admin.vm.List', None, None)] = (
         b'0\0dom0 class=AdminVM state=Running\n'
         b'fedora-25 class=TemplateVM state=Halted\n'
     )
     args = unittest.mock.Mock(backup_location='/backup/path',
                               appvm=None)
     obj = RestoreInDisposableVM(self.app, args)
     obj.dispvm = unittest.mock.Mock()
     obj.dispvm.run_with_args.return_value = b'this is a log', None
     backup_log = obj.extract_log()
     obj.dispvm.run_with_args.assert_called_once_with(
         'cat', '/var/tmp/backup-restore.log',
         stdout=subprocess.PIPE,
         stderr=subprocess.DEVNULL)
     self.assertEqual(backup_log, b'this is a log')
 def restore_backup(self,
                    source=None,
                    appvm=None,
                    options=None,
                    expect_errors=None,
                    manipulate_restore_info=None,
                    passphrase='qubes'):
     args = unittest.mock.Mock(
         spec=['app', 'appvm', 'backup_location', 'vms'])
     args.app = qubesadmin.Qubes()
     args.appvm = appvm
     args.backup_location = source
     # XXX FIXME
     args.app.blind_mode = True
     args.vms = []
     args.auto_close = True
     with tempfile.NamedTemporaryFile() as pass_file:
         pass_file.file.write(passphrase.encode())
         pass_file.file.flush()
         args.pass_file = pass_file.name
         restore_in_dispvm = RestoreInDisposableVM(args.app, args)
         try:
             backup_log = self.loop.run_until_complete(
                 self.loop.run_in_executor(None, restore_in_dispvm.run))
         except qubesadmin.exc.BackupRestoreError as e:
             self.fail(str(e) + ' backup log: ' + e.backup_log.decode())
         self.app.log.debug(backup_log.decode())
 def test_030_transfer_pass_file(self, mock_uname, mock_check_call):
     self.app.expected_calls[('dom0', 'admin.vm.List', None, None)] = (
         b'0\0dom0 class=AdminVM state=Running\n'
         b'fedora-25 class=TemplateVM state=Halted\n'
         b'testvm class=AppVM state=Running\n'
     )
     mock_uname.return_value = ('Linux', 'dom0', '5.0.0', '#1', 'x86_64')
     args = unittest.mock.Mock(appvm='testvm')
     obj = RestoreInDisposableVM(self.app, args)
     obj.dispvm = unittest.mock.Mock(default_user='******')
     new_path = obj.transfer_pass_file('/some/path')
     self.assertEqual(new_path, '/home/user2/QubesIncoming/dom0/path')
     mock_check_call.assert_called_once_with(
         ['qvm-copy-to-vm', 'disp-backup-restore', '/some/path'],
         stdout=subprocess.DEVNULL,
         stderr=subprocess.DEVNULL)
     self.assertAllCalled()
Example #10
0
    def test_050_invalidate_backup_access(self):
        self.app.expected_calls[('dom0', 'admin.vm.List', None, None)] = (
            b'0\0dom0 class=AdminVM state=Running\n'
            b'fedora-25 class=TemplateVM state=Halted\n'
            b'backup-storage class=AppVM state=Running\n'
        )
        self.app.expected_calls[
            ('backup-storage', 'admin.vm.tag.Remove', 'backup-restore-storage',
             None)] = b'0\0'

        args = unittest.mock.Mock(backup_location='/backup/path',
                                  appvm='backup-storage')
        obj = RestoreInDisposableVM(self.app, args)
        obj.storage_access_proc = unittest.mock.Mock()
        obj.invalidate_backup_access()
        self.assertEqual(obj.storage_access_proc.mock_calls, [
            call.stdin.close(),
            call.wait(),
        ])
        self.assertAllCalled()
Example #11
0
    def test_010_clear_old_tags(self):
        self.app.expected_calls[('dom0', 'admin.vm.List', None, None)] = (
            b'0\0dom0 class=AdminVM state=Running\n'
            b'fedora-25 class=TemplateVM state=Halted\n'
            b'testvm class=AppVM state=Running\n'
        )
        for tag in ('backup-restore-mgmt',
                    'backup-restore-in-progress',
                    'backup-restore-storage'):
            self.app.expected_calls[
                ('dom0', 'admin.vm.tag.Remove', tag, None)] = \
                b'2\x00QubesTagNotFoundError\x00\x00Tag not found\x00'
            self.app.expected_calls[
                ('fedora-25', 'admin.vm.tag.Remove', tag, None)] = b'0\0'
            self.app.expected_calls[
                ('testvm', 'admin.vm.tag.Remove', tag, None)] = b'0\0'

        args = unittest.mock.Mock(appvm='testvm')
        obj = RestoreInDisposableVM(self.app, args)
        obj.clear_old_tags()
        self.assertAllCalled()
Example #12
0
    def test_101_run_pass_file(self):
        self.app.expected_calls[('dom0', 'admin.vm.List', None, None)] = (
            b'0\0dom0 class=AdminVM state=Running\n'
            b'fedora-25 class=TemplateVM state=Halted\n'
        )
        args = unittest.mock.Mock(backup_location='/backup/path',
            pass_file='/some/path',
            appvm=None)
        obj = RestoreInDisposableVM(self.app, args)
        methods = ['create_dispvm', 'clear_old_tags', 'register_backup_source',
                   'prepare_inner_args', 'extract_log', 'finalize_tags',
                   'transfer_pass_file']
        for m in methods:
            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.terminal_app = ('terminal',)
        obj.dispvm = unittest.mock.Mock()
        with tempfile.NamedTemporaryFile() as tmp:
            with unittest.mock.patch('qubesadmin.backup.dispvm.LOCKFILE',
                    tmp.name):
                obj.run()

        for m in methods:
            self.assertEqual(len(getattr(obj, m).mock_calls), 1)
        self.assertEqual(obj.dispvm.mock_calls, [
            call.start(),
            call.run_service_for_stdio('qubes.WaitForSession'),
            call.tags.add('backup-restore-mgmt'),
            call.run_with_args('terminal', 'qvm-backup-restore', 'args',
                stdout=subprocess.DEVNULL,
                stderr=subprocess.DEVNULL),
            call.tags.discard('backup-restore-mgmt'),
            call.kill()
        ])
Example #13
0
def main(args=None, app=None):
    '''Main function of qvm-backup-restore'''
    # pylint: disable=too-many-return-statements
    args = parser.parse_args(args, app=app)

    appvm = None
    if args.appvm:
        try:
            appvm = args.app.domains[args.appvm]
        except KeyError:
            parser.error('no such domain: {!r}'.format(args.appvm))

    if args.location_is_service and not args.appvm:
        parser.error('--location-is-service option requires -d')

    if args.paranoid_mode:
        args.dom0_home = False
        args.app.log.info("Starting restore process in a DisposableVM...")
        args.app.log.info("When operation completes, close its window "
                          "manually.")
        restore_in_dispvm = RestoreInDisposableVM(args.app, args)
        try:
            backup_log = restore_in_dispvm.run()
            if args.auto_close:
                print_backup_log(backup_log)
        except qubesadmin.exc.BackupRestoreError as e:
            if e.backup_log is not None:
                print_backup_log(e.backup_log)
            parser.error_runtime(str(e))
            return 1
        except qubesadmin.exc.QubesException as e:
            parser.error_runtime(str(e))
            return 1
        return

    if args.pass_file is not None:
        pass_f = open(args.pass_file) if args.pass_file != "-" else sys.stdin
        passphrase = pass_f.readline().rstrip()
        if pass_f is not sys.stdin:
            pass_f.close()
    else:
        passphrase = getpass.getpass("Please enter the passphrase to verify "
                                     "and (if encrypted) decrypt the backup: ")

    args.app.log.info("Checking backup content...")

    try:
        backup = BackupRestore(args.app,
                               args.backup_location,
                               appvm,
                               passphrase,
                               location_is_service=args.location_is_service,
                               force_compression_filter=args.compression)
    except qubesadmin.exc.QubesException as e:
        parser.error_runtime(str(e))
        # unreachable - error_runtime will raise SystemExit
        return 1

    backup.options.use_default_template = args.ignore_missing
    backup.options.use_default_netvm = args.ignore_missing
    backup.options.rename_conflicting = args.rename_conflicting
    backup.options.dom0_home = args.dom0_home
    backup.options.ignore_username_mismatch = args.ignore_username_mismatch
    backup.options.ignore_size_limit = args.ignore_size_limit
    backup.options.exclude = args.exclude
    backup.options.verify_only = args.verify_only

    restore_info = None
    try:
        restore_info = backup.get_restore_info()
    except qubesadmin.exc.QubesException as e:
        parser.error_runtime(str(e))

    if args.vms:
        # use original name here, not renamed
        backup.options.exclude += [
            vm_info.vm.name for vm_info in restore_info.values()
            if vm_info.vm.name not in args.vms
        ]
        restore_info = backup.restore_info_verify(restore_info)

    print(backup.get_restore_summary(restore_info))

    try:
        handle_broken(args.app, args, restore_info)
    except qubesadmin.exc.QubesException as e:
        parser.error_runtime(str(e))

    if args.pass_file is None:
        if input("Do you want to proceed? [y/N] ").upper() != "Y":
            sys.exit(0)

    try:
        backup.restore_do(restore_info)
    except qubesadmin.exc.QubesException as e:
        parser.error_runtime(str(e))