Merge branch 'new-backups'
Conflicts: core-modules/000QubesVm.py
This commit is contained in:
commit
27f6f0e64e
@ -123,6 +123,9 @@ class QubesVm(object):
|
|||||||
"default_user": { "default": "user" },
|
"default_user": { "default": "user" },
|
||||||
"qrexec_timeout": { "default": 60 },
|
"qrexec_timeout": { "default": 60 },
|
||||||
"autostart": { "default": False, "attr": "_autostart" },
|
"autostart": { "default": False, "attr": "_autostart" },
|
||||||
|
"backup_content" : { 'default': False },
|
||||||
|
"backup_size" : { 'default': 0, "eval": "int(value)" },
|
||||||
|
"backup_path" : { 'default': "" },
|
||||||
##### Internal attributes - will be overriden in __init__ regardless of args
|
##### Internal attributes - will be overriden in __init__ regardless of args
|
||||||
"config_file_template": { "eval": 'system_path["config_template_pv"]' },
|
"config_file_template": { "eval": 'system_path["config_template_pv"]' },
|
||||||
"icon_path": { "eval": 'os.path.join(self.dir_path, "icon.png") if self.dir_path is not None else None' },
|
"icon_path": { "eval": 'os.path.join(self.dir_path, "icon.png") if self.dir_path is not None else None' },
|
||||||
@ -140,7 +143,9 @@ class QubesVm(object):
|
|||||||
'uses_default_kernel', 'kernel', 'uses_default_kernelopts',\
|
'uses_default_kernel', 'kernel', 'uses_default_kernelopts',\
|
||||||
'kernelopts', 'services', 'installed_by_rpm',\
|
'kernelopts', 'services', 'installed_by_rpm',\
|
||||||
'uses_default_netvm', 'include_in_backups', 'debug',\
|
'uses_default_netvm', 'include_in_backups', 'debug',\
|
||||||
'default_user', 'qrexec_timeout', 'autostart' ]:
|
'default_user', 'qrexec_timeout', 'autostart',
|
||||||
|
'default_user', 'qrexec_timeout',
|
||||||
|
'backup_content', 'backup_size', 'backup_path' ]:
|
||||||
attrs[prop]['save'] = 'str(self.%s)' % prop
|
attrs[prop]['save'] = 'str(self.%s)' % prop
|
||||||
# Simple paths
|
# Simple paths
|
||||||
for prop in ['conf_file', 'root_img', 'volatile_img', 'private_img']:
|
for prop in ['conf_file', 'root_img', 'volatile_img', 'private_img']:
|
||||||
|
@ -25,9 +25,13 @@ from qubes.qubes import QubesNetVm,register_qubes_vm_class,xl_ctx,xc
|
|||||||
from qubes.qubes import defaults
|
from qubes.qubes import defaults
|
||||||
from qubes.qubes import QubesException,dry_run
|
from qubes.qubes import QubesException,dry_run
|
||||||
|
|
||||||
class QubesDom0NetVm(QubesNetVm):
|
class QubesAdminVm(QubesNetVm):
|
||||||
|
|
||||||
|
# In which order load this VM type from qubes.xml
|
||||||
|
load_order = 10
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
super(QubesDom0NetVm, self).__init__(qid=0, name="dom0", netid=0,
|
super(QubesAdminVm, self).__init__(qid=0, name="dom0", netid=0,
|
||||||
dir_path=None,
|
dir_path=None,
|
||||||
private_img = None,
|
private_img = None,
|
||||||
template = None,
|
template = None,
|
||||||
@ -35,6 +39,10 @@ class QubesDom0NetVm(QubesNetVm):
|
|||||||
**kwargs)
|
**kwargs)
|
||||||
self.xid = 0
|
self.xid = 0
|
||||||
|
|
||||||
|
@property
|
||||||
|
def type(self):
|
||||||
|
return "AdminVM"
|
||||||
|
|
||||||
def is_running(self):
|
def is_running(self):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@ -83,10 +91,7 @@ class QubesDom0NetVm(QubesNetVm):
|
|||||||
domains = xc.domain_getinfo(0, 1)
|
domains = xc.domain_getinfo(0, 1)
|
||||||
return domains[0]
|
return domains[0]
|
||||||
|
|
||||||
def create_xml_element(self):
|
|
||||||
return None
|
|
||||||
|
|
||||||
def verify_files(self):
|
def verify_files(self):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
register_qubes_vm_class(QubesDom0NetVm)
|
register_qubes_vm_class(QubesAdminVm)
|
1369
core/backup.py
Normal file
1369
core/backup.py
Normal file
File diff suppressed because it is too large
Load Diff
@ -609,10 +609,6 @@ class QubesVmCollection(dict):
|
|||||||
def load(self):
|
def load(self):
|
||||||
self.clear()
|
self.clear()
|
||||||
|
|
||||||
dom0vm = QubesDom0NetVm (collection=self)
|
|
||||||
self[dom0vm.qid] = dom0vm
|
|
||||||
self.default_netvm_qid = 0
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
tree = lxml.etree.parse(self.qubes_store_file)
|
tree = lxml.etree.parse(self.qubes_store_file)
|
||||||
except (EnvironmentError,
|
except (EnvironmentError,
|
||||||
@ -665,6 +661,12 @@ class QubesVmCollection(dict):
|
|||||||
# using 123/udp port)
|
# using 123/udp port)
|
||||||
if self.clockvm_qid is not None:
|
if self.clockvm_qid is not None:
|
||||||
self[self.clockvm_qid].services['ntpd'] = False
|
self[self.clockvm_qid].services['ntpd'] = False
|
||||||
|
|
||||||
|
# Add dom0 if wasn't present in qubes.xml
|
||||||
|
if not 0 in self.keys():
|
||||||
|
dom0vm = QubesAdminVm (collection=self)
|
||||||
|
self[dom0vm.qid] = dom0vm
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def pop(self, qid):
|
def pop(self, qid):
|
||||||
|
@ -28,6 +28,7 @@ import sys
|
|||||||
import os
|
import os
|
||||||
import subprocess
|
import subprocess
|
||||||
import re
|
import re
|
||||||
|
import shutil
|
||||||
import time
|
import time
|
||||||
import grp,pwd
|
import grp,pwd
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
@ -754,622 +755,4 @@ class QubesWatch(object):
|
|||||||
while True:
|
while True:
|
||||||
self.watch_single()
|
self.watch_single()
|
||||||
|
|
||||||
######## Backups #########
|
|
||||||
|
|
||||||
def get_disk_usage(file_or_dir):
|
|
||||||
if not os.path.exists(file_or_dir):
|
|
||||||
return 0
|
|
||||||
|
|
||||||
p = subprocess.Popen (["du", "-s", "--block-size=1", file_or_dir],
|
|
||||||
stdout=subprocess.PIPE)
|
|
||||||
result = p.communicate()
|
|
||||||
m = re.match(r"^(\d+)\s.*", result[0])
|
|
||||||
sz = int(m.group(1)) if m is not None else 0
|
|
||||||
return sz
|
|
||||||
|
|
||||||
|
|
||||||
def file_to_backup (file_path, sz = None):
|
|
||||||
if sz is None:
|
|
||||||
sz = os.path.getsize (system_path["qubes_store_filename"])
|
|
||||||
|
|
||||||
abs_file_path = os.path.abspath (file_path)
|
|
||||||
abs_base_dir = os.path.abspath (system_path["qubes_base_dir"]) + '/'
|
|
||||||
abs_file_dir = os.path.dirname (abs_file_path) + '/'
|
|
||||||
(nothing, dir, subdir) = abs_file_dir.partition (abs_base_dir)
|
|
||||||
assert nothing == ""
|
|
||||||
assert dir == abs_base_dir
|
|
||||||
return [ { "path" : file_path, "size": sz, "subdir": subdir} ]
|
|
||||||
|
|
||||||
def backup_prepare(base_backup_dir, vms_list = None, exclude_list = [], print_callback = print_stdout):
|
|
||||||
"""If vms = None, include all (sensible) VMs; exclude_list is always applied"""
|
|
||||||
|
|
||||||
if not os.path.exists (base_backup_dir):
|
|
||||||
raise QubesException("The target directory doesn't exist!")
|
|
||||||
|
|
||||||
files_to_backup = file_to_backup (system_path["qubes_store_filename"])
|
|
||||||
|
|
||||||
if exclude_list is None:
|
|
||||||
exclude_list = []
|
|
||||||
|
|
||||||
if vms_list is None:
|
|
||||||
qvm_collection = QubesVmCollection()
|
|
||||||
qvm_collection.lock_db_for_reading()
|
|
||||||
qvm_collection.load()
|
|
||||||
# FIXME: should be after backup completed
|
|
||||||
qvm_collection.unlock_db()
|
|
||||||
|
|
||||||
all_vms = [vm for vm in qvm_collection.values()]
|
|
||||||
selected_vms = [vm for vm in all_vms if vm.include_in_backups]
|
|
||||||
appvms_to_backup = [vm for vm in selected_vms if vm.is_appvm() and not vm.internal]
|
|
||||||
netvms_to_backup = [vm for vm in selected_vms if vm.is_netvm() and not vm.qid == 0]
|
|
||||||
template_vms_worth_backingup = [vm for vm in selected_vms if (vm.is_template() and not vm.installed_by_rpm)]
|
|
||||||
|
|
||||||
vms_list = appvms_to_backup + netvms_to_backup + template_vms_worth_backingup
|
|
||||||
|
|
||||||
vms_for_backup = vms_list
|
|
||||||
# Apply exclude list
|
|
||||||
if exclude_list:
|
|
||||||
vms_for_backup = [vm for vm in vms_list if vm.name not in exclude_list]
|
|
||||||
|
|
||||||
no_vms = len (vms_for_backup)
|
|
||||||
|
|
||||||
there_are_running_vms = False
|
|
||||||
|
|
||||||
fields_to_display = [
|
|
||||||
{ "name": "VM", "width": 16},
|
|
||||||
{ "name": "type","width": 12 },
|
|
||||||
{ "name": "size", "width": 12}
|
|
||||||
]
|
|
||||||
|
|
||||||
# Display the header
|
|
||||||
s = ""
|
|
||||||
for f in fields_to_display:
|
|
||||||
fmt="{{0:-^{0}}}-+".format(f["width"] + 1)
|
|
||||||
s += fmt.format('-')
|
|
||||||
print_callback(s)
|
|
||||||
s = ""
|
|
||||||
for f in fields_to_display:
|
|
||||||
fmt="{{0:>{0}}} |".format(f["width"] + 1)
|
|
||||||
s += fmt.format(f["name"])
|
|
||||||
print_callback(s)
|
|
||||||
s = ""
|
|
||||||
for f in fields_to_display:
|
|
||||||
fmt="{{0:-^{0}}}-+".format(f["width"] + 1)
|
|
||||||
s += fmt.format('-')
|
|
||||||
print_callback(s)
|
|
||||||
|
|
||||||
for vm in vms_for_backup:
|
|
||||||
if vm.is_template():
|
|
||||||
# handle templates later
|
|
||||||
continue
|
|
||||||
|
|
||||||
if vm.private_img is not None:
|
|
||||||
vm_sz = vm.get_disk_usage (vm.private_img)
|
|
||||||
files_to_backup += file_to_backup(vm.private_img, vm_sz )
|
|
||||||
|
|
||||||
if vm.is_appvm():
|
|
||||||
files_to_backup += file_to_backup(vm.icon_path)
|
|
||||||
if vm.updateable:
|
|
||||||
if os.path.exists(vm.dir_path + "/apps.templates"):
|
|
||||||
# template
|
|
||||||
files_to_backup += file_to_backup(vm.dir_path + "/apps.templates")
|
|
||||||
else:
|
|
||||||
# standaloneVM
|
|
||||||
files_to_backup += file_to_backup(vm.dir_path + "/apps")
|
|
||||||
|
|
||||||
if os.path.exists(vm.dir_path + "/kernels"):
|
|
||||||
files_to_backup += file_to_backup(vm.dir_path + "/kernels")
|
|
||||||
if os.path.exists (vm.firewall_conf):
|
|
||||||
files_to_backup += file_to_backup(vm.firewall_conf)
|
|
||||||
if 'appmenus_whitelist' in vm_files and \
|
|
||||||
os.path.exists(vm.dir_path + vm_files['appmenus_whitelist']):
|
|
||||||
files_to_backup += file_to_backup(vm.dir_path + vm_files['appmenus_whitelist'])
|
|
||||||
|
|
||||||
if vm.updateable:
|
|
||||||
sz = vm.get_disk_usage(vm.root_img)
|
|
||||||
files_to_backup += file_to_backup(vm.root_img, sz)
|
|
||||||
vm_sz += sz
|
|
||||||
|
|
||||||
s = ""
|
|
||||||
fmt="{{0:>{0}}} |".format(fields_to_display[0]["width"] + 1)
|
|
||||||
s += fmt.format(vm.name)
|
|
||||||
|
|
||||||
fmt="{{0:>{0}}} |".format(fields_to_display[1]["width"] + 1)
|
|
||||||
if vm.is_netvm():
|
|
||||||
s += fmt.format("NetVM" + (" + Sys" if vm.updateable else ""))
|
|
||||||
else:
|
|
||||||
s += fmt.format("AppVM" + (" + Sys" if vm.updateable else ""))
|
|
||||||
|
|
||||||
fmt="{{0:>{0}}} |".format(fields_to_display[2]["width"] + 1)
|
|
||||||
s += fmt.format(size_to_human(vm_sz))
|
|
||||||
|
|
||||||
if vm.is_running():
|
|
||||||
s += " <-- The VM is running, please shut it down before proceeding with the backup!"
|
|
||||||
there_are_running_vms = True
|
|
||||||
|
|
||||||
print_callback(s)
|
|
||||||
|
|
||||||
for vm in vms_for_backup:
|
|
||||||
if not vm.is_template():
|
|
||||||
# already handled
|
|
||||||
continue
|
|
||||||
vm_sz = vm.get_disk_utilization()
|
|
||||||
files_to_backup += file_to_backup (vm.dir_path, vm_sz)
|
|
||||||
|
|
||||||
s = ""
|
|
||||||
fmt="{{0:>{0}}} |".format(fields_to_display[0]["width"] + 1)
|
|
||||||
s += fmt.format(vm.name)
|
|
||||||
|
|
||||||
fmt="{{0:>{0}}} |".format(fields_to_display[1]["width"] + 1)
|
|
||||||
s += fmt.format("Template VM")
|
|
||||||
|
|
||||||
fmt="{{0:>{0}}} |".format(fields_to_display[2]["width"] + 1)
|
|
||||||
s += fmt.format(size_to_human(vm_sz))
|
|
||||||
|
|
||||||
if vm.is_running():
|
|
||||||
s += " <-- The VM is running, please shut it down before proceeding with the backup!"
|
|
||||||
there_are_running_vms = True
|
|
||||||
|
|
||||||
print_callback(s)
|
|
||||||
|
|
||||||
# Dom0 user home
|
|
||||||
if not 'dom0' in exclude_list:
|
|
||||||
local_user = grp.getgrnam('qubes').gr_mem[0]
|
|
||||||
home_dir = pwd.getpwnam(local_user).pw_dir
|
|
||||||
# Home dir should have only user-owned files, so fix it now to prevent
|
|
||||||
# permissions problems - some root-owned files can left after
|
|
||||||
# 'sudo bash' and similar commands
|
|
||||||
subprocess.check_call(['sudo', 'chown', '-R', local_user, home_dir])
|
|
||||||
|
|
||||||
home_sz = get_disk_usage(home_dir)
|
|
||||||
home_to_backup = [ { "path" : home_dir, "size": home_sz, "subdir": 'dom0-home'} ]
|
|
||||||
files_to_backup += home_to_backup
|
|
||||||
|
|
||||||
s = ""
|
|
||||||
fmt="{{0:>{0}}} |".format(fields_to_display[0]["width"] + 1)
|
|
||||||
s += fmt.format('Dom0')
|
|
||||||
|
|
||||||
fmt="{{0:>{0}}} |".format(fields_to_display[1]["width"] + 1)
|
|
||||||
s += fmt.format("User home")
|
|
||||||
|
|
||||||
fmt="{{0:>{0}}} |".format(fields_to_display[2]["width"] + 1)
|
|
||||||
s += fmt.format(size_to_human(home_sz))
|
|
||||||
|
|
||||||
print_callback(s)
|
|
||||||
|
|
||||||
total_backup_sz = 0
|
|
||||||
for file in files_to_backup:
|
|
||||||
total_backup_sz += file["size"]
|
|
||||||
|
|
||||||
s = ""
|
|
||||||
for f in fields_to_display:
|
|
||||||
fmt="{{0:-^{0}}}-+".format(f["width"] + 1)
|
|
||||||
s += fmt.format('-')
|
|
||||||
print_callback(s)
|
|
||||||
|
|
||||||
s = ""
|
|
||||||
fmt="{{0:>{0}}} |".format(fields_to_display[0]["width"] + 1)
|
|
||||||
s += fmt.format("Total size:")
|
|
||||||
fmt="{{0:>{0}}} |".format(fields_to_display[1]["width"] + 1 + 2 + fields_to_display[2]["width"] + 1)
|
|
||||||
s += fmt.format(size_to_human(total_backup_sz))
|
|
||||||
print_callback(s)
|
|
||||||
|
|
||||||
s = ""
|
|
||||||
for f in fields_to_display:
|
|
||||||
fmt="{{0:-^{0}}}-+".format(f["width"] + 1)
|
|
||||||
s += fmt.format('-')
|
|
||||||
print_callback(s)
|
|
||||||
|
|
||||||
stat = os.statvfs(base_backup_dir)
|
|
||||||
backup_fs_free_sz = stat.f_bsize * stat.f_bavail
|
|
||||||
print_callback("")
|
|
||||||
if (total_backup_sz > backup_fs_free_sz):
|
|
||||||
raise QubesException("Not enough space available on the backup filesystem!")
|
|
||||||
|
|
||||||
if (there_are_running_vms):
|
|
||||||
raise QubesException("Please shutdown all VMs before proceeding.")
|
|
||||||
|
|
||||||
print_callback("-> Available space: {0}".format(size_to_human(backup_fs_free_sz)))
|
|
||||||
|
|
||||||
return files_to_backup
|
|
||||||
|
|
||||||
def backup_do(base_backup_dir, files_to_backup, progress_callback = None):
|
|
||||||
|
|
||||||
total_backup_sz = 0
|
|
||||||
for file in files_to_backup:
|
|
||||||
total_backup_sz += file["size"]
|
|
||||||
|
|
||||||
backup_dir = base_backup_dir + "/qubes-{0}".format (time.strftime("%Y-%m-%d-%H%M%S"))
|
|
||||||
if os.path.exists (backup_dir):
|
|
||||||
raise QubesException("ERROR: the path {0} already exists?!".format(backup_dir))
|
|
||||||
|
|
||||||
os.mkdir (backup_dir)
|
|
||||||
|
|
||||||
if not os.path.exists (backup_dir):
|
|
||||||
raise QubesException("Strange: couldn't create backup dir: {0}?!".format(backup_dir))
|
|
||||||
|
|
||||||
bytes_backedup = 0
|
|
||||||
for file in files_to_backup:
|
|
||||||
# We prefer to use Linux's cp, because it nicely handles sparse files
|
|
||||||
progress = bytes_backedup * 100 / total_backup_sz
|
|
||||||
progress_callback(progress)
|
|
||||||
dest_dir = backup_dir + '/' + file["subdir"]
|
|
||||||
if file["subdir"] != "":
|
|
||||||
retcode = subprocess.call (["mkdir", "-p", dest_dir])
|
|
||||||
if retcode != 0:
|
|
||||||
raise QubesException("Cannot create directory: {0}?!".format(dest_dir))
|
|
||||||
|
|
||||||
retcode = subprocess.call (["cp", "-rp", file["path"], dest_dir])
|
|
||||||
if retcode != 0:
|
|
||||||
raise QubesException("Error while copying file {0} to {1}".format(file["path"], dest_dir))
|
|
||||||
|
|
||||||
bytes_backedup += file["size"]
|
|
||||||
progress = bytes_backedup * 100 / total_backup_sz
|
|
||||||
progress_callback(progress)
|
|
||||||
|
|
||||||
def backup_restore_set_defaults(options):
|
|
||||||
if 'use-default-netvm' not in options:
|
|
||||||
options['use-default-netvm'] = False
|
|
||||||
if 'use-none-netvm' not in options:
|
|
||||||
options['use-none-netvm'] = False
|
|
||||||
if 'use-default-template' not in options:
|
|
||||||
options['use-default-template'] = False
|
|
||||||
if 'dom0-home' not in options:
|
|
||||||
options['dom0-home'] = True
|
|
||||||
if 'replace-template' not in options:
|
|
||||||
options['replace-template'] = []
|
|
||||||
|
|
||||||
return options
|
|
||||||
|
|
||||||
|
|
||||||
def backup_restore_prepare(backup_dir, options = {}, host_collection = None):
|
|
||||||
# Defaults
|
|
||||||
backup_restore_set_defaults(options)
|
|
||||||
|
|
||||||
#### Private functions begin
|
|
||||||
def is_vm_included_in_backup (backup_dir, vm):
|
|
||||||
if vm.qid == 0:
|
|
||||||
# Dom0 is not included, obviously
|
|
||||||
return False
|
|
||||||
|
|
||||||
backup_vm_dir_path = vm.dir_path.replace (system_path["qubes_base_dir"], backup_dir)
|
|
||||||
|
|
||||||
if os.path.exists (backup_vm_dir_path):
|
|
||||||
return True
|
|
||||||
else:
|
|
||||||
return False
|
|
||||||
|
|
||||||
def find_template_name(template, replaces):
|
|
||||||
rx_replace = re.compile("(.*):(.*)")
|
|
||||||
for r in replaces:
|
|
||||||
m = rx_replace.match(r)
|
|
||||||
if m.group(1) == template:
|
|
||||||
return m.group(2)
|
|
||||||
|
|
||||||
return template
|
|
||||||
|
|
||||||
#### Private functions end
|
|
||||||
|
|
||||||
if not os.path.exists (backup_dir):
|
|
||||||
raise QubesException("The backup directory doesn't exist!")
|
|
||||||
|
|
||||||
backup_collection = QubesVmCollection(store_filename = backup_dir + "/qubes.xml")
|
|
||||||
backup_collection.lock_db_for_reading()
|
|
||||||
backup_collection.load()
|
|
||||||
|
|
||||||
if host_collection is None:
|
|
||||||
host_collection = QubesVmCollection()
|
|
||||||
host_collection.lock_db_for_reading()
|
|
||||||
host_collection.load()
|
|
||||||
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 is_vm_included_in_backup (backup_dir, vm):
|
|
||||||
|
|
||||||
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']
|
|
||||||
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_dir, 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
|
|
||||||
else:
|
|
||||||
netvm_name = vm.netvm.name
|
|
||||||
vms_to_restore[vm.name]['netvm'] = netvm_name
|
|
||||||
# Set to None to not confuse QubesVm object from backup
|
|
||||||
# collection with host collection (further in clone_attrs). Set
|
|
||||||
# directly _netvm to suppress setter action, especially
|
|
||||||
# modifying firewall
|
|
||||||
vm._netvm = None
|
|
||||||
|
|
||||||
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?
|
|
||||||
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_dir, 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
|
|
||||||
|
|
||||||
# ...and dom0 home
|
|
||||||
if options['dom0-home'] and os.path.exists(backup_dir + '/dom0-home'):
|
|
||||||
vms_to_restore['dom0'] = {}
|
|
||||||
local_user = grp.getgrnam('qubes').gr_mem[0]
|
|
||||||
|
|
||||||
dom0_homes = os.listdir(backup_dir + '/dom0-home')
|
|
||||||
if len(dom0_homes) > 1:
|
|
||||||
raise QubesException("More than one dom0 homedir in backup")
|
|
||||||
|
|
||||||
vms_to_restore['dom0']['username'] = dom0_homes[0]
|
|
||||||
if dom0_homes[0] != local_user:
|
|
||||||
vms_to_restore['dom0']['username-mismatch'] = True
|
|
||||||
if not options['ignore-dom0-username-mismatch']:
|
|
||||||
vms_to_restore['dom0']['good-to-go'] = False
|
|
||||||
|
|
||||||
if 'good-to-go' not in vms_to_restore['dom0']:
|
|
||||||
vms_to_restore['dom0']['good-to-go'] = True
|
|
||||||
|
|
||||||
return vms_to_restore
|
|
||||||
|
|
||||||
def backup_restore_print_summary(restore_info, print_callback = print_stdout):
|
|
||||||
fields = {
|
|
||||||
"qid": {"func": "vm.qid"},
|
|
||||||
|
|
||||||
"name": {"func": "('[' if vm.is_template() else '')\
|
|
||||||
+ ('{' if vm.is_netvm() else '')\
|
|
||||||
+ vm.name \
|
|
||||||
+ (']' if vm.is_template() else '')\
|
|
||||||
+ ('}' if vm.is_netvm() else '')"},
|
|
||||||
|
|
||||||
"type": {"func": "'Tpl' if vm.is_template() else \
|
|
||||||
'HVM' if vm.type == 'HVM' else \
|
|
||||||
vm.type.replace('VM','')"},
|
|
||||||
|
|
||||||
"updbl" : {"func": "'Yes' if vm.updateable else ''"},
|
|
||||||
|
|
||||||
"template": {"func": "'n/a' if vm.is_template() or vm.template is None else\
|
|
||||||
vm_info['template']"},
|
|
||||||
|
|
||||||
"netvm": {"func": "'n/a' if vm.is_netvm() and not vm.is_proxyvm() else\
|
|
||||||
('*' if vm.uses_default_netvm else '') +\
|
|
||||||
vm_info['netvm'] if vm_info['netvm'] is not None else '-'"},
|
|
||||||
|
|
||||||
"label" : {"func" : "vm.label.name"},
|
|
||||||
}
|
|
||||||
|
|
||||||
fields_to_display = ["name", "type", "template", "updbl", "netvm", "label" ]
|
|
||||||
|
|
||||||
# First calculate the maximum width of each field we want to display
|
|
||||||
total_width = 0;
|
|
||||||
for f in fields_to_display:
|
|
||||||
fields[f]["max_width"] = len(f)
|
|
||||||
for vm_info in restore_info.values():
|
|
||||||
if 'vm' in vm_info.keys():
|
|
||||||
vm = vm_info['vm']
|
|
||||||
l = len(str(eval(fields[f]["func"])))
|
|
||||||
if l > fields[f]["max_width"]:
|
|
||||||
fields[f]["max_width"] = l
|
|
||||||
total_width += fields[f]["max_width"]
|
|
||||||
|
|
||||||
print_callback("")
|
|
||||||
print_callback("The following VMs are included in the backup:")
|
|
||||||
print_callback("")
|
|
||||||
|
|
||||||
# Display the header
|
|
||||||
s = ""
|
|
||||||
for f in fields_to_display:
|
|
||||||
fmt="{{0:-^{0}}}-+".format(fields[f]["max_width"] + 1)
|
|
||||||
s += fmt.format('-')
|
|
||||||
print_callback(s)
|
|
||||||
s = ""
|
|
||||||
for f in fields_to_display:
|
|
||||||
fmt="{{0:>{0}}} |".format(fields[f]["max_width"] + 1)
|
|
||||||
s += fmt.format(f)
|
|
||||||
print_callback(s)
|
|
||||||
s = ""
|
|
||||||
for f in fields_to_display:
|
|
||||||
fmt="{{0:-^{0}}}-+".format(fields[f]["max_width"] + 1)
|
|
||||||
s += fmt.format('-')
|
|
||||||
print_callback(s)
|
|
||||||
|
|
||||||
for vm_info in restore_info.values():
|
|
||||||
# Skip non-VM here
|
|
||||||
if not 'vm' in vm_info:
|
|
||||||
continue
|
|
||||||
vm = vm_info['vm']
|
|
||||||
s = ""
|
|
||||||
for f in fields_to_display:
|
|
||||||
fmt="{{0:>{0}}} |".format(fields[f]["max_width"] + 1)
|
|
||||||
s += fmt.format(eval(fields[f]["func"]))
|
|
||||||
|
|
||||||
if 'excluded' in vm_info and vm_info['excluded']:
|
|
||||||
s += " <-- Excluded from restore"
|
|
||||||
elif 'already-exists' in vm_info:
|
|
||||||
s += " <-- A VM with the same name already exists on the host!"
|
|
||||||
elif 'missing-template' in vm_info:
|
|
||||||
s += " <-- No matching template on the host or in the backup found!"
|
|
||||||
elif 'missing-netvm' in vm_info:
|
|
||||||
s += " <-- No matching netvm on the host or in the backup found!"
|
|
||||||
elif 'orig-template' in vm_info:
|
|
||||||
s += " <-- Original template was '%s'" % (vm_info['orig-template'])
|
|
||||||
|
|
||||||
print_callback(s)
|
|
||||||
|
|
||||||
if 'dom0' in restore_info.keys():
|
|
||||||
s = ""
|
|
||||||
for f in fields_to_display:
|
|
||||||
fmt="{{0:>{0}}} |".format(fields[f]["max_width"] + 1)
|
|
||||||
if f == "name":
|
|
||||||
s += fmt.format("Dom0")
|
|
||||||
elif f == "type":
|
|
||||||
s += fmt.format("Home")
|
|
||||||
else:
|
|
||||||
s += fmt.format("")
|
|
||||||
if 'username-mismatch' in restore_info['dom0']:
|
|
||||||
s += " <-- username in backup and dom0 mismatch"
|
|
||||||
|
|
||||||
print_callback(s)
|
|
||||||
|
|
||||||
def backup_restore_do(backup_dir, restore_info, host_collection = None, print_callback = print_stdout, error_callback = print_stderr):
|
|
||||||
|
|
||||||
### Private functions begin
|
|
||||||
def restore_vm_dir (backup_dir, src_dir, dst_dir):
|
|
||||||
|
|
||||||
backup_src_dir = src_dir.replace (system_path["qubes_base_dir"], backup_dir)
|
|
||||||
|
|
||||||
# We prefer to use Linux's cp, because it nicely handles sparse files
|
|
||||||
retcode = subprocess.call (["cp", "-rp", backup_src_dir, dst_dir])
|
|
||||||
if retcode != 0:
|
|
||||||
raise QubesException("*** Error while copying file {0} to {1}".format(backup_src_dir, dest_dir))
|
|
||||||
### Private functions end
|
|
||||||
|
|
||||||
lock_obtained = False
|
|
||||||
if host_collection is None:
|
|
||||||
host_collection = QubesVmCollection()
|
|
||||||
host_collection.lock_db_for_writing()
|
|
||||||
host_collection.load()
|
|
||||||
lock_obtained = True
|
|
||||||
|
|
||||||
# Add VM in right order
|
|
||||||
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
|
|
||||||
vm = vm_info['vm']
|
|
||||||
if not vm.__class__ == vm_class:
|
|
||||||
continue
|
|
||||||
print_callback("-> Restoring {type} {0}...".format(vm.name, type=vm_class_name))
|
|
||||||
retcode = subprocess.call (["mkdir", "-p", vm.dir_path])
|
|
||||||
if retcode != 0:
|
|
||||||
error_callback("*** Cannot create directory: {0}?!".format(dest_dir))
|
|
||||||
error_callback("Skipping...")
|
|
||||||
continue
|
|
||||||
|
|
||||||
|
|
||||||
template = None
|
|
||||||
if vm.template is not None:
|
|
||||||
template_name = vm_info['template']
|
|
||||||
template = host_collection.get_vm_by_name(template_name)
|
|
||||||
|
|
||||||
new_vm = None
|
|
||||||
|
|
||||||
try:
|
|
||||||
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,
|
|
||||||
installed_by_rpm=False)
|
|
||||||
restore_vm_dir (backup_dir, vm.dir_path, os.path.dirname(new_vm.dir_path));
|
|
||||||
|
|
||||||
new_vm.verify_files()
|
|
||||||
except Exception as err:
|
|
||||||
error_callback("ERROR: {0}".format(err))
|
|
||||||
error_callback("*** Skipping VM: {0}".format(vm.name))
|
|
||||||
if new_vm:
|
|
||||||
host_collection.pop(new_vm.qid)
|
|
||||||
continue
|
|
||||||
|
|
||||||
try:
|
|
||||||
new_vm.clone_attrs(vm)
|
|
||||||
except Exception as err:
|
|
||||||
error_callback("ERROR: {0}".format(err))
|
|
||||||
error_callback("*** Some VM property will not be restored")
|
|
||||||
|
|
||||||
try:
|
|
||||||
new_vm.appmenus_create(verbose=True)
|
|
||||||
except Exception as err:
|
|
||||||
error_callback("ERROR during appmenu restore: {0}".format(err))
|
|
||||||
error_callback("*** VM '{0}' will not have appmenus".format(vm.name))
|
|
||||||
|
|
||||||
# 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
|
|
||||||
vm = vm_info['vm']
|
|
||||||
host_vm = host_collection.get_vm_by_name(vm.name)
|
|
||||||
if host_vm is None:
|
|
||||||
# Failed/skipped VM
|
|
||||||
continue
|
|
||||||
|
|
||||||
if not vm.uses_default_netvm:
|
|
||||||
host_vm.netvm = host_collection.get_vm_by_name (vm_info['netvm']) if vm_info['netvm'] is not None else None
|
|
||||||
|
|
||||||
host_collection.save()
|
|
||||||
if lock_obtained:
|
|
||||||
host_collection.unlock_db()
|
|
||||||
|
|
||||||
# ... and dom0 home as last step
|
|
||||||
if 'dom0' in restore_info.keys() and restore_info['dom0']['good-to-go']:
|
|
||||||
backup_info = restore_info['dom0']
|
|
||||||
local_user = grp.getgrnam('qubes').gr_mem[0]
|
|
||||||
home_dir = pwd.getpwnam(local_user).pw_dir
|
|
||||||
backup_dom0_home_dir = backup_dir + '/dom0-home/' + backup_info['username']
|
|
||||||
restore_home_backupdir = "home-pre-restore-{0}".format (time.strftime("%Y-%m-%d-%H%M%S"))
|
|
||||||
|
|
||||||
print_callback("-> Restoring home of user '{0}'...".format(local_user))
|
|
||||||
print_callback("--> Existing files/dirs backed up in '{0}' dir".format(restore_home_backupdir))
|
|
||||||
os.mkdir(home_dir + '/' + restore_home_backupdir)
|
|
||||||
for f in os.listdir(backup_dom0_home_dir):
|
|
||||||
home_file = home_dir + '/' + f
|
|
||||||
if os.path.exists(home_file):
|
|
||||||
os.rename(home_file, home_dir + '/' + restore_home_backupdir + '/' + f)
|
|
||||||
retcode = subprocess.call (["cp", "-nrp", backup_dom0_home_dir + '/' + f, home_file])
|
|
||||||
if retcode != 0:
|
|
||||||
error_callback("*** Error while copying file {0} to {1}".format(backup_dom0_home_dir + '/' + f, home_file))
|
|
||||||
retcode = subprocess.call(['sudo', 'chown', '-R', local_user, home_dir])
|
|
||||||
if retcode != 0:
|
|
||||||
error_callback("*** Error while setting home directory owner")
|
|
||||||
|
|
||||||
# vim:sw=4:et:
|
# vim:sw=4:et:
|
||||||
|
@ -22,10 +22,12 @@
|
|||||||
|
|
||||||
from qubes.qubes import QubesVmCollection
|
from qubes.qubes import QubesVmCollection
|
||||||
from qubes.qubes import QubesException
|
from qubes.qubes import QubesException
|
||||||
from qubes.qubesutils import backup_prepare, backup_do
|
from qubes.backup import backup_prepare, backup_do
|
||||||
|
from qubes.qubesutils import size_to_human
|
||||||
from optparse import OptionParser
|
from optparse import OptionParser
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
import getpass
|
||||||
|
|
||||||
def print_progress(progress):
|
def print_progress(progress):
|
||||||
print >> sys.stderr, "\r-> Backing up files: {0}%...".format (progress),
|
print >> sys.stderr, "\r-> Backing up files: {0}%...".format (progress),
|
||||||
@ -38,6 +40,10 @@ def main():
|
|||||||
help="Exclude the specified VM from backup (might be repeated)")
|
help="Exclude the specified VM from backup (might be repeated)")
|
||||||
parser.add_option ("--force-root", action="store_true", dest="force_root", default=False,
|
parser.add_option ("--force-root", action="store_true", dest="force_root", default=False,
|
||||||
help="Force to run, even with root privileges")
|
help="Force to run, even with root privileges")
|
||||||
|
parser.add_option ("-d", "--dest-vm", action="store", dest="appvm",
|
||||||
|
help="The AppVM to send backups to")
|
||||||
|
parser.add_option ("-e", "--encrypt", action="store_true", dest="encrypt", default=False,
|
||||||
|
help="Encrypts the backup")
|
||||||
|
|
||||||
(options, args) = parser.parse_args ()
|
(options, args) = parser.parse_args ()
|
||||||
|
|
||||||
@ -66,17 +72,57 @@ def main():
|
|||||||
|
|
||||||
files_to_backup = None
|
files_to_backup = None
|
||||||
try:
|
try:
|
||||||
files_to_backup = backup_prepare(base_backup_dir, vms_list=vms, exclude_list=options.exclude_list)
|
files_to_backup = backup_prepare(
|
||||||
|
vms_list=vms,
|
||||||
|
exclude_list=options.exclude_list,
|
||||||
|
hide_vm_names=options.encrypt)
|
||||||
except QubesException as e:
|
except QubesException as e:
|
||||||
print >>sys.stderr, "ERROR: %s" % str(e)
|
print >>sys.stderr, "ERROR: %s" % str(e)
|
||||||
exit(1)
|
exit(1)
|
||||||
|
|
||||||
|
total_backup_sz = reduce(lambda size, file: size+file["size"],
|
||||||
|
files_to_backup, 0)
|
||||||
|
|
||||||
|
if not options.appvm:
|
||||||
|
appvm = None
|
||||||
|
|
||||||
|
stat = os.statvfs(base_backup_dir)
|
||||||
|
backup_fs_free_sz = stat.f_bsize * stat.f_bavail
|
||||||
|
print
|
||||||
|
if (total_backup_sz > backup_fs_free_sz):
|
||||||
|
print >>sys.stderr, "ERROR: Not enough space available on the backup filesystem!"
|
||||||
|
exit(1)
|
||||||
|
|
||||||
|
print "-> Available space: {0}".format(size_to_human(backup_fs_free_sz))
|
||||||
|
else:
|
||||||
|
appvm = qvm_collection.get_vm_by_name(options.appvm)
|
||||||
|
if appvm is None:
|
||||||
|
print >>sys.stderr, "ERROR: VM {0} does not exist".format(options.appvm)
|
||||||
|
exit(1)
|
||||||
|
|
||||||
|
stat = os.statvfs('/var/tmp')
|
||||||
|
backup_fs_free_sz = stat.f_bsize * stat.f_bavail
|
||||||
|
print
|
||||||
|
if (backup_fs_free_sz < 1000000000):
|
||||||
|
print >>sys.stderr, "ERROR: Not enough space available " \
|
||||||
|
"on the local filesystem (needs 1GB for temporary files)!"
|
||||||
|
exit(1)
|
||||||
|
|
||||||
prompt = raw_input ("Do you want to proceed? [y/N] ")
|
prompt = raw_input ("Do you want to proceed? [y/N] ")
|
||||||
if not (prompt == "y" or prompt == "Y"):
|
if not (prompt == "y" or prompt == "Y"):
|
||||||
exit (0)
|
exit (0)
|
||||||
|
|
||||||
|
passphrase = getpass.getpass("Please enter the pass phrase that will be used to encrypt/verify the backup: ")
|
||||||
|
passphrase2 = getpass.getpass("Enter again for verification: ")
|
||||||
|
if passphrase != passphrase2:
|
||||||
|
print >>sys.stderr, "ERROR: Password mismatch"
|
||||||
|
exit(1)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
backup_do(base_backup_dir, files_to_backup, progress_callback=print_progress)
|
backup_do(base_backup_dir, files_to_backup, passphrase,
|
||||||
|
progress_callback=print_progress,
|
||||||
|
encrypt=options.encrypt,
|
||||||
|
appvm=appvm)
|
||||||
except QubesException as e:
|
except QubesException as e:
|
||||||
print >>sys.stderr, "ERROR: %s" % str(e)
|
print >>sys.stderr, "ERROR: %s" % str(e)
|
||||||
exit(1)
|
exit(1)
|
||||||
|
@ -22,13 +22,15 @@
|
|||||||
|
|
||||||
from qubes.qubes import QubesVmCollection
|
from qubes.qubes import QubesVmCollection
|
||||||
from qubes.qubes import QubesException
|
from qubes.qubes import QubesException
|
||||||
from qubes.qubesutils import backup_restore_prepare
|
from qubes.backup import backup_restore_header
|
||||||
from qubes.qubesutils import backup_restore_print_summary
|
from qubes.backup import backup_restore_prepare
|
||||||
from qubes.qubesutils import backup_restore_do
|
from qubes.backup import backup_restore_print_summary
|
||||||
|
from qubes.backup import backup_restore_do
|
||||||
from optparse import OptionParser
|
from optparse import OptionParser
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
import getpass
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
usage = "usage: %prog [options] <backup-dir>"
|
usage = "usage: %prog [options] <backup-dir>"
|
||||||
@ -58,6 +60,12 @@ def main():
|
|||||||
parser.add_option ("--ignore-username-mismatch", action="store_true", dest="ignore_username_mismatch", default=False,
|
parser.add_option ("--ignore-username-mismatch", action="store_true", dest="ignore_username_mismatch", default=False,
|
||||||
help="Ignore dom0 username mismatch while restoring homedir")
|
help="Ignore dom0 username mismatch while restoring homedir")
|
||||||
|
|
||||||
|
parser.add_option ("-d", "--dest-vm", action="store", dest="appvm",
|
||||||
|
help="The AppVM to send backups to")
|
||||||
|
|
||||||
|
parser.add_option ("-e", "--encrypted", action="store_true", dest="decrypt", default=False,
|
||||||
|
help="The backup is encrypted")
|
||||||
|
|
||||||
(options, args) = parser.parse_args ()
|
(options, args) = parser.parse_args ()
|
||||||
|
|
||||||
if (len (args) != 1):
|
if (len (args) != 1):
|
||||||
@ -66,9 +74,9 @@ def main():
|
|||||||
|
|
||||||
backup_dir = args[0]
|
backup_dir = args[0]
|
||||||
|
|
||||||
if not os.path.exists (backup_dir):
|
#if not os.path.exists (backup_dir):
|
||||||
print >> sys.stderr, "The backup directory doesn't exist!"
|
# print >> sys.stderr, "The backup directory doesn't exist!"
|
||||||
exit(1)
|
# exit(1)
|
||||||
|
|
||||||
host_collection = QubesVmCollection()
|
host_collection = QubesVmCollection()
|
||||||
host_collection.lock_db_for_writing()
|
host_collection.lock_db_for_writing()
|
||||||
@ -87,9 +95,28 @@ def main():
|
|||||||
if options.exclude:
|
if options.exclude:
|
||||||
restore_options['exclude'] = options.exclude
|
restore_options['exclude'] = options.exclude
|
||||||
|
|
||||||
|
appvm = None
|
||||||
|
if options.appvm is not None:
|
||||||
|
appvm = host_collection.get_vm_by_name(options.appvm)
|
||||||
|
if appvm is None:
|
||||||
|
print >>sys.stderr, "ERROR: VM {0} does not exist".format(options.appvm)
|
||||||
|
exit(1)
|
||||||
|
|
||||||
|
passphrase = getpass.getpass("Please enter the pass phrase that will be used to decrypt/verify the backup: ")
|
||||||
|
|
||||||
|
print >> sys.stderr, "Checking backup content..."
|
||||||
|
restore_tmpdir,qubes_xml = backup_restore_header(backup_dir, passphrase, encrypted=options.decrypt, appvm=appvm)
|
||||||
|
|
||||||
restore_info = None
|
restore_info = None
|
||||||
try:
|
try:
|
||||||
restore_info = backup_restore_prepare(backup_dir, options=restore_options, host_collection=host_collection)
|
restore_info = backup_restore_prepare(
|
||||||
|
backup_dir,
|
||||||
|
os.path.join(restore_tmpdir, qubes_xml),
|
||||||
|
passphrase,
|
||||||
|
options=restore_options,
|
||||||
|
host_collection=host_collection,
|
||||||
|
encrypt=options.decrypt,
|
||||||
|
appvm=appvm)
|
||||||
except QubesException as e:
|
except QubesException as e:
|
||||||
print >> sys.stderr, "ERROR: %s" % str(e)
|
print >> sys.stderr, "ERROR: %s" % str(e)
|
||||||
exit(1)
|
exit(1)
|
||||||
@ -113,8 +140,6 @@ def main():
|
|||||||
if 'username-mismatch' in vm_info.keys():
|
if 'username-mismatch' in vm_info.keys():
|
||||||
dom0_username_mismatch = True
|
dom0_username_mismatch = True
|
||||||
|
|
||||||
print
|
|
||||||
|
|
||||||
if os.geteuid() == 0:
|
if os.geteuid() == 0:
|
||||||
print >> sys.stderr, "*** Running this tool as root is strongly discouraged, this will lead you in permissions problems."
|
print >> sys.stderr, "*** Running this tool as root is strongly discouraged, this will lead you in permissions problems."
|
||||||
if options.force_root:
|
if options.force_root:
|
||||||
@ -179,7 +204,14 @@ def main():
|
|||||||
if not (prompt == "y" or prompt == "Y"):
|
if not (prompt == "y" or prompt == "Y"):
|
||||||
exit (0)
|
exit (0)
|
||||||
|
|
||||||
backup_restore_do(backup_dir, restore_info, host_collection=host_collection)
|
|
||||||
|
backup_restore_do(backup_dir,
|
||||||
|
restore_tmpdir,
|
||||||
|
passphrase,
|
||||||
|
restore_info,
|
||||||
|
host_collection=host_collection,
|
||||||
|
encrypted=options.decrypt,
|
||||||
|
appvm=appvm)
|
||||||
|
|
||||||
host_collection.unlock_db()
|
host_collection.unlock_db()
|
||||||
|
|
||||||
|
@ -110,6 +110,8 @@ cp core/qubesutils.py $RPM_BUILD_ROOT%{python_sitearch}/qubes
|
|||||||
cp core/qubesutils.py[co] $RPM_BUILD_ROOT%{python_sitearch}/qubes
|
cp core/qubesutils.py[co] $RPM_BUILD_ROOT%{python_sitearch}/qubes
|
||||||
cp core/guihelpers.py $RPM_BUILD_ROOT%{python_sitearch}/qubes
|
cp core/guihelpers.py $RPM_BUILD_ROOT%{python_sitearch}/qubes
|
||||||
cp core/guihelpers.py[co] $RPM_BUILD_ROOT%{python_sitearch}/qubes
|
cp core/guihelpers.py[co] $RPM_BUILD_ROOT%{python_sitearch}/qubes
|
||||||
|
cp core/backup.py $RPM_BUILD_ROOT%{python_sitearch}/qubes
|
||||||
|
cp core/backup.py[co] $RPM_BUILD_ROOT%{python_sitearch}/qubes
|
||||||
cp core/__init__.py $RPM_BUILD_ROOT%{python_sitearch}/qubes
|
cp core/__init__.py $RPM_BUILD_ROOT%{python_sitearch}/qubes
|
||||||
cp core/__init__.py[co] $RPM_BUILD_ROOT%{python_sitearch}/qubes
|
cp core/__init__.py[co] $RPM_BUILD_ROOT%{python_sitearch}/qubes
|
||||||
cp qmemman/qmemman*py $RPM_BUILD_ROOT%{python_sitearch}/qubes
|
cp qmemman/qmemman*py $RPM_BUILD_ROOT%{python_sitearch}/qubes
|
||||||
@ -273,6 +275,9 @@ fi
|
|||||||
%{python_sitearch}/qubes/guihelpers.py
|
%{python_sitearch}/qubes/guihelpers.py
|
||||||
%{python_sitearch}/qubes/guihelpers.pyc
|
%{python_sitearch}/qubes/guihelpers.pyc
|
||||||
%{python_sitearch}/qubes/guihelpers.pyo
|
%{python_sitearch}/qubes/guihelpers.pyo
|
||||||
|
%{python_sitearch}/qubes/backup.py
|
||||||
|
%{python_sitearch}/qubes/backup.pyc
|
||||||
|
%{python_sitearch}/qubes/backup.pyo
|
||||||
%{python_sitearch}/qubes/__init__.py
|
%{python_sitearch}/qubes/__init__.py
|
||||||
%{python_sitearch}/qubes/__init__.pyc
|
%{python_sitearch}/qubes/__init__.pyc
|
||||||
%{python_sitearch}/qubes/__init__.pyo
|
%{python_sitearch}/qubes/__init__.pyo
|
||||||
|
Loading…
Reference in New Issue
Block a user