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:
parent
92b479bf49
commit
2005207462
@ -444,6 +444,19 @@ class QubesVm(object):
|
|||||||
for hook in self.hooks_post_rename:
|
for hook in self.hooks_post_rename:
|
||||||
hook(self, old_name)
|
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):
|
def is_template(self):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
@ -29,7 +29,7 @@ import sys
|
|||||||
import re
|
import re
|
||||||
|
|
||||||
from qubes.qubes import QubesVm,register_qubes_vm_class,xs,xc,dry_run
|
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
|
from qubes.qubes import system_path,defaults
|
||||||
|
|
||||||
system_path["config_template_hvm"] = '/usr/share/qubes/vm-template-hvm.conf'
|
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_kernel')
|
||||||
attrs.pop('uses_default_kernelopts')
|
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['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['config_file_template']['eval'] = 'system_path["config_template_hvm"]'
|
||||||
attrs['drive'] = { 'save': 'str(self.drive)' }
|
attrs['drive'] = { 'save': 'str(self.drive)' }
|
||||||
attrs['maxmem'].pop('save')
|
attrs['maxmem'].pop('save')
|
||||||
@ -65,8 +64,6 @@ class QubesHVm(QubesVm):
|
|||||||
attrs['_start_guid_first']['eval'] = 'True'
|
attrs['_start_guid_first']['eval'] = 'True'
|
||||||
attrs['services']['default'] = "{'meminfo-writer': False}"
|
attrs['services']['default'] = "{'meminfo-writer': False}"
|
||||||
|
|
||||||
# only standalone HVM supported for now
|
|
||||||
attrs['template']['eval'] = 'None'
|
|
||||||
attrs['memory']['default'] = defaults["hvm_memory"]
|
attrs['memory']['default'] = defaults["hvm_memory"]
|
||||||
|
|
||||||
return attrs
|
return attrs
|
||||||
@ -90,6 +87,9 @@ class QubesHVm(QubesVm):
|
|||||||
if self.guiagent_installed:
|
if self.guiagent_installed:
|
||||||
self._start_guid_first = False
|
self._start_guid_first = False
|
||||||
|
|
||||||
|
# The QubesHVM can be a template itself, so collect appvms based on it
|
||||||
|
self.appvms = QubesVmCollection()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def type(self):
|
def type(self):
|
||||||
return "HVM"
|
return "HVM"
|
||||||
@ -97,6 +97,20 @@ class QubesHVm(QubesVm):
|
|||||||
def is_appvm(self):
|
def is_appvm(self):
|
||||||
return True
|
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):
|
def get_clone_attrs(self):
|
||||||
attrs = super(QubesHVm, self).get_clone_attrs()
|
attrs = super(QubesHVm, self).get_clone_attrs()
|
||||||
attrs.remove('kernel')
|
attrs.remove('kernel')
|
||||||
@ -123,11 +137,18 @@ class QubesHVm(QubesVm):
|
|||||||
self.create_config_file()
|
self.create_config_file()
|
||||||
|
|
||||||
# create empty disk
|
# create empty disk
|
||||||
|
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 = open(self.root_img, "w")
|
||||||
f_root.truncate(defaults["hvm_disk_size"])
|
f_root.truncate(defaults["hvm_disk_size"])
|
||||||
f_root.close()
|
f_root.close()
|
||||||
|
|
||||||
# create empty private.img
|
# 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 = open(self.private_img, "w")
|
||||||
f_private.truncate(defaults["hvm_private_img_size"])
|
f_private.truncate(defaults["hvm_private_img_size"])
|
||||||
f_private.close()
|
f_private.close()
|
||||||
@ -155,6 +176,14 @@ class QubesHVm(QubesVm):
|
|||||||
f_private.truncate (size)
|
f_private.truncate (size)
|
||||||
f_private.close ()
|
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):
|
def get_config_params(self, source_template=None):
|
||||||
|
|
||||||
params = super(QubesHVm, self).get_config_params(source_template=source_template)
|
params = super(QubesHVm, self).get_config_params(source_template=source_template)
|
||||||
@ -229,7 +258,23 @@ class QubesHVm(QubesVm):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
def reset_volatile_storage(self, **kwargs):
|
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
|
@property
|
||||||
def vif(self):
|
def vif(self):
|
||||||
@ -259,6 +304,11 @@ class QubesHVm(QubesVm):
|
|||||||
return -1
|
return -1
|
||||||
|
|
||||||
def start(self, *args, **kwargs):
|
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:
|
try:
|
||||||
super(QubesHVm, self).start(*args, **kwargs)
|
super(QubesHVm, self).start(*args, **kwargs)
|
||||||
except QubesException as e:
|
except QubesException as e:
|
||||||
|
@ -625,7 +625,11 @@ class QubesVmCollection(dict):
|
|||||||
|
|
||||||
for (vm_class_name, vm_class) in sorted(QubesVmClasses.items(),
|
for (vm_class_name, vm_class) in sorted(QubesVmClasses.items(),
|
||||||
key=lambda _x: _x[1].load_order):
|
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:
|
try:
|
||||||
vm = vm_class(xml_element=element, collection=self)
|
vm = vm_class(xml_element=element, collection=self)
|
||||||
self[vm.qid] = vm
|
self[vm.qid] = vm
|
||||||
|
@ -87,10 +87,6 @@ def main():
|
|||||||
exit (1)
|
exit (1)
|
||||||
label = QubesVmLabels[options.label]
|
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:
|
if not options.standalone and options.root is not None:
|
||||||
print >> sys.stderr, "root.img can be specified only for standalone VMs"
|
print >> sys.stderr, "root.img can be specified only for standalone VMs"
|
||||||
exit (1)
|
exit (1)
|
||||||
|
Loading…
Reference in New Issue
Block a user