From 40953176f40373102583e6841515d9782b0c13ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Marczykowski-G=C3=B3recki?= Date: Mon, 13 Jan 2014 04:45:02 +0100 Subject: [PATCH] backups: reorganise restore API Call backup_restore_header from backup_restore_prepare, there is no sense in requiring the user to call them separately. Also store all parameters in restore_info object as special '$OPTIONS$' VM to not require passing them twice (with all the chances for the errors). --- core/backup.py | 163 ++++++++++++++++++++++------------- qvm-tools/qvm-backup-restore | 16 ++-- 2 files changed, 109 insertions(+), 70 deletions(-) diff --git a/core/backup.py b/core/backup.py index 2ebcdda1..03f74457 100644 --- a/core/backup.py +++ b/core/backup.py @@ -1020,8 +1020,74 @@ def backup_restore_header(source, passphrase, return (restore_tmpdir, os.path.join(restore_tmpdir, "qubes.xml")) -def backup_restore_prepare(backup_location, qubes_xml, passphrase, options = {}, - host_collection = None, encrypt=False, appvm=None, format_version=None): +def restore_info_verify(restore_info, host_collection): + options = restore_info['$OPTIONS$'] + for vm in restore_info.keys(): + if vm in ['$OPTIONS$', 'dom0']: + continue + + vm_info = restore_info[vm] + + vm_info.pop('excluded', None) + if 'exclude' in options.keys(): + if vm in options['exclude']: + vm_info['excluded'] = True + + vm_info.pop('already-exists', None) + if host_collection.get_vm_by_name (vm) is not None: + vm_info['already-exists'] = True + + # check template + vm_info.pop('missing-template', None) + if vm_info['template']: + template_name = vm_info['template'] + host_template = host_collection.get_vm_by_name(template_name) + if not host_template or not host_template.is_template(): + # Maybe the (custom) template is in the backup? + if not (template_name in restore_info.keys() and + restore_info[template_name]['vm'].is_template()): + if options['use-default-template']: + if 'orig-template' not in vm_info.keys(): + vm_info['orig-template'] = template_name + vm_info['template'] = host_collection\ + .get_default_template().name + else: + vm_info['missing-template'] = True + + # check netvm + vm_info.pop('missing-netvm', None) + if vm_info['netvm']: + netvm_name = vm_info['netvm'] + + netvm_on_host = host_collection.get_vm_by_name (netvm_name) + + # No netvm on the host? + if not ((netvm_on_host is not None) and netvm_on_host.is_netvm()): + + # Maybe the (custom) netvm is in the backup? + if not (netvm_name in restore_info.keys() and \ + restore_info[netvm_name]['vm'].is_netvm()): + if options['use-default-netvm']: + vm_info['netvm'] = host_collection\ + .get_default_netvm().name + vm_info['vm'].uses_default_netvm = True + elif options['use-none-netvm']: + vm_info['netvm'] = None + else: + vm_info['missing-netvm'] = True + + vm_info['good-to-go'] = not any([(prop in vm_info.keys()) for + prop in ['missing-netvm', + 'missing-template', + 'already-exists', + 'excluded']]) + + return restore_info + +def backup_restore_prepare(backup_location, passphrase, options = {}, + host_collection = None, encrypted=False, appvm=None, + compressed = False, print_callback = print_stdout, error_callback = print_stderr, + format_version=None): # Defaults backup_restore_set_defaults(options) @@ -1066,6 +1132,15 @@ def backup_restore_prepare(backup_location, qubes_xml, passphrase, options = {}, else: raise QubesException("Unknown backup format version: %s" % str(format_version)) + (restore_tmpdir, qubes_xml) = backup_restore_header(backup_location, + passphrase, + encrypted=encrypted, + appvm=appvm, + compressed=compressed, + print_callback=print_callback, + error_callback=error_callback, + format_version=format_version) + if BACKUP_DEBUG: print "Loading file", qubes_xml backup_collection = QubesVmCollection(store_filename = qubes_xml) @@ -1079,14 +1154,8 @@ def backup_restore_prepare(backup_location, qubes_xml, passphrase, options = {}, host_collection.unlock_db() backup_vms_list = [vm for vm in backup_collection.values()] - host_vms_list = [vm for vm in host_collection.values()] vms_to_restore = {} - there_are_conflicting_vms = False - there_are_missing_templates = False - there_are_missing_netvms = False - dom0_username_mismatch = False - restore_home = False # ... and the actual data for vm in backup_vms_list: if vm.qid == 0: @@ -1098,35 +1167,12 @@ def backup_restore_prepare(backup_location, qubes_xml, passphrase, options = {}, vms_to_restore[vm.name] = {} vms_to_restore[vm.name]['vm'] = vm; - if 'exclude' in options.keys(): - vms_to_restore[vm.name]['excluded'] = vm.name in options['exclude'] - if vms_to_restore[vm.name]['excluded']: - vms_to_restore[vm.name]['good-to-go'] = False - - if host_collection.get_vm_by_name (vm.name) is not None: - vms_to_restore[vm.name]['already-exists'] = True - vms_to_restore[vm.name]['good-to-go'] = False if vm.template is None: vms_to_restore[vm.name]['template'] = None else: templatevm_name = find_template_name(vm.template.name, options['replace-template']) vms_to_restore[vm.name]['template'] = templatevm_name - template_vm_on_host = host_collection.get_vm_by_name (templatevm_name) - - # No template on the host? - if not ((template_vm_on_host is not None) and template_vm_on_host.is_template()): - # Maybe the (custom) template is in the backup? - template_vm_on_backup = backup_collection.get_vm_by_name (templatevm_name) - if template_vm_on_backup is None or not \ - (is_vm_included_in_backup(backup_location, template_vm_on_backup) and \ - template_vm_on_backup.is_template()): - if options['use-default-template']: - vms_to_restore[vm.name]['orig-template'] = templatevm_name - vms_to_restore[vm.name]['template'] = host_collection.get_default_template().name - else: - vms_to_restore[vm.name]['missing-template'] = True - vms_to_restore[vm.name]['good-to-go'] = False if vm.netvm is None: vms_to_restore[vm.name]['netvm'] = None @@ -1139,27 +1185,17 @@ def backup_restore_prepare(backup_location, qubes_xml, passphrase, options = {}, # modifying firewall vm._netvm = None - netvm_on_host = host_collection.get_vm_by_name (netvm_name) + # Store restore parameters + options['location'] = backup_location + options['restore_tmpdir'] = restore_tmpdir + options['passphrase'] = passphrase + options['encrypted'] = encrypted + options['compressed'] = compressed + options['appvm'] = appvm + options['format_version'] = format_version + vms_to_restore['$OPTIONS$'] = options - # No netvm on the host? - if not ((netvm_on_host is not None) and netvm_on_host.is_netvm()): - - # Maybe the (custom) netvm is in the backup? - netvm_on_backup = backup_collection.get_vm_by_name (netvm_name) - if not ((netvm_on_backup is not None) and \ - netvm_on_backup.is_netvm() and \ - is_vm_included_in_backup(backup_location, netvm_on_backup)): - if options['use-default-netvm']: - vms_to_restore[vm.name]['netvm'] = host_collection.get_default_netvm().name - vm.uses_default_netvm = True - elif options['use-none-netvm']: - vms_to_restore[vm.name]['netvm'] = None - else: - vms_to_restore[vm.name]['missing-netvm'] = True - vms_to_restore[vm.name]['good-to-go'] = False - - if 'good-to-go' not in vms_to_restore[vm.name].keys(): - vms_to_restore[vm.name]['good-to-go'] = True + vms_to_restore = restore_info_verify(vms_to_restore, host_collection) # ...and dom0 home if options['dom0-home'] and \ @@ -1290,10 +1326,10 @@ def backup_restore_print_summary(restore_info, print_callback = print_stdout): print_callback(s) -def backup_restore_do(backup_location, restore_tmpdir, passphrase, restore_info, +def backup_restore_do(restore_info, host_collection = None, print_callback = print_stdout, error_callback = print_stderr, progress_callback = None, - encrypted=False, appvm=None, compressed = False, format_version = None): + ): ### Private functions begin def restore_vm_dir_v1 (backup_dir, src_dir, dst_dir): @@ -1308,6 +1344,15 @@ def backup_restore_do(backup_location, restore_tmpdir, passphrase, restore_info, dst_dir)) ### Private functions end + options = restore_info['$OPTIONS$'] + backup_location = options['location'] + restore_tmpdir = options['restore_tmpdir'] + passphrase = options['passphrase'] + encrypted = options['encrypted'] + compressed = options['compressed'] + appvm = options['appvm'] + format_version = options['format_version'] + if format_version is None: format_version = backup_detect_format_version(backup_location) @@ -1324,10 +1369,10 @@ def backup_restore_do(backup_location, restore_tmpdir, passphrase, restore_info, vms_size = 0 vms = {} for vm_info in restore_info.values(): - if not vm_info['good-to-go']: - continue if 'vm' not in vm_info: continue + if not vm_info['good-to-go']: + continue vm = vm_info['vm'] vms_size += vm.backup_size vms_dirs.append(vm.backup_path) @@ -1354,10 +1399,10 @@ def backup_restore_do(backup_location, restore_tmpdir, passphrase, restore_info, for (vm_class_name, vm_class) in sorted(QubesVmClasses.items(), key=lambda _x: _x[1].load_order): for vm_info in restore_info.values(): - if not vm_info['good-to-go']: - continue if 'vm' not in vm_info: continue + if not vm_info['good-to-go']: + continue vm = vm_info['vm'] if not vm.__class__ == vm_class: continue @@ -1413,10 +1458,10 @@ def backup_restore_do(backup_location, restore_tmpdir, passphrase, restore_info, # Set network dependencies - only non-default netvm setting for vm_info in restore_info.values(): - if not vm_info['good-to-go']: - continue if 'vm' not in vm_info: continue + if not vm_info['good-to-go']: + continue vm = vm_info['vm'] host_vm = host_collection.get_vm_by_name(vm.name) if host_vm is None: diff --git a/qvm-tools/qvm-backup-restore b/qvm-tools/qvm-backup-restore index 05da7812..2048b450 100755 --- a/qvm-tools/qvm-backup-restore +++ b/qvm-tools/qvm-backup-restore @@ -116,11 +116,11 @@ def main(): try: restore_info = backup_restore_prepare( backup_dir, - os.path.join(restore_tmpdir, qubes_xml), - passphrase, + passphrase=passphrase, options=restore_options, host_collection=host_collection, - encrypt=options.decrypt, + encrypted=options.decrypt, + compressed=options.compressed, appvm=appvm) except QubesException as e: print >> sys.stderr, "ERROR: %s" % str(e) @@ -210,14 +210,8 @@ def main(): exit (0) - backup_restore_do(backup_dir, - restore_tmpdir, - passphrase, - restore_info, - host_collection=host_collection, - encrypted=options.decrypt, - compressed=options.compressed, - appvm=appvm) + backup_restore_do(restore_info, + host_collection=host_collection) host_collection.unlock_db()