Standalone VM (#98)

'updateable' property is now read-onlyr; updateable=True means that VM has own
root.img, not persistent root-cow.img.
This commit is contained in:
Marek Marczykowski 2011-03-16 18:45:02 -04:00
parent ef6a3e576b
commit 4e68c4cde9
3 changed files with 128 additions and 87 deletions

View File

@ -64,6 +64,7 @@ default_swapcow_img = "swap-cow.img"
default_private_img = "private.img"
default_appvms_conf_file = "appvm-template.conf"
default_netvms_conf_file = "netvm-template.conf"
default_standalonevms_conf_file = "standalone-template.conf"
default_templatevm_conf_template = "templatevm.conf" # needed for TemplateVM cloning
default_appmenus_templates_subdir = "apps.templates"
default_kernels_subdir = "kernels"
@ -177,6 +178,8 @@ class QubesVm(object):
installed_by_rpm = False,
updateable = False,
label = None,
root_img = None,
private_img = None,
memory = default_memory,
vcpus = None):
@ -203,6 +206,21 @@ class QubesVm(object):
# We use it in remove from disk to avoid removing rpm files (for templates)
self.installed_by_rpm = installed_by_rpm
# Setup standard VM storage; some VM types may not use them all
if root_img is not None and os.path.isabs(root_img):
self.root_img = root_img
else:
self.root_img = dir_path + "/" + (
root_img if root_img is not None else default_root_img)
self.rootcow_img = dir_path + "/" + default_rootcow_img
if private_img is not None and os.path.isabs(private_img):
self.private_img = private_img
else:
self.private_img = dir_path + "/" + (
private_img if private_img is not None else default_private_img)
self.updateable = updateable
self.label = label if label is not None else QubesVmLabels["red"]
if self.dir_path is not None:
@ -399,6 +417,18 @@ class QubesVm(object):
p = 100
return p
def get_disk_utilization_root_img(self):
if not os.path.exists(self.root_img):
return 0
return self.get_disk_usage(self.root_img)
def get_root_img_sz(self):
if not os.path.exists(self.root_img):
return 0
return os.path.getsize(self.root_img)
def get_power_state(self):
if dry_run:
return "NA"
@ -623,29 +653,13 @@ class QubesTemplateVm(QubesVm):
if "updateable" not in kwargs or kwargs["updateable"] is None :
kwargs["updateable"] = True
root_img = kwargs.pop("root_img") if "root_img" in kwargs else None
private_img = kwargs.pop("private_img") if "private_img" in kwargs else None
appvms_conf_file = kwargs.pop("appvms_conf_file") if "appvms_conf_file" in kwargs else None
netvms_conf_file = kwargs.pop("netvms_conf_file") if "netvms_conf_file" in kwargs else None
standalonevms_conf_file = kwargs.pop("standalonevms_conf_file") if "standalonevms_conf_file" in kwargs else None
super(QubesTemplateVm, self).__init__(label = default_template_label, **kwargs)
dir_path = kwargs["dir_path"]
if root_img is not None and os.path.isabs(root_img):
self.root_img = root_img
else:
self.root_img = dir_path + "/" + (
root_img if root_img is not None else default_root_img)
self.rootcow_img = dir_path + "/" + default_rootcow_img
if private_img is not None and os.path.isabs(private_img):
self.private_img = private_img
else:
self.private_img = dir_path + "/" + (
private_img if private_img is not None else default_private_img)
if appvms_conf_file is not None and os.path.isabs(appvms_conf_file):
self.appvms_conf_file = appvms_conf_file
else:
@ -658,6 +672,12 @@ class QubesTemplateVm(QubesVm):
self.netvms_conf_file = dir_path + "/" + (
netvms_conf_file if netvms_conf_file is not None else default_netvms_conf_file)
if standalonevms_conf_file is not None and os.path.isabs(standalonevms_conf_file):
self.standalonevms_conf_file = standalonevms_conf_file
else:
self.standalonevms_conf_file = dir_path + "/" + (
standalonevms_conf_file if standalonevms_conf_file is not None else default_standalonevms_conf_file)
self.templatevm_conf_template = self.dir_path + "/" + default_templatevm_conf_template
self.kernels_dir = self.dir_path + "/" + default_kernels_subdir
self.appmenus_templates_dir = self.dir_path + "/" + default_appmenus_templates_subdir
@ -725,6 +745,11 @@ class QubesTemplateVm(QubesVm):
format(src_template_vm.netvms_conf_file, self.netvms_conf_file)
shutil.copy (src_template_vm.netvms_conf_file, self.netvms_conf_file)
if verbose:
print "--> Copying the VM config template :\n{0} ==>\n{1}".\
format(src_template_vm.standalonevms_conf_file, self.standalonevms_conf_file)
shutil.copy (src_template_vm.standalonevms_conf_file, self.standalonevms_conf_file)
if verbose:
print "--> Copying the template's private image:\n{0} ==>\n{1}".\
format(src_template_vm.private_img, self.private_img)
@ -761,15 +786,6 @@ class QubesTemplateVm(QubesVm):
shutil.copytree (src_template_vm.appmenus_templates_dir, self.appmenus_templates_dir)
def get_disk_utilization_root_img(self):
return self.get_disk_usage(self.root_img)
def get_root_img_sz(self):
if not os.path.exists(self.root_img):
return 0
return os.path.getsize(self.root_img)
def verify_files(self):
if dry_run:
return
@ -847,6 +863,7 @@ class QubesTemplateVm(QubesVm):
conf_file=self.conf_file,
appvms_conf_file=self.appvms_conf_file,
netvms_conf_file=self.netvms_conf_file,
standalonevms_conf_file=self.standalonevms_conf_file,
root_img=self.root_img,
rootcow_img=self.rootcow_img,
private_img=self.private_img,
@ -859,7 +876,8 @@ class QubesTemplateVm(QubesVm):
class QubesCowVm(QubesVm):
"""
A class that represent a VM based on some template, i.e. doesn't have own root.img
A class that represent a VM that may be based on some template, i.e. doesn't have own root.img
"""
def __init__(self, **kwargs):
if "dir_path" not in kwargs or kwargs["dir_path"] is None:
@ -868,52 +886,40 @@ class QubesCowVm(QubesVm):
if "updateable" not in kwargs or kwargs["updateable"] is None:
kwargs["updateable"] = False
private_img = kwargs.pop("private_img")
template_vm = kwargs.pop("template_vm")
super(QubesCowVm, self).__init__(**kwargs)
qid = kwargs["qid"]
dir_path = kwargs["dir_path"]
# Dirty hack for QubesDom0NetVm...
if not isinstance(self, QubesDom0NetVm):
assert template_vm is not None, "Missing template_vm for template based VM!"
if not template_vm.is_template():
print "ERROR: template_qid={0} doesn't point to a valid TemplateVM".\
format(template_vm.qid)
return False
if not self.is_updateable():
# Dirty hack for QubesDom0NetVm...
if not isinstance(self, QubesDom0NetVm):
assert template_vm is not None, "Missing template_vm for template based VM!"
if not template_vm.is_template():
print "ERROR: template_qid={0} doesn't point to a valid TemplateVM".\
format(template_vm.qid)
return False
template_vm.appvms[qid] = self
else:
assert self.root_img is not None, "Missing root_img for standalone VM!"
template_vm.appvms[qid] = self
self.template_vm = template_vm
# template based VM doesn't have its own root_img, it uses the one provided by the TemplateVM
if private_img is not None and os.path.isabs(private_img):
self.private_img = private_img
else:
self.private_img = dir_path + "/" + (
private_img if private_img is not None else default_private_img)
self.rootcow_img = dir_path + "/" + default_rootcow_img
self.swapcow_img = dir_path + "/" + default_swapcow_img
def set_updateable(self):
if self.is_updateable():
return
assert not self.is_running()
# Check if the TemaplteVM is *non* updatable...
if not self.template_vm.is_updateable():
self.updateable = True
self.reset_cow_storage()
self.reset_swap_cow_storage()
else:
# Temaplate VM is Updatable itself --> can't make the AppVM updateable too
# as this would cause COW-backed storage incoherency
raise QubesException ("TemaplteVM is updateable: cannot make the template based VM '{0}' updateable".format(self.name))
raise QubesException ("Change 'updateable' flag is not supported. Please use qvm-create.")
def create_config_file(self):
conf_template = None
if self.type == "NetVM":
conf_template = open (self.template_vm.netvms_conf_file, "r")
elif self.updateable:
conf_template = open (self.template_vm.standalonevms_conf_file, "r")
else:
conf_template = open (self.template_vm.appvms_conf_file, "r")
if os.path.isfile(self.conf_file):
@ -962,11 +968,17 @@ class QubesCowVm(QubesVm):
raise IOError ("Error while copying {0} to {1}".\
format(template_priv, self.private_img))
def get_disk_utilization_root_img(self):
return 0
if self.is_updateable():
template_root = self.template_vm.root_img
if verbose:
print "--> Copying the template's root image: {0}".\
format(template_root)
def get_root_img_sz(self):
return 0
# We prefer to use Linux's cp, because it nicely handles sparse files
retcode = subprocess.call (["cp", template_root, self.root_img])
if retcode != 0:
raise IOError ("Error while copying {0} to {1}".\
format(template_root, self.root_img))
def verify_files(self):
if dry_run:
@ -982,6 +994,11 @@ class QubesCowVm(QubesVm):
"VM config file doesn't exist: {0}".\
format(self.conf_file))
if self.is_updateable() and not os.path.exists (self.root_img):
raise QubesException (
"VM root image file doesn't exist: {0}".\
format(self.root_img))
if not os.path.exists (self.private_img):
raise QubesException (
"VM private image file doesn't exist: {0}".\
@ -1136,8 +1153,9 @@ class QubesNetVm(QubesCowVm):
name=self.name,
dir_path=self.dir_path,
conf_file=self.conf_file,
template_qid=str(self.template_vm.qid),
template_qid=str(self.template_vm.qid) if self.template_vm is not None else "none",
updateable=str(self.updateable),
root_img=self.root_img,
private_img=self.private_img,
installed_by_rpm=str(self.installed_by_rpm),
label=self.label.name,
@ -1282,9 +1300,10 @@ class QubesProxyVm(QubesNetVm):
name=self.name,
dir_path=self.dir_path,
conf_file=self.conf_file,
template_qid=str(self.template_vm.qid),
template_qid=str(self.template_vm.qid) if self.template_vm is not None else "none",
updateable=str(self.updateable),
netvm_qid=str(self.netvm_vm.qid) if self.netvm_vm is not None else "none",
root_img=self.root_img,
private_img=self.private_img,
installed_by_rpm=str(self.installed_by_rpm),
label=self.label.name,
@ -1422,10 +1441,6 @@ class QubesAppVm(QubesCowVm):
def type(self):
return "AppVM"
def set_updateable(self):
super(QubesAppVm, self).set_updateable()
def create_on_disk(self, verbose):
if dry_run:
return
@ -1529,9 +1544,10 @@ class QubesAppVm(QubesCowVm):
name=self.name,
dir_path=self.dir_path,
conf_file=self.conf_file,
template_qid=str(self.template_vm.qid),
template_qid=str(self.template_vm.qid) if self.template_vm is not None else "none",
uses_default_netvm=str(self.uses_default_netvm),
netvm_qid=str(self.netvm_vm.qid) if self.netvm_vm is not None else "none",
root_img=self.root_img,
private_img=self.private_img,
installed_by_rpm=str(self.installed_by_rpm),
updateable=str(self.updateable),
@ -1585,6 +1601,7 @@ class QubesVmCollection(dict):
def add_new_appvm(self, name, template_vm,
dir_path = None, conf_file = None,
private_img = None,
updateable = False,
label = None):
qid = self.get_new_unused_qid()
@ -1592,6 +1609,7 @@ class QubesVmCollection(dict):
dir_path=dir_path, conf_file=conf_file,
private_img=private_img,
netvm_vm = self.get_default_netvm_vm(),
updateable=updateable,
label=label)
if not self.verify_new_vm (vm):
@ -1645,13 +1663,14 @@ class QubesVmCollection(dict):
def add_new_netvm(self, name, template_vm,
dir_path = None, conf_file = None,
private_img = None, installed_by_rpm = False,
label = None):
label = None, updateable = False):
qid = self.get_new_unused_qid()
netid = self.get_new_unused_netid()
vm = QubesNetVm (qid=qid, name=name, template_vm=template_vm,
netid=netid, label=label,
private_img=private_img, installed_by_rpm=installed_by_rpm,
updateable=updateable,
dir_path=dir_path, conf_file=conf_file)
if not self.verify_new_vm (vm):
@ -1666,7 +1685,7 @@ class QubesVmCollection(dict):
def add_new_proxyvm(self, name, template_vm,
dir_path = None, conf_file = None,
private_img = None, installed_by_rpm = False,
label = None):
label = None, updateable = False):
qid = self.get_new_unused_qid()
netid = self.get_new_unused_netid()
@ -1674,6 +1693,7 @@ class QubesVmCollection(dict):
netid=netid, label=label,
private_img=private_img, installed_by_rpm=installed_by_rpm,
dir_path=dir_path, conf_file=conf_file,
updateable=updateable,
netvm_vm = self.get_default_fw_netvm_vm())
if not self.verify_new_vm (vm):
@ -1863,7 +1883,7 @@ class QubesVmCollection(dict):
kwargs = {}
attr_list = ("qid", "name", "dir_path", "conf_file",
"appvms_conf_file", "appvms_conf_file",
"appvms_conf_file", "netvms_conf_file", "standalonevms_conf_file",
"private_img", "root_img",
"installed_by_rpm", "updateable",
"uses_default_netvm")
@ -1891,20 +1911,25 @@ class QubesVmCollection(dict):
kwargs = {}
attr_list = ("qid", "netid", "name", "dir_path", "conf_file",
"private_img", "template_qid", "updateable", "label",
"root_img",
)
for attribute in attr_list:
kwargs[attribute] = element.get(attribute)
kwargs["qid"] = int(kwargs["qid"])
kwargs["template_qid"] = int(kwargs["template_qid"])
if kwargs["updateable"] is not None:
kwargs["updateable"] = True if kwargs["updateable"] == "True" else False
template_vm = self[kwargs.pop("template_qid")]
if template_vm is None:
print "ERROR: NetVM '{0}' uses unkown template qid='{1}'!".\
format(kwargs["name"], kwargs["template_qid"])
template_vm = None
if kwargs["updateable"] == False:
kwargs["template_qid"] = int(kwargs["template_qid"])
template_vm = self[kwargs.pop("template_qid")]
if template_vm is None:
print "ERROR: NetVM '{0}' uses unkown template qid='{1}'!".\
format(kwargs["name"], kwargs["template_qid"])
else:
kwargs.pop("template_qid")
kwargs["template_vm"] = template_vm
kwargs["netid"] = int(kwargs["netid"])
@ -1936,14 +1961,18 @@ class QubesVmCollection(dict):
kwargs[attribute] = element.get(attribute)
kwargs["qid"] = int(kwargs["qid"])
kwargs["template_qid"] = int(kwargs["template_qid"])
if kwargs["updateable"] is not None:
kwargs["updateable"] = True if kwargs["updateable"] == "True" else False
template_vm = self[kwargs.pop("template_qid")]
if template_vm is None:
print "ERROR: ProxyVM '{0}' uses unkown template qid='{1}'!".\
format(kwargs["name"], kwargs["template_qid"])
template_vm = None
if kwargs["updateable"] == False:
kwargs["template_qid"] = int(kwargs["template_qid"])
template_vm = self[kwargs.pop("template_qid")]
if template_vm is None:
print "ERROR: ProxyVM '{0}' uses unkown template qid='{1}'!".\
format(kwargs["name"], kwargs["template_qid"])
else:
kwargs.pop("template_qid")
kwargs["template_vm"] = template_vm
kwargs["netid"] = int(kwargs["netid"])
@ -2042,14 +2071,18 @@ class QubesVmCollection(dict):
kwargs[attribute] = element.get(attribute)
kwargs["qid"] = int(kwargs["qid"])
kwargs["template_qid"] = int(kwargs["template_qid"])
if kwargs["updateable"] is not None:
kwargs["updateable"] = True if kwargs["updateable"] == "True" else False
template_vm = self[kwargs.pop("template_qid")]
if template_vm is None:
print "ERROR: AppVM '{0}' uses unkown template qid='{1}'!".\
format(kwargs["name"], kwargs["template_qid"])
template_vm = None
if kwargs["updateable"] == False:
kwargs["template_qid"] = int(kwargs["template_qid"])
template_vm = self[kwargs.pop("template_qid")]
if template_vm is None:
print "ERROR: AppVM '{0}' uses unkown template qid='{1}'!".\
format(kwargs["name"], kwargs["template_qid"])
else:
kwargs.pop("template_qid")
kwargs["template_vm"] = template_vm

