NetVM and ProxyVM based on template: part 1 (core)
This commit is contained in:
parent
13c3a04755
commit
c1bd86142c
@ -71,7 +71,7 @@ default_firewall_conf_file = "firewall.xml"
|
|||||||
# do not allow to start a new AppVM if Dom0 mem was to be less than this
|
# do not allow to start a new AppVM if Dom0 mem was to be less than this
|
||||||
dom0_min_memory = 700*1024*1024
|
dom0_min_memory = 700*1024*1024
|
||||||
|
|
||||||
# We need this global reference, as each instance of QubesVM
|
# We need this global reference, as each instance of QubesVm
|
||||||
# must be able to ask Dom0 VM about how much memory it currently has...
|
# must be able to ask Dom0 VM about how much memory it currently has...
|
||||||
dom0_vm = None
|
dom0_vm = None
|
||||||
|
|
||||||
@ -270,8 +270,8 @@ class QubesVm(object):
|
|||||||
def is_netvm(self):
|
def is_netvm(self):
|
||||||
return isinstance(self, QubesNetVm)
|
return isinstance(self, QubesNetVm)
|
||||||
|
|
||||||
def is_fwvm(self):
|
def is_proxyvm(self):
|
||||||
return isinstance(self, QubesFirewallVm)
|
return isinstance(self, QubesProxyVm)
|
||||||
|
|
||||||
def is_disposablevm(self):
|
def is_disposablevm(self):
|
||||||
return isinstance(self, QubesDisposableVm)
|
return isinstance(self, QubesDisposableVm)
|
||||||
@ -529,7 +529,7 @@ class QubesVm(object):
|
|||||||
print "--> Setting Xen Store info for the VM..."
|
print "--> Setting Xen Store info for the VM..."
|
||||||
self.create_xenstore_entries(xid)
|
self.create_xenstore_entries(xid)
|
||||||
|
|
||||||
if (not self.is_netvm() or self.is_fwvm()) and self.netvm_vm is not None:
|
if self.netvm_vm is not None:
|
||||||
assert self.netvm_vm is not None
|
assert self.netvm_vm is not None
|
||||||
if verbose:
|
if verbose:
|
||||||
print "--> Attaching to the network backend (netvm={0})...".format(self.netvm_vm.name)
|
print "--> Attaching to the network backend (netvm={0})...".format(self.netvm_vm.name)
|
||||||
@ -555,7 +555,7 @@ class QubesVm(object):
|
|||||||
#if verbose:
|
#if verbose:
|
||||||
# print "--> Updating FirewallVMs rules..."
|
# print "--> Updating FirewallVMs rules..."
|
||||||
#for vm in qvm_collection.values():
|
#for vm in qvm_collection.values():
|
||||||
# if vm.is_fwvm():
|
# if vm.is_proxyvm():
|
||||||
# vm.write_iptables_xenstore_entry()
|
# vm.write_iptables_xenstore_entry()
|
||||||
|
|
||||||
if verbose:
|
if verbose:
|
||||||
@ -591,7 +591,7 @@ class QubesVm(object):
|
|||||||
|
|
||||||
class QubesTemplateVm(QubesVm):
|
class QubesTemplateVm(QubesVm):
|
||||||
"""
|
"""
|
||||||
A class that represents an TemplateVM. A child of QubesVM.
|
A class that represents an TemplateVM. A child of QubesVm.
|
||||||
"""
|
"""
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
|
|
||||||
@ -818,59 +818,110 @@ class QubesTemplateVm(QubesVm):
|
|||||||
)
|
)
|
||||||
return element
|
return element
|
||||||
|
|
||||||
class QubesServiceVm(QubesVm):
|
class QubesCowVm(QubesVm):
|
||||||
"""
|
"""
|
||||||
A class that represents a ServiceVM, e.g. NetVM. A child of QubesVM.
|
A class that represent a VM based on some template, i.e. doesn't have own root.img
|
||||||
"""
|
"""
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
|
|
||||||
if "dir_path" not in kwargs or kwargs["dir_path"] is None:
|
if "dir_path" not in kwargs or kwargs["dir_path"] is None:
|
||||||
kwargs["dir_path"] = qubes_servicevms_dir + "/" + kwargs["name"]
|
kwargs["dir_path"] = qubes_appvms_dir + "/" + kwargs["name"]
|
||||||
|
|
||||||
root_img = kwargs.pop("root_img") if "root_img" in kwargs else None
|
if "updateable" not in kwargs or kwargs["updateable"] is None:
|
||||||
private_img = kwargs.pop("private_img") if "private_img" in kwargs else None
|
kwargs["updateable"] = False
|
||||||
|
|
||||||
kwargs["updateable"] = True
|
private_img = kwargs.pop("private_img")
|
||||||
super(QubesServiceVm, self).__init__(**kwargs)
|
template_vm = kwargs.pop("template_vm")
|
||||||
|
|
||||||
|
|
||||||
|
super(QubesCowVm, self).__init__(**kwargs)
|
||||||
|
qid = kwargs["qid"]
|
||||||
dir_path = kwargs["dir_path"]
|
dir_path = kwargs["dir_path"]
|
||||||
assert dir_path is not None
|
# 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 TempleteVM".\
|
||||||
|
format(template_vm.qid)
|
||||||
|
return False
|
||||||
|
|
||||||
if root_img is not None and os.path.isabs(root_img):
|
template_vm.appvms[qid] = self
|
||||||
self.root_img = root_img
|
self.template_vm = template_vm
|
||||||
else:
|
|
||||||
self.root_img = dir_path + "/" + (
|
|
||||||
root_img if root_img is not None else default_root_img)
|
|
||||||
|
|
||||||
|
# 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):
|
if private_img is not None and os.path.isabs(private_img):
|
||||||
self.private_img = private_img
|
self.private_img = private_img
|
||||||
else:
|
else:
|
||||||
self.private_img = dir_path + "/" + (
|
self.private_img = dir_path + "/" + (
|
||||||
private_img if private_img is not None else default_private_img)
|
private_img if private_img is not None else default_private_img)
|
||||||
|
|
||||||
|
self.rootcow_img = dir_path + "/" + default_rootcow_img
|
||||||
|
|
||||||
def set_updateable(self):
|
def set_updateable(self):
|
||||||
if self.is_updateable():
|
if self.is_updateable():
|
||||||
return
|
return
|
||||||
# ServiceVMs are standalone, we can always make it updateable
|
|
||||||
# In fact there is no point in making it unpdatebale...
|
|
||||||
# So, this is just for completncess
|
|
||||||
assert not self.is_running()
|
assert not self.is_running()
|
||||||
self.updateable = True
|
# Check if the TemaplteVM is *non* updatable...
|
||||||
|
if not self.template_vm.is_updateable():
|
||||||
|
self.updateable = True
|
||||||
|
self.reset_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))
|
||||||
|
|
||||||
|
def create_config_file(self):
|
||||||
|
conf_template = open (self.template_vm.appvms_conf_file, "r")
|
||||||
|
if os.path.isfile(self.conf_file):
|
||||||
|
shutil.copy(self.conf_file, self.conf_file + ".backup")
|
||||||
|
conf_appvm = open(self.conf_file, "w")
|
||||||
|
rx_vmname = re.compile (r"%VMNAME%")
|
||||||
|
rx_vmdir = re.compile (r"%VMDIR%")
|
||||||
|
rx_template = re.compile (r"%TEMPLATEDIR%")
|
||||||
|
|
||||||
|
for line in conf_template:
|
||||||
|
line = rx_vmname.sub (self.name, line)
|
||||||
|
line = rx_vmdir.sub (self.dir_path, line)
|
||||||
|
line = rx_template.sub (self.template_vm.dir_path, line)
|
||||||
|
conf_appvm.write(line)
|
||||||
|
|
||||||
|
conf_template.close()
|
||||||
|
conf_appvm.close()
|
||||||
|
|
||||||
|
def create_on_disk(self, verbose):
|
||||||
|
if dry_run:
|
||||||
|
return
|
||||||
|
|
||||||
|
if verbose:
|
||||||
|
print "--> Creating directory: {0}".format(self.dir_path)
|
||||||
|
os.mkdir (self.dir_path)
|
||||||
|
|
||||||
|
if verbose:
|
||||||
|
print "--> Creating the VM config file: {0}".format(self.conf_file)
|
||||||
|
|
||||||
|
self.create_config_file()
|
||||||
|
|
||||||
|
template_priv = self.template_vm.private_img
|
||||||
|
if verbose:
|
||||||
|
print "--> Copying the template's private image: {0}".\
|
||||||
|
format(template_priv)
|
||||||
|
|
||||||
|
# We prefer to use Linux's cp, because it nicely handles sparse files
|
||||||
|
retcode = subprocess.call (["cp", template_priv, self.private_img])
|
||||||
|
if retcode != 0:
|
||||||
|
raise IOError ("Error while copying {0} to {1}".\
|
||||||
|
format(template_priv, self.private_img))
|
||||||
|
|
||||||
def get_disk_utilization_root_img(self):
|
def get_disk_utilization_root_img(self):
|
||||||
return self.get_disk_usage(self.root_img)
|
return 0
|
||||||
|
|
||||||
def get_root_img_sz(self):
|
def get_root_img_sz(self):
|
||||||
if not os.path.exists(self.root_img):
|
return 0
|
||||||
return 0
|
|
||||||
|
|
||||||
return os.path.getsize(self.root_img)
|
|
||||||
|
|
||||||
def verify_files(self):
|
def verify_files(self):
|
||||||
if dry_run:
|
if dry_run:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
if not os.path.exists (self.dir_path):
|
if not os.path.exists (self.dir_path):
|
||||||
raise QubesException (
|
raise QubesException (
|
||||||
"VM directory doesn't exist: {0}".\
|
"VM directory doesn't exist: {0}".\
|
||||||
@ -881,19 +932,53 @@ class QubesServiceVm(QubesVm):
|
|||||||
"VM config file doesn't exist: {0}".\
|
"VM config file doesn't exist: {0}".\
|
||||||
format(self.conf_file))
|
format(self.conf_file))
|
||||||
|
|
||||||
if not os.path.exists (self.root_img):
|
if not os.path.exists (self.private_img):
|
||||||
raise QubesException (
|
raise QubesException (
|
||||||
"VM root image file doesn't exist: {0}".\
|
"VM private image file doesn't exist: {0}".\
|
||||||
format(self.root_img))
|
format(self.private_img))
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def create_xml_element(self):
|
def start(self, debug_console = False, verbose = False, preparing_dvm = False):
|
||||||
raise NotImplementedError
|
if dry_run:
|
||||||
|
return
|
||||||
|
|
||||||
class QubesNetVm(QubesServiceVm):
|
if self.is_running():
|
||||||
|
raise QubesException("VM is already running!")
|
||||||
|
|
||||||
|
if not self.is_updateable():
|
||||||
|
self.reset_cow_storage()
|
||||||
|
|
||||||
|
return super(QubesCowVm, self).start(debug_console=debug_console, verbose=verbose, preparing_dvm=preparing_dvm)
|
||||||
|
|
||||||
|
def reset_cow_storage (self):
|
||||||
|
|
||||||
|
print "--> Resetting the COW storage: {0}...".format (self.rootcow_img)
|
||||||
|
|
||||||
|
if dry_run:
|
||||||
|
return
|
||||||
|
# this is probbaly not needed, as open (..., "w") should remove the previous file
|
||||||
|
if os.path.exists (self.rootcow_img):
|
||||||
|
os.remove (self.rootcow_img)
|
||||||
|
|
||||||
|
|
||||||
|
f_cow = open (self.rootcow_img, "w")
|
||||||
|
f_root = open (self.template_vm.root_img, "r")
|
||||||
|
f_root.seek(0, os.SEEK_END)
|
||||||
|
f_cow.truncate (f_root.tell()) # make empty sparse file of the same size as root.img
|
||||||
|
f_cow.close ()
|
||||||
|
f_root.close()
|
||||||
|
|
||||||
|
def remove_from_disk(self):
|
||||||
|
if dry_run:
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
subprocess.check_call ([qubes_appmenu_remove_cmd, self.name])
|
||||||
|
shutil.rmtree (self.dir_path)
|
||||||
|
|
||||||
|
class QubesNetVm(QubesCowVm):
|
||||||
"""
|
"""
|
||||||
A class that represents a NetVM. A child of QubesServiceVM.
|
A class that represents a NetVM. A child of QubesCowVM.
|
||||||
"""
|
"""
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
netid = kwargs.pop("netid")
|
netid = kwargs.pop("netid")
|
||||||
@ -904,6 +989,9 @@ class QubesNetVm(QubesServiceVm):
|
|||||||
self.__gateway = self.netprefix + "0.1"
|
self.__gateway = self.netprefix + "0.1"
|
||||||
self.__secondary_dns = self.netprefix + "255.254"
|
self.__secondary_dns = self.netprefix + "255.254"
|
||||||
|
|
||||||
|
if "dir_path" not in kwargs or kwargs["dir_path"] is None:
|
||||||
|
kwargs["dir_path"] = qubes_servicevms_dir + "/" + kwargs["name"]
|
||||||
|
|
||||||
if "label" not in kwargs or kwargs["label"] is None:
|
if "label" not in kwargs or kwargs["label"] is None:
|
||||||
kwargs["label"] = default_servicevm_label
|
kwargs["label"] = default_servicevm_label
|
||||||
super(QubesNetVm, self).__init__(installed_by_rpm=True, **kwargs)
|
super(QubesNetVm, self).__init__(installed_by_rpm=True, **kwargs)
|
||||||
@ -942,28 +1030,29 @@ class QubesNetVm(QubesServiceVm):
|
|||||||
name=self.name,
|
name=self.name,
|
||||||
dir_path=self.dir_path,
|
dir_path=self.dir_path,
|
||||||
conf_file=self.conf_file,
|
conf_file=self.conf_file,
|
||||||
root_img=self.root_img,
|
template_qid=str(self.template_vm.qid),
|
||||||
|
updateable=str(self.updateable),
|
||||||
private_img=self.private_img,
|
private_img=self.private_img,
|
||||||
installed_by_rpm=str(self.installed_by_rpm),
|
installed_by_rpm=str(self.installed_by_rpm),
|
||||||
)
|
)
|
||||||
return element
|
return element
|
||||||
|
|
||||||
class QubesFirewallVm(QubesNetVm):
|
class QubesProxyVm(QubesNetVm):
|
||||||
"""
|
"""
|
||||||
A class that represents a FirewallVM. A child of QubesNetVM.
|
A class that represents a ProxyVM, ex FirewallVM. A child of QubesNetVM.
|
||||||
"""
|
"""
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
super(QubesFirewallVm, self).__init__(uses_default_netvm=False, **kwargs)
|
super(QubesProxyVm, self).__init__(uses_default_netvm=False, **kwargs)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def type(self):
|
def type(self):
|
||||||
return "FirewallVM"
|
return "ProxyVM"
|
||||||
|
|
||||||
def create_xenstore_entries(self, xid):
|
def create_xenstore_entries(self, xid):
|
||||||
if dry_run:
|
if dry_run:
|
||||||
return
|
return
|
||||||
|
|
||||||
super(QubesFirewallVm, self).create_xenstore_entries(xid)
|
super(QubesProxyVm, self).create_xenstore_entries(xid)
|
||||||
self.write_iptables_xenstore_entry()
|
self.write_iptables_xenstore_entry()
|
||||||
|
|
||||||
def write_iptables_xenstore_entry(self):
|
def write_iptables_xenstore_entry(self):
|
||||||
@ -1052,13 +1141,14 @@ class QubesFirewallVm(QubesNetVm):
|
|||||||
|
|
||||||
def create_xml_element(self):
|
def create_xml_element(self):
|
||||||
element = xml.etree.ElementTree.Element(
|
element = xml.etree.ElementTree.Element(
|
||||||
"QubesFirewallVm",
|
"QubesProxyVm",
|
||||||
qid=str(self.qid),
|
qid=str(self.qid),
|
||||||
netid=str(self.netid),
|
netid=str(self.netid),
|
||||||
name=self.name,
|
name=self.name,
|
||||||
dir_path=self.dir_path,
|
dir_path=self.dir_path,
|
||||||
conf_file=self.conf_file,
|
conf_file=self.conf_file,
|
||||||
root_img=self.root_img,
|
template_qid=str(self.template_vm.qid),
|
||||||
|
updateable=str(self.updateable),
|
||||||
netvm_qid=str(self.netvm_vm.qid) if self.netvm_vm is not None else "none",
|
netvm_qid=str(self.netvm_vm.qid) if self.netvm_vm is not None else "none",
|
||||||
private_img=self.private_img,
|
private_img=self.private_img,
|
||||||
installed_by_rpm=str(self.installed_by_rpm),
|
installed_by_rpm=str(self.installed_by_rpm),
|
||||||
@ -1068,8 +1158,9 @@ class QubesFirewallVm(QubesNetVm):
|
|||||||
class QubesDom0NetVm(QubesNetVm):
|
class QubesDom0NetVm(QubesNetVm):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super(QubesDom0NetVm, self).__init__(qid=0, name="dom0", netid=0,
|
super(QubesDom0NetVm, self).__init__(qid=0, name="dom0", netid=0,
|
||||||
dir_path=None, root_img = None,
|
dir_path=None,
|
||||||
private_img = None,
|
private_img = None,
|
||||||
|
template_vm = None,
|
||||||
label = default_template_label)
|
label = default_template_label)
|
||||||
if not dry_run and xend_session.session is not None:
|
if not dry_run and xend_session.session is not None:
|
||||||
self.session_hosts = xend_session.session.xenapi.host.get_all()
|
self.session_hosts = xend_session.session.xenapi.host.get_all()
|
||||||
@ -1140,7 +1231,7 @@ class QubesDom0NetVm(QubesNetVm):
|
|||||||
|
|
||||||
class QubesDisposableVm(QubesVm):
|
class QubesDisposableVm(QubesVm):
|
||||||
"""
|
"""
|
||||||
A class that represents an DisposableVM. A child of QubesVM.
|
A class that represents an DisposableVM. A child of QubesVm.
|
||||||
"""
|
"""
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
|
|
||||||
@ -1175,43 +1266,15 @@ class QubesDisposableVm(QubesVm):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
class QubesAppVm(QubesVm):
|
class QubesAppVm(QubesCowVm):
|
||||||
"""
|
"""
|
||||||
A class that represents an AppVM. A child of QubesVM.
|
A class that represents an AppVM. A child of QubesVm.
|
||||||
"""
|
"""
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
|
|
||||||
if "dir_path" not in kwargs or kwargs["dir_path"] is None:
|
|
||||||
kwargs["dir_path"] = qubes_appvms_dir + "/" + kwargs["name"]
|
|
||||||
|
|
||||||
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(QubesAppVm, self).__init__(**kwargs)
|
super(QubesAppVm, self).__init__(**kwargs)
|
||||||
qid = kwargs["qid"]
|
|
||||||
dir_path = kwargs["dir_path"]
|
dir_path = kwargs["dir_path"]
|
||||||
|
|
||||||
assert template_vm is not None, "Missing template_vm for AppVM!"
|
|
||||||
if not template_vm.is_template():
|
|
||||||
print "ERROR: template_qid={0} doesn't point to a valid TempleteVM".\
|
|
||||||
format(new_vm.template_vm.qid)
|
|
||||||
return False
|
|
||||||
|
|
||||||
self.template_vm = template_vm
|
|
||||||
template_vm.appvms[qid] = self
|
|
||||||
|
|
||||||
# AppVM 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
|
self.swapcow_img = dir_path + "/" + default_swapcow_img
|
||||||
|
|
||||||
if "firewall_conf" not in kwargs or kwargs["firewall_conf"] is None:
|
if "firewall_conf" not in kwargs or kwargs["firewall_conf"] is None:
|
||||||
@ -1225,62 +1288,15 @@ class QubesAppVm(QubesVm):
|
|||||||
return "AppVM"
|
return "AppVM"
|
||||||
|
|
||||||
def set_updateable(self):
|
def set_updateable(self):
|
||||||
if self.is_updateable():
|
|
||||||
return
|
|
||||||
|
|
||||||
assert not self.is_running()
|
super(QubesAppVm, self).set_updateable()
|
||||||
# Check if the TemaplteVM is *non* updatable...
|
self.reset_swap_cow_storage()
|
||||||
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 AppVM '{0}' updateable".format(self.name))
|
|
||||||
|
|
||||||
def create_config_file(self):
|
|
||||||
conf_template = open (self.template_vm.appvms_conf_file, "r")
|
|
||||||
if os.path.isfile(self.conf_file):
|
|
||||||
shutil.copy(self.conf_file, self.conf_file + ".backup")
|
|
||||||
conf_appvm = open(self.conf_file, "w")
|
|
||||||
rx_vmname = re.compile (r"%VMNAME%")
|
|
||||||
rx_vmdir = re.compile (r"%VMDIR%")
|
|
||||||
rx_template = re.compile (r"%TEMPLATEDIR%")
|
|
||||||
|
|
||||||
for line in conf_template:
|
|
||||||
line = rx_vmname.sub (self.name, line)
|
|
||||||
line = rx_vmdir.sub (self.dir_path, line)
|
|
||||||
line = rx_template.sub (self.template_vm.dir_path, line)
|
|
||||||
conf_appvm.write(line)
|
|
||||||
|
|
||||||
conf_template.close()
|
|
||||||
conf_appvm.close()
|
|
||||||
|
|
||||||
def create_on_disk(self, verbose):
|
def create_on_disk(self, verbose):
|
||||||
if dry_run:
|
if dry_run:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
super(QubesAppVm, self).create_on_disk(verbose)
|
||||||
if verbose:
|
|
||||||
print "--> Creating directory: {0}".format(self.dir_path)
|
|
||||||
os.mkdir (self.dir_path)
|
|
||||||
|
|
||||||
if verbose:
|
|
||||||
print "--> Creating the VM config file: {0}".format(self.conf_file)
|
|
||||||
|
|
||||||
self.create_config_file()
|
|
||||||
|
|
||||||
template_priv = self.template_vm.private_img
|
|
||||||
if verbose:
|
|
||||||
print "--> Copying the template's private image: {0}".\
|
|
||||||
format(template_priv)
|
|
||||||
|
|
||||||
# We prefer to use Linux's cp, because it nicely handles sparse files
|
|
||||||
retcode = subprocess.call (["cp", template_priv, self.private_img])
|
|
||||||
if retcode != 0:
|
|
||||||
raise IOError ("Error while copying {0} to {1}".\
|
|
||||||
format(template_priv, self.private_img))
|
|
||||||
|
|
||||||
if verbose:
|
if verbose:
|
||||||
print "--> Creating icon symlink: {0} -> {1}".format(self.icon_path, self.label.icon_path)
|
print "--> Creating icon symlink: {0} -> {1}".format(self.icon_path, self.label.icon_path)
|
||||||
@ -1363,35 +1379,6 @@ class QubesAppVm(QubesVm):
|
|||||||
|
|
||||||
return conf
|
return conf
|
||||||
|
|
||||||
def get_disk_utilization_root_img(self):
|
|
||||||
return 0
|
|
||||||
|
|
||||||
def get_root_img_sz(self):
|
|
||||||
return 0
|
|
||||||
|
|
||||||
|
|
||||||
def verify_files(self):
|
|
||||||
if dry_run:
|
|
||||||
return
|
|
||||||
|
|
||||||
|
|
||||||
if not os.path.exists (self.dir_path):
|
|
||||||
raise QubesException (
|
|
||||||
"VM directory doesn't exist: {0}".\
|
|
||||||
format(self.dir_path))
|
|
||||||
|
|
||||||
if not os.path.exists (self.conf_file):
|
|
||||||
raise QubesException (
|
|
||||||
"VM config file doesn't exist: {0}".\
|
|
||||||
format(self.conf_file))
|
|
||||||
|
|
||||||
if not os.path.exists (self.private_img):
|
|
||||||
raise QubesException (
|
|
||||||
"VM private image file doesn't exist: {0}".\
|
|
||||||
format(self.private_img))
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
def create_xml_element(self):
|
def create_xml_element(self):
|
||||||
element = xml.etree.ElementTree.Element(
|
element = xml.etree.ElementTree.Element(
|
||||||
"QubesAppVm",
|
"QubesAppVm",
|
||||||
@ -1412,33 +1399,10 @@ class QubesAppVm(QubesVm):
|
|||||||
if dry_run:
|
if dry_run:
|
||||||
return
|
return
|
||||||
|
|
||||||
if self.is_running():
|
|
||||||
raise QubesException("VM is already running!")
|
|
||||||
|
|
||||||
if not self.is_updateable():
|
|
||||||
self.reset_cow_storage()
|
|
||||||
self.reset_swap_cow_storage()
|
self.reset_swap_cow_storage()
|
||||||
|
|
||||||
return super(QubesAppVm, self).start(debug_console=debug_console, verbose=verbose, preparing_dvm=preparing_dvm)
|
return super(QubesAppVm, self).start(debug_console=debug_console, verbose=verbose, preparing_dvm=preparing_dvm)
|
||||||
|
|
||||||
def reset_cow_storage (self):
|
|
||||||
|
|
||||||
print "--> Resetting the COW storage: {0}...".format (self.rootcow_img)
|
|
||||||
|
|
||||||
if dry_run:
|
|
||||||
return
|
|
||||||
# this is probbaly not needed, as open (..., "w") should remove the previous file
|
|
||||||
if os.path.exists (self.rootcow_img):
|
|
||||||
os.remove (self.rootcow_img)
|
|
||||||
|
|
||||||
|
|
||||||
f_cow = open (self.rootcow_img, "w")
|
|
||||||
f_root = open (self.template_vm.root_img, "r")
|
|
||||||
f_root.seek(0, os.SEEK_END)
|
|
||||||
f_cow.truncate (f_root.tell()) # make empty sparse file of the same size as root.img
|
|
||||||
f_cow.close ()
|
|
||||||
f_root.close()
|
|
||||||
|
|
||||||
def reset_swap_cow_storage (self):
|
def reset_swap_cow_storage (self):
|
||||||
print "--> Resetting the swap COW storage: {0}...".format (self.swapcow_img)
|
print "--> Resetting the swap COW storage: {0}...".format (self.swapcow_img)
|
||||||
if os.path.exists (self.swapcow_img):
|
if os.path.exists (self.swapcow_img):
|
||||||
@ -1448,16 +1412,6 @@ class QubesAppVm(QubesVm):
|
|||||||
f_swap_cow.truncate (swap_cow_sz)
|
f_swap_cow.truncate (swap_cow_sz)
|
||||||
f_swap_cow.close()
|
f_swap_cow.close()
|
||||||
|
|
||||||
def remove_from_disk(self):
|
|
||||||
if dry_run:
|
|
||||||
return
|
|
||||||
|
|
||||||
|
|
||||||
subprocess.check_call ([qubes_appmenu_remove_cmd, self.name])
|
|
||||||
shutil.rmtree (self.dir_path)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class QubesVmCollection(dict):
|
class QubesVmCollection(dict):
|
||||||
"""
|
"""
|
||||||
A collection of Qubes VMs indexed by Qubes id (qid)
|
A collection of Qubes VMs indexed by Qubes id (qid)
|
||||||
@ -1551,13 +1505,13 @@ class QubesVmCollection(dict):
|
|||||||
return vm
|
return vm
|
||||||
|
|
||||||
|
|
||||||
def add_new_netvm(self, name,
|
def add_new_netvm(self, name, template_vm,
|
||||||
dir_path = None, conf_file = None,
|
dir_path = None, conf_file = None,
|
||||||
root_img = None):
|
root_img = None):
|
||||||
|
|
||||||
qid = self.get_new_unused_qid()
|
qid = self.get_new_unused_qid()
|
||||||
netid = self.get_new_unused_netid()
|
netid = self.get_new_unused_netid()
|
||||||
vm = QubesNetVm (qid=qid, name=name,
|
vm = QubesNetVm (qid=qid, name=name, template_vm=template_vm,
|
||||||
netid=netid,
|
netid=netid,
|
||||||
dir_path=dir_path, conf_file=conf_file,
|
dir_path=dir_path, conf_file=conf_file,
|
||||||
root_img=root_img)
|
root_img=root_img)
|
||||||
@ -1571,13 +1525,13 @@ class QubesVmCollection(dict):
|
|||||||
|
|
||||||
return vm
|
return vm
|
||||||
|
|
||||||
def add_new_fwvm(self, name,
|
def add_new_proxyvm(self, name, template_vm,
|
||||||
dir_path = None, conf_file = None,
|
dir_path = None, conf_file = None,
|
||||||
root_img = None):
|
root_img = None):
|
||||||
|
|
||||||
qid = self.get_new_unused_qid()
|
qid = self.get_new_unused_qid()
|
||||||
netid = self.get_new_unused_netid()
|
netid = self.get_new_unused_netid()
|
||||||
vm = QubesFirewallVm (qid=qid, name=name,
|
vm = QubesProxyVm (qid=qid, name=name, template_vm=template_vm,
|
||||||
netid=netid,
|
netid=netid,
|
||||||
dir_path=dir_path, conf_file=conf_file,
|
dir_path=dir_path, conf_file=conf_file,
|
||||||
netvm_vm = self.get_default_fw_netvm_vm(),
|
netvm_vm = self.get_default_fw_netvm_vm(),
|
||||||
@ -1603,7 +1557,7 @@ class QubesVmCollection(dict):
|
|||||||
return self[self.default_template_qid]
|
return self[self.default_template_qid]
|
||||||
|
|
||||||
def set_default_netvm_vm(self, vm):
|
def set_default_netvm_vm(self, vm):
|
||||||
assert vm.is_netvm(), "VM {0} is not a NetVM!".format(vm.name)
|
assert vm.is_netvm(), "VM {0} does not provide network!".format(vm.name)
|
||||||
self.default_netvm_qid = vm.qid
|
self.default_netvm_qid = vm.qid
|
||||||
|
|
||||||
def get_default_netvm_vm(self):
|
def get_default_netvm_vm(self):
|
||||||
@ -1613,7 +1567,7 @@ class QubesVmCollection(dict):
|
|||||||
return self[self.default_netvm_qid]
|
return self[self.default_netvm_qid]
|
||||||
|
|
||||||
def set_default_fw_netvm_vm(self, vm):
|
def set_default_fw_netvm_vm(self, vm):
|
||||||
assert vm.is_netvm(), "VM {0} is not a NetVM!".format(vm.name)
|
assert vm.is_netvm(), "VM {0} does not provide network!".format(vm.name)
|
||||||
self.default_fw_netvm_qid = vm.qid
|
self.default_fw_netvm_qid = vm.qid
|
||||||
|
|
||||||
def get_default_fw_netvm_vm(self):
|
def get_default_fw_netvm_vm(self):
|
||||||
@ -1754,65 +1708,6 @@ class QubesVmCollection(dict):
|
|||||||
if default_netvm != "None" else None
|
if default_netvm != "None" else None
|
||||||
#assert self.default_netvm_qid is not None
|
#assert self.default_netvm_qid is not None
|
||||||
|
|
||||||
# Read in the NetVMs first, because a reference to NetVM
|
|
||||||
# is needed to create all other VMs
|
|
||||||
for element in tree.findall("QubesNetVm"):
|
|
||||||
try:
|
|
||||||
kwargs = {}
|
|
||||||
attr_list = ("qid", "netid", "name", "dir_path", "conf_file",
|
|
||||||
"private_img", "root_img",
|
|
||||||
)
|
|
||||||
|
|
||||||
for attribute in attr_list:
|
|
||||||
kwargs[attribute] = element.get(attribute)
|
|
||||||
|
|
||||||
kwargs["qid"] = int(kwargs["qid"])
|
|
||||||
kwargs["netid"] = int(kwargs["netid"])
|
|
||||||
|
|
||||||
vm = QubesNetVm(**kwargs)
|
|
||||||
self[vm.qid] = vm
|
|
||||||
|
|
||||||
except (ValueError, LookupError) as err:
|
|
||||||
print("{0}: import error (QubesNetVM) {1}".format(
|
|
||||||
os.path.basename(sys.argv[0]), err))
|
|
||||||
return False
|
|
||||||
|
|
||||||
# Next read in the FirewallVMs, because they may be referenced
|
|
||||||
# by other VMs
|
|
||||||
for element in tree.findall("QubesFirewallVm"):
|
|
||||||
try:
|
|
||||||
kwargs = {}
|
|
||||||
attr_list = ("qid", "netid", "name", "dir_path", "conf_file",
|
|
||||||
"private_img", "root_img", "netvm_qid")
|
|
||||||
|
|
||||||
for attribute in attr_list:
|
|
||||||
kwargs[attribute] = element.get(attribute)
|
|
||||||
|
|
||||||
kwargs["qid"] = int(kwargs["qid"])
|
|
||||||
kwargs["netid"] = int(kwargs["netid"])
|
|
||||||
|
|
||||||
if kwargs["netvm_qid"] == "none" or kwargs["netvm_qid"] is None:
|
|
||||||
netvm_vm = None
|
|
||||||
kwargs.pop("netvm_qid")
|
|
||||||
else:
|
|
||||||
netvm_qid = int(kwargs.pop("netvm_qid"))
|
|
||||||
if netvm_qid not in self:
|
|
||||||
netvm_vm = None
|
|
||||||
else:
|
|
||||||
netvm_vm = self[netvm_qid]
|
|
||||||
|
|
||||||
kwargs["netvm_vm"] = netvm_vm
|
|
||||||
|
|
||||||
vm = QubesFirewallVm(**kwargs)
|
|
||||||
self[vm.qid] = vm
|
|
||||||
|
|
||||||
except (ValueError, LookupError) as err:
|
|
||||||
print("{0}: import error (QubesFirewallVM) {1}".format(
|
|
||||||
os.path.basename(sys.argv[0]), err))
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
self.default_template_qid
|
|
||||||
# Then, read in the TemplateVMs, because a reference to template VM
|
# Then, read in the TemplateVMs, because a reference to template VM
|
||||||
# is needed to create each AppVM
|
# is needed to create each AppVM
|
||||||
for element in tree.findall("QubesTemplateVm"):
|
for element in tree.findall("QubesTemplateVm"):
|
||||||
@ -1860,6 +1755,149 @@ class QubesVmCollection(dict):
|
|||||||
os.path.basename(sys.argv[0]), err))
|
os.path.basename(sys.argv[0]), err))
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
# Read in the NetVMs first, because a reference to NetVM
|
||||||
|
# is needed to create all other VMs
|
||||||
|
for element in tree.findall("QubesNetVm"):
|
||||||
|
try:
|
||||||
|
kwargs = {}
|
||||||
|
attr_list = ("qid", "netid", "name", "dir_path", "conf_file",
|
||||||
|
"private_img", "root_img", "template_qid",
|
||||||
|
)
|
||||||
|
|
||||||
|
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"])
|
||||||
|
|
||||||
|
kwargs["template_vm"] = template_vm
|
||||||
|
kwargs["netid"] = int(kwargs["netid"])
|
||||||
|
|
||||||
|
vm = QubesNetVm(**kwargs)
|
||||||
|
self[vm.qid] = vm
|
||||||
|
|
||||||
|
except (ValueError, LookupError) as err:
|
||||||
|
print("{0}: import error (QubesNetVM) {1}".format(
|
||||||
|
os.path.basename(sys.argv[0]), err))
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Next read in the ProxyVMs, because they may be referenced
|
||||||
|
# by other VMs
|
||||||
|
for element in tree.findall("QubesProxyVm"):
|
||||||
|
try:
|
||||||
|
kwargs = {}
|
||||||
|
attr_list = ("qid", "netid", "name", "dir_path", "conf_file",
|
||||||
|
"private_img", "root_img", "netvm_qid", "template_qid")
|
||||||
|
|
||||||
|
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: ProxyVM '{0}' uses unkown template qid='{1}'!".\
|
||||||
|
format(kwargs["name"], kwargs["template_qid"])
|
||||||
|
|
||||||
|
kwargs["template_vm"] = template_vm
|
||||||
|
kwargs["netid"] = int(kwargs["netid"])
|
||||||
|
|
||||||
|
if kwargs["netvm_qid"] == "none" or kwargs["netvm_qid"] is None:
|
||||||
|
netvm_vm = None
|
||||||
|
kwargs.pop("netvm_qid")
|
||||||
|
else:
|
||||||
|
netvm_qid = int(kwargs.pop("netvm_qid"))
|
||||||
|
if netvm_qid not in self:
|
||||||
|
netvm_vm = None
|
||||||
|
else:
|
||||||
|
netvm_vm = self[netvm_qid]
|
||||||
|
|
||||||
|
kwargs["netvm_vm"] = netvm_vm
|
||||||
|
|
||||||
|
vm = QubesProxyVm(**kwargs)
|
||||||
|
self[vm.qid] = vm
|
||||||
|
|
||||||
|
except (ValueError, LookupError) as err:
|
||||||
|
print("{0}: import error (QubesProxyVM) {1}".format(
|
||||||
|
os.path.basename(sys.argv[0]), err))
|
||||||
|
return False
|
||||||
|
|
||||||
|
# After importing all NetVMs and ProxyVMs, set netvm references
|
||||||
|
# 1. For TemplateVMs
|
||||||
|
for element in tree.findall("QubesTemplateVm"):
|
||||||
|
try:
|
||||||
|
|
||||||
|
kwargs = {}
|
||||||
|
attr_list = ("qid", "uses_default_netvm", "netvm_qid")
|
||||||
|
|
||||||
|
for attribute in attr_list:
|
||||||
|
kwargs[attribute] = element.get(attribute)
|
||||||
|
|
||||||
|
vm = self[int(kwargs["qid"])]
|
||||||
|
|
||||||
|
if "uses_default_netvm" not in kwargs:
|
||||||
|
vm.uses_default_netvm = True
|
||||||
|
else:
|
||||||
|
vm.uses_default_netvm = True if kwargs["uses_default_netvm"] == "True" else False
|
||||||
|
if vm.uses_default_netvm is True:
|
||||||
|
netvm_vm = self.get_default_netvm_vm()
|
||||||
|
kwargs.pop("netvm_qid")
|
||||||
|
else:
|
||||||
|
if kwargs["netvm_qid"] == "none" or kwargs["netvm_qid"] is None:
|
||||||
|
netvm_vm = None
|
||||||
|
kwargs.pop("netvm_qid")
|
||||||
|
else:
|
||||||
|
netvm_qid = int(kwargs.pop("netvm_qid"))
|
||||||
|
if netvm_qid not in self:
|
||||||
|
netvm_vm = None
|
||||||
|
else:
|
||||||
|
netvm_vm = self[netvm_qid]
|
||||||
|
|
||||||
|
vm.netvm_vm = netvm_vm
|
||||||
|
|
||||||
|
except (ValueError, LookupError) as err:
|
||||||
|
print("{0}: import error (QubesTemplateVm): {1}".format(
|
||||||
|
os.path.basename(sys.argv[0]), err))
|
||||||
|
return False
|
||||||
|
|
||||||
|
# 2. For PoxyVMs
|
||||||
|
for element in tree.findall("QubesProxyVm"):
|
||||||
|
try:
|
||||||
|
kwargs = {}
|
||||||
|
attr_list = ("qid", "netvm_qid")
|
||||||
|
|
||||||
|
for attribute in attr_list:
|
||||||
|
kwargs[attribute] = element.get(attribute)
|
||||||
|
|
||||||
|
vm = self[int(kwargs["qid"])]
|
||||||
|
|
||||||
|
if kwargs["netvm_qid"] == "none" or kwargs["netvm_qid"] is None:
|
||||||
|
netvm_vm = None
|
||||||
|
kwargs.pop("netvm_qid")
|
||||||
|
else:
|
||||||
|
netvm_qid = int(kwargs.pop("netvm_qid"))
|
||||||
|
if netvm_qid not in self:
|
||||||
|
netvm_vm = None
|
||||||
|
else:
|
||||||
|
netvm_vm = self[netvm_qid]
|
||||||
|
|
||||||
|
vm.netvm_vm = netvm_vm
|
||||||
|
|
||||||
|
except (ValueError, LookupError) as err:
|
||||||
|
print("{0}: import error (QubesProxyVM) {1}".format(
|
||||||
|
os.path.basename(sys.argv[0]), err))
|
||||||
|
return False
|
||||||
|
|
||||||
# Finally, read in the AppVMs
|
# Finally, read in the AppVMs
|
||||||
for element in tree.findall("QubesAppVm"):
|
for element in tree.findall("QubesAppVm"):
|
||||||
try:
|
try:
|
||||||
@ -2011,4 +2049,4 @@ class QubesDaemonPidfile(object):
|
|||||||
self.remove_pidfile()
|
self.remove_pidfile()
|
||||||
|
|
||||||
|
|
||||||
# vim:sw=4:et:
|
# vim:sw=4:et:ts=4:
|
||||||
|
@ -39,15 +39,16 @@ fields = {
|
|||||||
+ ('}' if vm.is_netvm() else '')"},
|
+ ('}' if vm.is_netvm() else '')"},
|
||||||
|
|
||||||
"type": {"func": "'Tpl' if vm.is_template() else \
|
"type": {"func": "'Tpl' if vm.is_template() else \
|
||||||
(' Fw' if vm.is_fwvm() else \
|
(' Prox' if vm.is_proxyvm() else \
|
||||||
(' Net' if vm.is_netvm() else ''))"},
|
(' Net' if vm.is_netvm() else ''))"},
|
||||||
|
|
||||||
"updbl" : {"func": "'Yes' if vm.is_updateable() else ''"},
|
"updbl" : {"func": "'Yes' if vm.is_updateable() else ''"},
|
||||||
|
|
||||||
"template": {"func": "'n/a' if vm.is_template() or vm.is_netvm() else\
|
"template": {"func": "'n/a' if vm.is_template() else\
|
||||||
qvm_collection[vm.template_vm.qid].name"},
|
('None' if vm.template_vm is None else\
|
||||||
|
qvm_collection[vm.template_vm.qid].name)"},
|
||||||
|
|
||||||
"netvm": {"func": "'n/a' if vm.is_netvm() and not vm.is_fwvm() else\
|
"netvm": {"func": "'n/a' if vm.is_netvm() and not vm.is_proxyvm() else\
|
||||||
('*' if vm.uses_default_netvm else '') +\
|
('*' if vm.uses_default_netvm else '') +\
|
||||||
qvm_collection[vm.netvm_vm.qid].name\
|
qvm_collection[vm.netvm_vm.qid].name\
|
||||||
if vm.netvm_vm is not None else '-'"},
|
if vm.netvm_vm is not None else '-'"},
|
||||||
|
Loading…
Reference in New Issue
Block a user