Template support for HVM (#719)

Any HVM (which isn't already template-based) can be a template for
another HVM. For now do not allow simultaneous run of template and its
VM (this assumption simplify the implementation, as no root-cow.img is
needed).
This commit is contained in:
Marek Marczykowski-Górecki 2013-11-19 18:42:59 +01:00
parent 92b479bf49
commit 2005207462
4 changed files with 76 additions and 13 deletions

View File

@ -444,6 +444,19 @@ class QubesVm(object):
for hook in self.hooks_post_rename:
hook(self, old_name)
@property
def template(self):
return self._template
@template.setter
def template(self, value):
# FIXME: check if the value is instance of QubesTemplateVM, not the VM
# type. The problem is while this file is loaded, QubesTemplateVM is
# not defined yet.
if value and (not value.is_template() or value.type != "TemplateVM"):
raise QubesException("Only PV template can be a template for the PV VM")
self._template = value
def is_template(self):
return False

View File

@ -29,7 +29,7 @@ import sys
import re
from qubes.qubes import QubesVm,register_qubes_vm_class,xs,xc,dry_run
from qubes.qubes import QubesException
from qubes.qubes import QubesException,QubesVmCollection
from qubes.qubes import system_path,defaults
system_path["config_template_hvm"] = '/usr/share/qubes/vm-template-hvm.conf'
@ -55,7 +55,6 @@ class QubesHVm(QubesVm):
attrs.pop('uses_default_kernel')
attrs.pop('uses_default_kernelopts')
attrs['dir_path']['eval'] = 'value if value is not None else os.path.join(system_path["qubes_appvms_dir"], self.name)'
attrs['volatile_img']['eval'] = 'None'
attrs['config_file_template']['eval'] = 'system_path["config_template_hvm"]'
attrs['drive'] = { 'save': 'str(self.drive)' }
attrs['maxmem'].pop('save')
@ -65,8 +64,6 @@ class QubesHVm(QubesVm):
attrs['_start_guid_first']['eval'] = 'True'
attrs['services']['default'] = "{'meminfo-writer': False}"
# only standalone HVM supported for now
attrs['template']['eval'] = 'None'
attrs['memory']['default'] = defaults["hvm_memory"]
return attrs
@ -90,6 +87,9 @@ class QubesHVm(QubesVm):
if self.guiagent_installed:
self._start_guid_first = False
# The QubesHVM can be a template itself, so collect appvms based on it
self.appvms = QubesVmCollection()
@property
def type(self):
return "HVM"
@ -97,6 +97,20 @@ class QubesHVm(QubesVm):
def is_appvm(self):
return True
def is_template(self):
# Any non-template based HVM can be a template itself
return self.template is None
@property
def template(self):
return self._template
@template.setter
def template(self, value):
if value and (not value.is_template() or value.type != "HVM"):
raise QubesException("Only HVM can be a template for the HVM")
self._template = value
def get_clone_attrs(self):
attrs = super(QubesHVm, self).get_clone_attrs()
attrs.remove('kernel')
@ -123,11 +137,18 @@ class QubesHVm(QubesVm):
self.create_config_file()
# create empty disk
f_root = open(self.root_img, "w")
f_root.truncate(defaults["hvm_disk_size"])
f_root.close()
if self.template is None:
if verbose:
print >> sys.stderr, "--> Creating root image: {0}".\
format(self.root_img)
f_root = open(self.root_img, "w")
f_root.truncate(defaults["hvm_disk_size"])
f_root.close()
# create empty private.img
if verbose:
print >> sys.stderr, "--> Creating private image: {0}".\
format(self.private_img)
f_private = open(self.private_img, "w")
f_private.truncate(defaults["hvm_private_img_size"])
f_private.close()
@ -155,6 +176,14 @@ class QubesHVm(QubesVm):
f_private.truncate (size)
f_private.close ()
def get_rootdev(self, source_template=None):
if self.template:
return "'script:snapshot:{template_root}:{volatile},xvda,w',".format(
template_root=self.template.root_img,
volatile=self.volatile_img)
else:
return "'script:file:{root_img},xvda,w',".format(root_img=self.root_img)
def get_config_params(self, source_template=None):
params = super(QubesHVm, self).get_config_params(source_template=source_template)
@ -229,7 +258,23 @@ class QubesHVm(QubesVm):
return True
def reset_volatile_storage(self, **kwargs):
pass
assert not self.is_running(), "Attempt to clean volatile image of running VM!"
source_template = kwargs.get("source_template", self.template)
if source_template is None:
# Nothing to do on non-template based VM
return
if os.path.exists (self.volatile_img):
os.remove (self.volatile_img)
f_volatile = open (self.volatile_img, "w")
f_root = open (self.template.root_img, "r")
f_root.seek(0, os.SEEK_END)
f_volatile.truncate (f_root.tell()) # make empty sparse file of the same size as root.img
f_volatile.close ()
f_root.close()
@property
def vif(self):
@ -259,6 +304,11 @@ class QubesHVm(QubesVm):
return -1
def start(self, *args, **kwargs):
for vm in self.appvms.values():
if vm.is_running():
raise QubesException("Cannot start HVM template while VMs based on it are running")
if self.template and self.template.is_running():
raise QubesException("Cannot start the HVM while its template is running")
try:
super(QubesHVm, self).start(*args, **kwargs)
except QubesException as e:

View File

@ -625,7 +625,11 @@ class QubesVmCollection(dict):
for (vm_class_name, vm_class) in sorted(QubesVmClasses.items(),
key=lambda _x: _x[1].load_order):
for element in tree.findall(vm_class_name):
vms_of_class = tree.findall(vm_class_name)
# first non-template based, then template based
sorted_vms_of_class = sorted(vms_of_class, key= \
lambda x: str(x.get('template_qid')).lower() != "none")
for element in sorted_vms_of_class:
try:
vm = vm_class(xml_element=element, collection=self)
self[vm.qid] = vm

View File

@ -87,10 +87,6 @@ def main():
exit (1)
label = QubesVmLabels[options.label]
if options.hvm:
# Only standalone HVMs are supported for now
options.standalone = True
if not options.standalone and options.root is not None:
print >> sys.stderr, "root.img can be specified only for standalone VMs"
exit (1)