From c1bd86142c65f9eb2176cf23c799dacab731c846 Mon Sep 17 00:00:00 2001 From: Marek Marczykowski Date: Sun, 6 Mar 2011 17:06:45 +0100 Subject: [PATCH] NetVM and ProxyVM based on template: part 1 (core) --- dom0/qvm-core/qubes.py | 552 ++++++++++++++++++++++------------------- dom0/qvm-tools/qvm-ls | 9 +- 2 files changed, 300 insertions(+), 261 deletions(-) diff --git a/dom0/qvm-core/qubes.py b/dom0/qvm-core/qubes.py index 3ab0ba83..19a4ce18 100755 --- a/dom0/qvm-core/qubes.py +++ b/dom0/qvm-core/qubes.py @@ -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 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... dom0_vm = None @@ -270,8 +270,8 @@ class QubesVm(object): def is_netvm(self): return isinstance(self, QubesNetVm) - def is_fwvm(self): - return isinstance(self, QubesFirewallVm) + def is_proxyvm(self): + return isinstance(self, QubesProxyVm) def is_disposablevm(self): return isinstance(self, QubesDisposableVm) @@ -529,7 +529,7 @@ class QubesVm(object): print "--> Setting Xen Store info for the VM..." 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 if verbose: print "--> Attaching to the network backend (netvm={0})...".format(self.netvm_vm.name) @@ -555,7 +555,7 @@ class QubesVm(object): #if verbose: # print "--> Updating FirewallVMs rules..." #for vm in qvm_collection.values(): - # if vm.is_fwvm(): + # if vm.is_proxyvm(): # vm.write_iptables_xenstore_entry() if verbose: @@ -591,7 +591,7 @@ class QubesVm(object): 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): @@ -818,59 +818,110 @@ class QubesTemplateVm(QubesVm): ) 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: - 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 - private_img = kwargs.pop("private_img") if "private_img" in kwargs else None + if "updateable" not in kwargs or kwargs["updateable"] is None: + kwargs["updateable"] = False - kwargs["updateable"] = True - super(QubesServiceVm, self).__init__(**kwargs) + 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"] - 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): - self.root_img = root_img - else: - self.root_img = dir_path + "/" + ( - root_img if root_img is not None else default_root_img) + 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 + def set_updateable(self): if self.is_updateable(): 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() - 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): - return self.get_disk_usage(self.root_img) + return 0 def get_root_img_sz(self): - if not os.path.exists(self.root_img): - return 0 - - return os.path.getsize(self.root_img) + 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}".\ @@ -881,19 +932,53 @@ class QubesServiceVm(QubesVm): "VM config file doesn't exist: {0}".\ format(self.conf_file)) - if not os.path.exists (self.root_img): + if not os.path.exists (self.private_img): raise QubesException ( - "VM root image file doesn't exist: {0}".\ - format(self.root_img)) - + "VM private image file doesn't exist: {0}".\ + format(self.private_img)) return True - def create_xml_element(self): - raise NotImplementedError + def start(self, debug_console = False, verbose = False, preparing_dvm = False): + 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): netid = kwargs.pop("netid") @@ -904,6 +989,9 @@ class QubesNetVm(QubesServiceVm): self.__gateway = self.netprefix + "0.1" 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: kwargs["label"] = default_servicevm_label super(QubesNetVm, self).__init__(installed_by_rpm=True, **kwargs) @@ -942,28 +1030,29 @@ class QubesNetVm(QubesServiceVm): name=self.name, dir_path=self.dir_path, 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, installed_by_rpm=str(self.installed_by_rpm), ) 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): - super(QubesFirewallVm, self).__init__(uses_default_netvm=False, **kwargs) + super(QubesProxyVm, self).__init__(uses_default_netvm=False, **kwargs) @property def type(self): - return "FirewallVM" + return "ProxyVM" def create_xenstore_entries(self, xid): if dry_run: return - super(QubesFirewallVm, self).create_xenstore_entries(xid) + super(QubesProxyVm, self).create_xenstore_entries(xid) self.write_iptables_xenstore_entry() def write_iptables_xenstore_entry(self): @@ -1052,13 +1141,14 @@ class QubesFirewallVm(QubesNetVm): def create_xml_element(self): element = xml.etree.ElementTree.Element( - "QubesFirewallVm", + "QubesProxyVm", qid=str(self.qid), netid=str(self.netid), name=self.name, dir_path=self.dir_path, 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", private_img=self.private_img, installed_by_rpm=str(self.installed_by_rpm), @@ -1068,8 +1158,9 @@ class QubesFirewallVm(QubesNetVm): class QubesDom0NetVm(QubesNetVm): def __init__(self): super(QubesDom0NetVm, self).__init__(qid=0, name="dom0", netid=0, - dir_path=None, root_img = None, + dir_path=None, private_img = None, + template_vm = None, label = default_template_label) if not dry_run and xend_session.session is not None: self.session_hosts = xend_session.session.xenapi.host.get_all() @@ -1140,7 +1231,7 @@ class QubesDom0NetVm(QubesNetVm): 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): @@ -1175,43 +1266,15 @@ class QubesDisposableVm(QubesVm): 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): - 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) - qid = kwargs["qid"] 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 if "firewall_conf" not in kwargs or kwargs["firewall_conf"] is None: @@ -1225,62 +1288,15 @@ class QubesAppVm(QubesVm): return "AppVM" 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 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() + super(QubesAppVm, self).set_updateable() + self.reset_swap_cow_storage() 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)) + super(QubesAppVm, self).create_on_disk(verbose) if verbose: print "--> Creating icon symlink: {0} -> {1}".format(self.icon_path, self.label.icon_path) @@ -1363,35 +1379,6 @@ class QubesAppVm(QubesVm): 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): element = xml.etree.ElementTree.Element( "QubesAppVm", @@ -1412,33 +1399,10 @@ class QubesAppVm(QubesVm): if dry_run: 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() 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): print "--> Resetting the swap COW storage: {0}...".format (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.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): """ A collection of Qubes VMs indexed by Qubes id (qid) @@ -1551,13 +1505,13 @@ class QubesVmCollection(dict): return vm - def add_new_netvm(self, name, + def add_new_netvm(self, name, template_vm, dir_path = None, conf_file = None, root_img = None): qid = self.get_new_unused_qid() netid = self.get_new_unused_netid() - vm = QubesNetVm (qid=qid, name=name, + vm = QubesNetVm (qid=qid, name=name, template_vm=template_vm, netid=netid, dir_path=dir_path, conf_file=conf_file, root_img=root_img) @@ -1571,13 +1525,13 @@ class QubesVmCollection(dict): return vm - def add_new_fwvm(self, name, + def add_new_proxyvm(self, name, template_vm, dir_path = None, conf_file = None, root_img = None): qid = self.get_new_unused_qid() netid = self.get_new_unused_netid() - vm = QubesFirewallVm (qid=qid, name=name, + vm = QubesProxyVm (qid=qid, name=name, template_vm=template_vm, netid=netid, dir_path=dir_path, conf_file=conf_file, netvm_vm = self.get_default_fw_netvm_vm(), @@ -1603,7 +1557,7 @@ class QubesVmCollection(dict): return self[self.default_template_qid] 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 def get_default_netvm_vm(self): @@ -1613,7 +1567,7 @@ class QubesVmCollection(dict): return self[self.default_netvm_qid] 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 def get_default_fw_netvm_vm(self): @@ -1754,65 +1708,6 @@ class QubesVmCollection(dict): if default_netvm != "None" else 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 # is needed to create each AppVM for element in tree.findall("QubesTemplateVm"): @@ -1860,6 +1755,149 @@ class QubesVmCollection(dict): os.path.basename(sys.argv[0]), err)) 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 for element in tree.findall("QubesAppVm"): try: @@ -2011,4 +2049,4 @@ class QubesDaemonPidfile(object): self.remove_pidfile() -# vim:sw=4:et: +# vim:sw=4:et:ts=4: diff --git a/dom0/qvm-tools/qvm-ls b/dom0/qvm-tools/qvm-ls index 1bb241f0..7516af1e 100755 --- a/dom0/qvm-tools/qvm-ls +++ b/dom0/qvm-tools/qvm-ls @@ -39,15 +39,16 @@ fields = { + ('}' if vm.is_netvm() 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 ''))"}, "updbl" : {"func": "'Yes' if vm.is_updateable() else ''"}, - "template": {"func": "'n/a' if vm.is_template() or vm.is_netvm() else\ - qvm_collection[vm.template_vm.qid].name"}, + "template": {"func": "'n/a' if vm.is_template() else\ + ('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 '') +\ qvm_collection[vm.netvm_vm.qid].name\ if vm.netvm_vm is not None else '-'"},