dom0/core: rework of VM clone mechanism
This commit is contained in:
parent
5191c64b06
commit
5f87303b69
@ -921,6 +921,73 @@ class QubesVm(object):
|
||||
except subprocess.CalledProcessError:
|
||||
print >> sys.stderr, "Ooops, there was a problem creating appmenus for {0} VM!".format (self.name)
|
||||
|
||||
def get_clone_attrs(self):
|
||||
return ['kernel', 'uses_default_kernel', 'netvm_vm', 'uses_default_netvm', \
|
||||
'memory', 'maxmem', 'kernelopts', 'uses_default_kernelopts', 'services', 'vcpus']
|
||||
|
||||
def clone_attrs(self, src_vm):
|
||||
for prop in self.get_clone_attrs():
|
||||
setattr(self, prop, getattr(src_vm, prop))
|
||||
|
||||
def clone_disk_files(self, src_vm, verbose):
|
||||
if dry_run:
|
||||
return
|
||||
|
||||
if src_vm.is_running():
|
||||
raise QubesException("Attempt to clone a running VM!")
|
||||
|
||||
if verbose:
|
||||
print >> sys.stderr, "--> Creating directory: {0}".format(self.dir_path)
|
||||
os.mkdir (self.dir_path)
|
||||
|
||||
if src_vm.private_img is not None and self.private_img is not None:
|
||||
if verbose:
|
||||
print >> sys.stderr, "--> Copying the private image:\n{0} ==>\n{1}".\
|
||||
format(src_vm.private_img, self.private_img)
|
||||
# We prefer to use Linux's cp, because it nicely handles sparse files
|
||||
retcode = subprocess.call (["cp", src_vm.private_img, self.private_img])
|
||||
if retcode != 0:
|
||||
raise IOError ("Error while copying {0} to {1}".\
|
||||
format(src_vm.private_img, self.private_img))
|
||||
|
||||
if src_vm.updateable and src_vm.root_img is not None and self.root_img is not None:
|
||||
if verbose:
|
||||
print >> sys.stderr, "--> Copying the root image:\n{0} ==>\n{1}".\
|
||||
format(src_vm.root_img, self.root_img)
|
||||
# We prefer to use Linux's cp, because it nicely handles sparse files
|
||||
retcode = subprocess.call (["cp", src_vm.root_img, self.root_img])
|
||||
if retcode != 0:
|
||||
raise IOError ("Error while copying {0} to {1}".\
|
||||
format(src_vm.root_img, self.root_img))
|
||||
|
||||
if src_vm.updateable and src_vm.appmenus_templates_dir is not None and self.appmenus_templates_dir is not None:
|
||||
if verbose:
|
||||
print >> sys.stderr, "--> Copying the template's appmenus templates dir:\n{0} ==>\n{1}".\
|
||||
format(src_vm.appmenus_templates_dir, self.appmenus_templates_dir)
|
||||
shutil.copytree (src_vm.appmenus_templates_dir, self.appmenus_templates_dir)
|
||||
|
||||
if os.path.exists(src_vm.dir_path + '/' + qubes_whitelisted_appmenus):
|
||||
if verbose:
|
||||
print >> sys.stderr, "--> Copying whitelisted apps list: {0}".\
|
||||
format(self.dir_path + '/' + qubes_whitelisted_appmenus)
|
||||
shutil.copy(src_vm.dir_path + '/' + qubes_whitelisted_appmenus,
|
||||
self.dir_path + '/' + qubes_whitelisted_appmenus)
|
||||
|
||||
if src_vm.icon_path is not None and self.icon_path is not None:
|
||||
if os.path.exists (src_vm.dir_path):
|
||||
if os.path.islink(src_vm.dir_path):
|
||||
icon_path = os.readlink(src_vm.dir_path)
|
||||
if verbose:
|
||||
print >> sys.stderr, "--> Creating icon symlink: {0} -> {1}".format(self.icon_path, icon_path)
|
||||
os.symlink (icon_path, self.icon_path)
|
||||
else:
|
||||
if verbose:
|
||||
print >> sys.stderr, "--> Copying icon: {0} -> {1}".format(src_vm.icon_path, self.icon_path)
|
||||
shutil.copy(src_vm.icon_path, self.icon_path)
|
||||
|
||||
# Create appmenus
|
||||
self.create_appmenus(verbose)
|
||||
|
||||
def remove_appmenus(self):
|
||||
vmtype = None
|
||||
if self.is_netvm():
|
||||
@ -1389,39 +1456,24 @@ class QubesTemplateVm(QubesVm):
|
||||
def get_rootdev(self, source_template=None):
|
||||
return "'script:origin:{dir}/root.img:{dir}/root-cow.img,xvda,w',".format(dir=self.dir_path)
|
||||
|
||||
def clone_disk_files(self, src_template_vm, verbose):
|
||||
def clone_disk_files(self, src_vm, verbose):
|
||||
if dry_run:
|
||||
return
|
||||
|
||||
super(QubesTemplateVM, self).clone_disk_files(src_vm=src_vm, verbose=verbose)
|
||||
|
||||
assert not src_template_vm.is_running(), "Attempt to clone a running Template VM!"
|
||||
if os.path.exists(src_vm.dir_path + '/vm-' + qubes_whitelisted_appmenus):
|
||||
if verbose:
|
||||
print >> sys.stderr, "--> Copying default whitelisted apps list: {0}".\
|
||||
format(self.dir_path + '/vm-' + qubes_whitelisted_appmenus)
|
||||
shutil.copy(src_vm.dir_path + '/vm-' + qubes_whitelisted_appmenus,
|
||||
self.dir_path + '/vm-' + qubes_whitelisted_appmenus)
|
||||
|
||||
if verbose:
|
||||
print >> sys.stderr, "--> Creating directory: {0}".format(self.dir_path)
|
||||
os.mkdir (self.dir_path)
|
||||
|
||||
if verbose:
|
||||
print >> sys.stderr, "--> Copying the template's private image:\n{0} ==>\n{1}".\
|
||||
format(src_template_vm.private_img, self.private_img)
|
||||
# We prefer to use Linux's cp, because it nicely handles sparse files
|
||||
retcode = subprocess.call (["cp", src_template_vm.private_img, self.private_img])
|
||||
if retcode != 0:
|
||||
raise IOError ("Error while copying {0} to {1}".\
|
||||
format(src_template_vm.private_img, self.private_img))
|
||||
|
||||
if verbose:
|
||||
print >> sys.stderr, "--> Copying the template's root image:\n{0} ==>\n{1}".\
|
||||
format(src_template_vm.root_img, self.root_img)
|
||||
# We prefer to use Linux's cp, because it nicely handles sparse files
|
||||
retcode = subprocess.call (["cp", src_template_vm.root_img, self.root_img])
|
||||
if retcode != 0:
|
||||
raise IOError ("Error while copying {0} to {1}".\
|
||||
format(src_template_vm.root_img, self.root_img))
|
||||
if verbose:
|
||||
print >> sys.stderr, "--> Copying the template's clean volatile image:\n{0} ==>\n{1}".\
|
||||
format(src_template_vm.clean_volatile_img, self.clean_volatile_img)
|
||||
format(src_vm.clean_volatile_img, self.clean_volatile_img)
|
||||
# We prefer to use Linux's cp, because it nicely handles sparse files
|
||||
retcode = subprocess.call (["cp", src_template_vm.clean_volatile_img, self.clean_volatile_img])
|
||||
retcode = subprocess.call (["cp", src_vm.clean_volatile_img, self.clean_volatile_img])
|
||||
if retcode != 0:
|
||||
raise IOError ("Error while copying {0} to {1}".\
|
||||
format(src_template_vm.clean_volatile_img, self.clean_volatile_img))
|
||||
@ -1432,44 +1484,17 @@ class QubesTemplateVm(QubesVm):
|
||||
retcode = subprocess.call (["cp", self.clean_volatile_img, self.volatile_img])
|
||||
if retcode != 0:
|
||||
raise IOError ("Error while copying {0} to {1}".\
|
||||
format(self.clean_volatile_img, self.volatile_img))
|
||||
format(self.clean_img, self.volatile_img))
|
||||
|
||||
if verbose:
|
||||
print >> sys.stderr, "--> Copying the template's DispVM prerun script..."
|
||||
retcode = subprocess.call (["cp", src_template_vm.dir_path + '/dispvm-prerun.sh', self.dir_path + '/dispvm-prerun.sh'])
|
||||
retcode = subprocess.call (["cp", src_vm.dir_path + '/dispvm-prerun.sh', self.dir_path + '/dispvm-prerun.sh'])
|
||||
if retcode != 0:
|
||||
raise IOError ("Error while copying DispVM prerun script")
|
||||
|
||||
if verbose:
|
||||
print >> sys.stderr, "--> Copying the template's appmenus templates dir:\n{0} ==>\n{1}".\
|
||||
format(src_template_vm.appmenus_templates_dir, self.appmenus_templates_dir)
|
||||
shutil.copytree (src_template_vm.appmenus_templates_dir, self.appmenus_templates_dir)
|
||||
|
||||
if os.path.exists(src_template_vm.dir_path + '/' + qubes_whitelisted_appmenus):
|
||||
if verbose:
|
||||
print >> sys.stderr, "--> Copying whitelisted apps list: {0}".\
|
||||
format(self.dir_path + '/' + qubes_whitelisted_appmenus)
|
||||
shutil.copy(src_template_vm.dir_path + '/' + qubes_whitelisted_appmenus,
|
||||
self.dir_path + '/' + qubes_whitelisted_appmenus)
|
||||
|
||||
if os.path.exists(src_template_vm.dir_path + '/vm-' + qubes_whitelisted_appmenus):
|
||||
if verbose:
|
||||
print >> sys.stderr, "--> Copying default whitelisted apps list: {0}".\
|
||||
format(self.dir_path + '/vm-' + qubes_whitelisted_appmenus)
|
||||
shutil.copy(src_template_vm.dir_path + '/vm-' + qubes_whitelisted_appmenus,
|
||||
self.dir_path + '/vm-' + qubes_whitelisted_appmenus)
|
||||
|
||||
icon_path = "/usr/share/qubes/icons/template.png"
|
||||
if verbose:
|
||||
print >> sys.stderr, "--> Creating icon symlink: {0} -> {1}".format(self.icon_path, icon_path)
|
||||
os.symlink (icon_path, self.icon_path)
|
||||
|
||||
# Create root-cow.img
|
||||
self.commit_changes(verbose=verbose)
|
||||
|
||||
# Create appmenus
|
||||
self.create_appmenus(verbose, source_template = src_template_vm)
|
||||
|
||||
def create_appmenus(self, verbose, source_template = None):
|
||||
if source_template is None:
|
||||
source_template = self.template_vm
|
||||
|
@ -21,13 +21,14 @@
|
||||
#
|
||||
|
||||
from qubes.qubes import QubesVmCollection
|
||||
from qubes.qubes import QubesAppVm, QubesTemplateVm
|
||||
from qubes.qubes import QubesException
|
||||
from optparse import OptionParser;
|
||||
import sys
|
||||
|
||||
def main():
|
||||
usage = "usage: %prog [options] <src-template-name> <new-template-name>\n"\
|
||||
"Clones an existing template by copying all its disk files"
|
||||
usage = "usage: %prog [options] <src-name> <new-name>\n"\
|
||||
"Clones an existing VM by copying all its disk files"
|
||||
|
||||
parser = OptionParser (usage)
|
||||
parser.add_option ("-q", "--quiet", action="store_false", dest="verbose", default=True)
|
||||
@ -44,8 +45,8 @@ def main():
|
||||
qvm_collection.lock_db_for_writing()
|
||||
qvm_collection.load()
|
||||
|
||||
src_tvm = qvm_collection.get_vm_by_name(srcname)
|
||||
if src_tvm is None:
|
||||
src_vm = qvm_collection.get_vm_by_name(srcname)
|
||||
if src_vm is None:
|
||||
print >> sys.stderr, "ERROR: A VM with the name '{0}' does not exist in the system.".format(srcname)
|
||||
exit(1)
|
||||
|
||||
@ -53,15 +54,24 @@ def main():
|
||||
print >> sys.stderr, "ERROR: A VM with the name '{0}' already exists in the system.".format(dstname)
|
||||
exit(1)
|
||||
|
||||
dst_tvm = qvm_collection.clone_templatevm(src_template_vm=src_tvm,
|
||||
name=dstname,
|
||||
dst_vm = None
|
||||
if isinstance(src_vm, QubesTemplateVm):
|
||||
dst_vm = qvm_collection.add_new_templatevm(name=dstname,
|
||||
dir_path=options.dir_path, installed_by_rpm=False)
|
||||
elif isinstance(src_vm, QubesAppVm):
|
||||
dst_vm = qvm_collection.add_new_appvm(name=dstname, template_vm=src_vm.template_vm,
|
||||
updateable=src_vm.updateable, label=src_vm.label,
|
||||
dir_path=options.dir_path)
|
||||
else:
|
||||
print >> sys.stderr, "ERROR: Clone not supported for this type of VM"
|
||||
exit(1)
|
||||
|
||||
try:
|
||||
dst_tvm.clone_disk_files (src_template_vm=src_tvm, verbose=options.verbose)
|
||||
dst_vm.clone_attrs(src_vm)
|
||||
dst_vm.clone_disk_files (src_vm=src_vm, verbose=options.verbose)
|
||||
except (IOError, OSError) as err:
|
||||
print >> sys.stderr, "ERROR: {0}".format(err)
|
||||
qvm_collection.pop(dst_tvm.qid)
|
||||
qvm_collection.pop(dst_vm.qid)
|
||||
exit (1)
|
||||
|
||||
qvm_collection.save()
|
Loading…
Reference in New Issue
Block a user