backup: add option to on-the-fly renaming conflicting VMs
Fixes QubesOS/qubes-issues#869
This commit is contained in:
parent
3065431058
commit
24d660d61e
@ -1572,6 +1572,8 @@ def backup_restore_set_defaults(options):
|
|||||||
options['ignore-username-mismatch'] = False
|
options['ignore-username-mismatch'] = False
|
||||||
if 'verify-only' not in options:
|
if 'verify-only' not in options:
|
||||||
options['verify-only'] = False
|
options['verify-only'] = False
|
||||||
|
if 'rename-conflicting' not in options:
|
||||||
|
options['rename-conflicting'] = False
|
||||||
|
|
||||||
return options
|
return options
|
||||||
|
|
||||||
@ -1639,6 +1641,22 @@ def backup_restore_header(source, passphrase,
|
|||||||
return (restore_tmpdir, os.path.join(restore_tmpdir, "qubes.xml"),
|
return (restore_tmpdir, os.path.join(restore_tmpdir, "qubes.xml"),
|
||||||
header_data)
|
header_data)
|
||||||
|
|
||||||
|
def generate_new_name_for_conflicting_vm(orig_name, host_collection,
|
||||||
|
restore_info):
|
||||||
|
number = 1
|
||||||
|
if len(orig_name) > 29:
|
||||||
|
orig_name = orig_name[0:29]
|
||||||
|
new_name = orig_name
|
||||||
|
while (new_name in restore_info.keys() or
|
||||||
|
new_name in map(lambda x: x.get('rename_to', None),
|
||||||
|
restore_info.values()) or
|
||||||
|
host_collection.get_vm_by_name(new_name)):
|
||||||
|
new_name = str('{}{}'.format(orig_name, number))
|
||||||
|
number += 1
|
||||||
|
if number == 100:
|
||||||
|
# give up
|
||||||
|
return None
|
||||||
|
return new_name
|
||||||
|
|
||||||
def restore_info_verify(restore_info, host_collection):
|
def restore_info_verify(restore_info, host_collection):
|
||||||
options = restore_info['$OPTIONS$']
|
options = restore_info['$OPTIONS$']
|
||||||
@ -1656,7 +1674,16 @@ def restore_info_verify(restore_info, host_collection):
|
|||||||
vm_info.pop('already-exists', None)
|
vm_info.pop('already-exists', None)
|
||||||
if not options['verify-only'] and \
|
if not options['verify-only'] and \
|
||||||
host_collection.get_vm_by_name(vm) is not None:
|
host_collection.get_vm_by_name(vm) is not None:
|
||||||
vm_info['already-exists'] = True
|
if options['rename-conflicting']:
|
||||||
|
new_name = generate_new_name_for_conflicting_vm(
|
||||||
|
vm, host_collection, restore_info
|
||||||
|
)
|
||||||
|
if new_name is not None:
|
||||||
|
vm_info['rename-to'] = new_name
|
||||||
|
else:
|
||||||
|
vm_info['already-exists'] = True
|
||||||
|
else:
|
||||||
|
vm_info['already-exists'] = True
|
||||||
|
|
||||||
# check template
|
# check template
|
||||||
vm_info.pop('missing-template', None)
|
vm_info.pop('missing-template', None)
|
||||||
@ -1703,6 +1730,22 @@ def restore_info_verify(restore_info, host_collection):
|
|||||||
'already-exists',
|
'already-exists',
|
||||||
'excluded']])
|
'excluded']])
|
||||||
|
|
||||||
|
# update references to renamed VMs:
|
||||||
|
for vm in restore_info.keys():
|
||||||
|
if vm in ['$OPTIONS$', 'dom0']:
|
||||||
|
continue
|
||||||
|
vm_info = restore_info[vm]
|
||||||
|
template_name = vm_info['template']
|
||||||
|
if (template_name in restore_info and
|
||||||
|
restore_info[template_name]['good-to-go'] and
|
||||||
|
'rename-to' in restore_info[template_name]):
|
||||||
|
vm_info['template'] = restore_info[template_name]['rename-to']
|
||||||
|
netvm_name = vm_info['netvm']
|
||||||
|
if (netvm_name in restore_info and
|
||||||
|
restore_info[netvm_name]['good-to-go'] and
|
||||||
|
'rename-to' in restore_info[netvm_name]):
|
||||||
|
vm_info['netvm'] = restore_info[netvm_name]['rename-to']
|
||||||
|
|
||||||
return restore_info
|
return restore_info
|
||||||
|
|
||||||
|
|
||||||
@ -1975,8 +2018,11 @@ def backup_restore_print_summary(restore_info, print_callback=print_stdout):
|
|||||||
s += " <-- No matching template on the host or in the backup found!"
|
s += " <-- No matching template on the host or in the backup found!"
|
||||||
elif 'missing-netvm' in vm_info:
|
elif 'missing-netvm' in vm_info:
|
||||||
s += " <-- No matching netvm on the host or in the backup found!"
|
s += " <-- No matching netvm on the host or in the backup found!"
|
||||||
elif 'orig-template' in vm_info:
|
else:
|
||||||
s += " <-- Original template was '%s'" % (vm_info['orig-template'])
|
if 'orig-template' in vm_info:
|
||||||
|
s += " <-- Original template was '%s'" % (vm_info['orig-template'])
|
||||||
|
if 'rename-to' in vm_info:
|
||||||
|
s += " <-- Will be renamed to '%s'" % vm_info['rename-to']
|
||||||
|
|
||||||
print_callback(s)
|
print_callback(s)
|
||||||
|
|
||||||
@ -2124,33 +2170,35 @@ def backup_restore_do(restore_info,
|
|||||||
error_callback("Skipping...")
|
error_callback("Skipping...")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if os.path.exists(vm.dir_path):
|
|
||||||
move_to_path = tempfile.mkdtemp('', os.path.basename(
|
|
||||||
vm.dir_path), os.path.dirname(vm.dir_path))
|
|
||||||
try:
|
|
||||||
os.rename(vm.dir_path, move_to_path)
|
|
||||||
error_callback("*** Directory {} already exists! It has "
|
|
||||||
"been moved to {}".format(vm.dir_path,
|
|
||||||
move_to_path))
|
|
||||||
except OSError:
|
|
||||||
error_callback("*** Directory {} already exists and "
|
|
||||||
"cannot be moved!".format(vm.dir_path))
|
|
||||||
error_callback("Skipping...")
|
|
||||||
continue
|
|
||||||
|
|
||||||
template = None
|
template = None
|
||||||
if vm.template is not None:
|
if vm.template is not None:
|
||||||
template_name = restore_info[vm.name]['template']
|
template_name = restore_info[vm.name]['template']
|
||||||
template = host_collection.get_vm_by_name(template_name)
|
template = host_collection.get_vm_by_name(template_name)
|
||||||
|
|
||||||
new_vm = None
|
new_vm = None
|
||||||
|
vm_name = vm.name
|
||||||
|
if 'rename-to' in restore_info[vm.name]:
|
||||||
|
vm_name = restore_info[vm.name]['rename-to']
|
||||||
|
|
||||||
try:
|
try:
|
||||||
new_vm = host_collection.add_new_vm(vm_class_name, name=vm.name,
|
new_vm = host_collection.add_new_vm(vm_class_name, name=vm_name,
|
||||||
conf_file=vm.conf_file,
|
|
||||||
dir_path=vm.dir_path,
|
|
||||||
template=template,
|
template=template,
|
||||||
installed_by_rpm=False)
|
installed_by_rpm=False)
|
||||||
|
if os.path.exists(new_vm.dir_path):
|
||||||
|
move_to_path = tempfile.mkdtemp('', os.path.basename(
|
||||||
|
new_vm.dir_path), os.path.dirname(new_vm.dir_path))
|
||||||
|
try:
|
||||||
|
os.rename(new_vm.dir_path, move_to_path)
|
||||||
|
error_callback(
|
||||||
|
"*** Directory {} already exists! It has "
|
||||||
|
"been moved to {}".format(new_vm.dir_path,
|
||||||
|
move_to_path))
|
||||||
|
except OSError:
|
||||||
|
error_callback(
|
||||||
|
"*** Directory {} already exists and "
|
||||||
|
"cannot be moved!".format(new_vm.dir_path))
|
||||||
|
error_callback("Skipping...")
|
||||||
|
continue
|
||||||
|
|
||||||
if format_version == 1:
|
if format_version == 1:
|
||||||
restore_vm_dir_v1(backup_location,
|
restore_vm_dir_v1(backup_location,
|
||||||
@ -2194,7 +2242,11 @@ def backup_restore_do(restore_info,
|
|||||||
|
|
||||||
# Set network dependencies - only non-default netvm setting
|
# Set network dependencies - only non-default netvm setting
|
||||||
for vm in vms.values():
|
for vm in vms.values():
|
||||||
host_vm = host_collection.get_vm_by_name(vm.name)
|
vm_name = vm.name
|
||||||
|
if 'rename-to' in restore_info[vm.name]:
|
||||||
|
vm_name = restore_info[vm.name]['rename-to']
|
||||||
|
host_vm = host_collection.get_vm_by_name(vm_name)
|
||||||
|
|
||||||
if host_vm is None:
|
if host_vm is None:
|
||||||
# Failed/skipped VM
|
# Failed/skipped VM
|
||||||
continue
|
continue
|
||||||
|
Loading…
Reference in New Issue
Block a user