View File

@ -57,6 +57,8 @@ def main():
help="Create ProxyVM")
parser.add_option ("-n", "--net", action="store_true", dest="netvm", default=False,
help="Create NetVM")
parser.add_option ("-s", "--standalone", action="store_true", dest="standalone", default=False,
help="Create standalone VM - independent of template ")
parser.add_option ("-m", "--mem", dest="mem", default=None,
help="Initial memory size (in MB)")
parser.add_option ("-c", "--vcpus", dest="vcpus", default=None,
@ -115,7 +117,7 @@ def main():
vm = None
if options.netvm:
vm = qvm_collection.add_new_netvm(vmname, template_vm, label = label)
vm = qvm_collection.add_new_netvm(vmname, template_vm, label = label, updateable = options.standalone)
net_devices = find_net_devices()
print "Found the following net devices in your system:"
@ -128,9 +130,9 @@ def main():
vm.pcidevs = dev_str
elif options.proxyvm:
vm = qvm_collection.add_new_proxyvm(vmname, template_vm, label = label)
vm = qvm_collection.add_new_proxyvm(vmname, template_vm, label = label, updateable = options.standalone)
else:
vm = qvm_collection.add_new_appvm(vmname, template_vm, label = label)
vm = qvm_collection.add_new_appvm(vmname, template_vm, label = label, updateable = options.standalone)
if options.mem is not None:
vm.memory = options.mem

View File

@ -141,12 +141,18 @@ def main():
if netvm.is_netvm():
vms_to_display.append (netvm)
# Now, the AppVMs without template...
for appvm in vms_list:
if appvm.is_appvm() and appvm.template_vm is None:
vms_to_display.append (appvm)
# Now, the template, and all its AppVMs...
for tvm in vms_list:
if tvm.is_template():
vms_to_display.append (tvm)
for vm in vms_list:
if (vm.is_appvm() or vm.is_disposablevm()) and vm.template_vm.qid == tvm.qid:
if (vm.is_appvm() or vm.is_disposablevm()) and \
vm.template_vm and vm.template_vm.qid == tvm.qid:
vms_to_display.append(vm)
assert len(vms_to_display) == no_vms