From 1c2311226ef86bc3afb39eaf950b79d76147b860 Mon Sep 17 00:00:00 2001 From: Marek Marczykowski Date: Fri, 2 Mar 2012 13:49:23 +0100 Subject: [PATCH 1/8] dom0/network: use static MAC for VMs --- dom0/qvm-core/qubes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dom0/qvm-core/qubes.py b/dom0/qvm-core/qubes.py index afe5fb18..bc19deb0 100755 --- a/dom0/qvm-core/qubes.py +++ b/dom0/qvm-core/qubes.py @@ -804,7 +804,7 @@ class QubesVm(object): args['maxmem'] = str(self.maxmem) args['vcpus'] = str(self.vcpus) if self.netvm_vm is not None: - args['netdev'] = "'script=/etc/xen/scripts/vif-route-qubes,ip={ip}".format(ip=self.ip) + args['netdev'] = "'mac=00:16:3E:5E:6C:{qid:02X},script=/etc/xen/scripts/vif-route-qubes,ip={ip}".format(ip=self.ip, qid=self.qid) if self.netvm_vm.qid != 0: args['netdev'] += ",backend={0}".format(self.netvm_vm.name) args['netdev'] += "'" From 4d554fd182d0bba7351f95658be2c100635db842 Mon Sep 17 00:00:00 2001 From: Marek Marczykowski Date: Fri, 2 Mar 2012 14:28:09 +0100 Subject: [PATCH 2/8] dom0/core: set default memory for HVM to 512MB --- dom0/qvm-core/qubes.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/dom0/qvm-core/qubes.py b/dom0/qvm-core/qubes.py index 597be74d..23ee95c5 100755 --- a/dom0/qvm-core/qubes.py +++ b/dom0/qvm-core/qubes.py @@ -80,6 +80,7 @@ default_kernelopts = "" default_kernelopts_pcidevs = "iommu=soft swiotlb=2048" default_hvm_disk_size = 20*1024*1024*1024 +default_hvm_memory = 512 config_template_pv = '/usr/share/qubes/vm-template.conf' config_template_hvm = '/usr/share/qubes/vm-template-hvm.conf' @@ -2049,6 +2050,8 @@ class QubesHVm(QubesVm): # only updateable HVM supported kwargs["updateable"] = True kwargs["template_vm"] = None + if "memory" not in kwargs or kwargs["memory"] is None: + kwargs["memory"] = default_hvm_memory super(QubesHVm, self).__init__(**kwargs) self.updateable = True From 5191c64b06b361145d048f17e228210ce174ee2e Mon Sep 17 00:00:00 2001 From: Marek Marczykowski Date: Fri, 24 Feb 2012 04:19:51 +0100 Subject: [PATCH 3/8] dom0/core: more robust XML attrs generation Still not ideal... --- dom0/qvm-core/qubes.py | 28 +++++++++++----------------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/dom0/qvm-core/qubes.py b/dom0/qvm-core/qubes.py index bc19deb0..715abfde 100755 --- a/dom0/qvm-core/qubes.py +++ b/dom0/qvm-core/qubes.py @@ -1314,26 +1314,20 @@ class QubesVm(object): attrs["qid"] = str(self.qid) attrs["name"] = self.name attrs["dir_path"] = self.dir_path - attrs["conf_file"] = self.relative_path(self.conf_file) - attrs["root_img"] = self.relative_path(self.root_img) - attrs["volatile_img"] = self.relative_path(self.volatile_img) - attrs["private_img"] = self.relative_path(self.private_img) - attrs["uses_default_netvm"] = str(self.uses_default_netvm) + # Simple paths + for prop in ['conf_file', 'root_img', 'volatile_img', 'private_img']: + if hasattr(self, prop): + attrs[prop] = self.relative_path(self.__getattribute__(prop)) + # Simple string attrs + for prop in ['memory', 'maxmem', 'pcidevs', 'vcpus', 'internal',\ + 'uses_default_kernel', 'kernel', 'uses_default_kernelopts',\ + 'kernelopts', 'services', 'updateable', 'installed_by_rpm',\ + 'uses_default_netvm' ]: + if hasattr(self, prop): + attrs[prop] = str(self.__getattribute__(prop)) attrs["netvm_qid"] = str(self.netvm_vm.qid) if self.netvm_vm is not None else "none" - attrs["installed_by_rpm"] = str(self.installed_by_rpm) attrs["template_qid"] = str(self.template_vm.qid) if self.template_vm and not self.is_updateable() else "none" - attrs["updateable"] = str(self.updateable) attrs["label"] = self.label.name - attrs["memory"] = str(self.memory) - attrs["maxmem"] = str(self.maxmem) - attrs["pcidevs"] = str(self.pcidevs) - attrs["vcpus"] = str(self.vcpus) - attrs["internal"] = str(self.internal) - attrs["uses_default_kernel"] = str(self.uses_default_kernel) - attrs["kernel"] = str(self.kernel) - attrs["uses_default_kernelopts"] = str(self.uses_default_kernelopts) - attrs["kernelopts"] = str(self.kernelopts) - attrs["services"] = str(self.services) return attrs def create_xml_element(self): From 5f87303b69b168fc00b7cce6bc39315f25ab4304 Mon Sep 17 00:00:00 2001 From: Marek Marczykowski Date: Fri, 2 Mar 2012 16:06:26 +0100 Subject: [PATCH 4/8] dom0/core: rework of VM clone mechanism --- dom0/qvm-core/qubes.py | 133 +++++++++++------- .../{qvm-clone-template => qvm-clone} | 26 ++-- 2 files changed, 97 insertions(+), 62 deletions(-) rename dom0/qvm-tools/{qvm-clone-template => qvm-clone} (69%) diff --git a/dom0/qvm-core/qubes.py b/dom0/qvm-core/qubes.py index 715abfde..9fa51f79 100755 --- a/dom0/qvm-core/qubes.py +++ b/dom0/qvm-core/qubes.py @@ -921,6 +921,73 @@ class QubesVm(object): except subprocess.CalledProcessError: print >> sys.stderr, "Ooops, there was a problem creating appmenus for {0} VM!".format (self.name) + def get_clone_attrs(self): + return ['kernel', 'uses_default_kernel', 'netvm_vm', 'uses_default_netvm', \ + 'memory', 'maxmem', 'kernelopts', 'uses_default_kernelopts', 'services', 'vcpus'] + + def clone_attrs(self, src_vm): + for prop in self.get_clone_attrs(): + setattr(self, prop, getattr(src_vm, prop)) + + def clone_disk_files(self, src_vm, verbose): + if dry_run: + return + + if src_vm.is_running(): + raise QubesException("Attempt to clone a running VM!") + + if verbose: + print >> sys.stderr, "--> Creating directory: {0}".format(self.dir_path) + os.mkdir (self.dir_path) + + if src_vm.private_img is not None and self.private_img is not None: + if verbose: + print >> sys.stderr, "--> Copying the private image:\n{0} ==>\n{1}".\ + format(src_vm.private_img, self.private_img) + # We prefer to use Linux's cp, because it nicely handles sparse files + retcode = subprocess.call (["cp", src_vm.private_img, self.private_img]) + if retcode != 0: + raise IOError ("Error while copying {0} to {1}".\ + format(src_vm.private_img, self.private_img)) + + if src_vm.updateable and src_vm.root_img is not None and self.root_img is not None: + if verbose: + print >> sys.stderr, "--> Copying the root image:\n{0} ==>\n{1}".\ + format(src_vm.root_img, self.root_img) + # We prefer to use Linux's cp, because it nicely handles sparse files + retcode = subprocess.call (["cp", src_vm.root_img, self.root_img]) + if retcode != 0: + raise IOError ("Error while copying {0} to {1}".\ + format(src_vm.root_img, self.root_img)) + + if src_vm.updateable and src_vm.appmenus_templates_dir is not None and self.appmenus_templates_dir is not None: + if verbose: + print >> sys.stderr, "--> Copying the template's appmenus templates dir:\n{0} ==>\n{1}".\ + format(src_vm.appmenus_templates_dir, self.appmenus_templates_dir) + shutil.copytree (src_vm.appmenus_templates_dir, self.appmenus_templates_dir) + + if os.path.exists(src_vm.dir_path + '/' + qubes_whitelisted_appmenus): + if verbose: + print >> sys.stderr, "--> Copying whitelisted apps list: {0}".\ + format(self.dir_path + '/' + qubes_whitelisted_appmenus) + shutil.copy(src_vm.dir_path + '/' + qubes_whitelisted_appmenus, + self.dir_path + '/' + qubes_whitelisted_appmenus) + + if src_vm.icon_path is not None and self.icon_path is not None: + if os.path.exists (src_vm.dir_path): + if os.path.islink(src_vm.dir_path): + icon_path = os.readlink(src_vm.dir_path) + if verbose: + print >> sys.stderr, "--> Creating icon symlink: {0} -> {1}".format(self.icon_path, icon_path) + os.symlink (icon_path, self.icon_path) + else: + if verbose: + print >> sys.stderr, "--> Copying icon: {0} -> {1}".format(src_vm.icon_path, self.icon_path) + shutil.copy(src_vm.icon_path, self.icon_path) + + # Create appmenus + self.create_appmenus(verbose) + def remove_appmenus(self): vmtype = None if self.is_netvm(): @@ -1389,39 +1456,24 @@ class QubesTemplateVm(QubesVm): def get_rootdev(self, source_template=None): return "'script:origin:{dir}/root.img:{dir}/root-cow.img,xvda,w',".format(dir=self.dir_path) - def clone_disk_files(self, src_template_vm, verbose): + def clone_disk_files(self, src_vm, verbose): if dry_run: return + super(QubesTemplateVM, self).clone_disk_files(src_vm=src_vm, verbose=verbose) - assert not src_template_vm.is_running(), "Attempt to clone a running Template VM!" + if os.path.exists(src_vm.dir_path + '/vm-' + qubes_whitelisted_appmenus): + if verbose: + print >> sys.stderr, "--> Copying default whitelisted apps list: {0}".\ + format(self.dir_path + '/vm-' + qubes_whitelisted_appmenus) + shutil.copy(src_vm.dir_path + '/vm-' + qubes_whitelisted_appmenus, + self.dir_path + '/vm-' + qubes_whitelisted_appmenus) - if verbose: - print >> sys.stderr, "--> Creating directory: {0}".format(self.dir_path) - os.mkdir (self.dir_path) - - if verbose: - print >> sys.stderr, "--> Copying the template's private image:\n{0} ==>\n{1}".\ - format(src_template_vm.private_img, self.private_img) - # We prefer to use Linux's cp, because it nicely handles sparse files - retcode = subprocess.call (["cp", src_template_vm.private_img, self.private_img]) - if retcode != 0: - raise IOError ("Error while copying {0} to {1}".\ - format(src_template_vm.private_img, self.private_img)) - - if verbose: - print >> sys.stderr, "--> Copying the template's root image:\n{0} ==>\n{1}".\ - format(src_template_vm.root_img, self.root_img) - # We prefer to use Linux's cp, because it nicely handles sparse files - retcode = subprocess.call (["cp", src_template_vm.root_img, self.root_img]) - if retcode != 0: - raise IOError ("Error while copying {0} to {1}".\ - format(src_template_vm.root_img, self.root_img)) if verbose: print >> sys.stderr, "--> Copying the template's clean volatile image:\n{0} ==>\n{1}".\ - format(src_template_vm.clean_volatile_img, self.clean_volatile_img) + format(src_vm.clean_volatile_img, self.clean_volatile_img) # We prefer to use Linux's cp, because it nicely handles sparse files - retcode = subprocess.call (["cp", src_template_vm.clean_volatile_img, self.clean_volatile_img]) + retcode = subprocess.call (["cp", src_vm.clean_volatile_img, self.clean_volatile_img]) if retcode != 0: raise IOError ("Error while copying {0} to {1}".\ format(src_template_vm.clean_volatile_img, self.clean_volatile_img)) @@ -1432,44 +1484,17 @@ class QubesTemplateVm(QubesVm): retcode = subprocess.call (["cp", self.clean_volatile_img, self.volatile_img]) if retcode != 0: raise IOError ("Error while copying {0} to {1}".\ - format(self.clean_volatile_img, self.volatile_img)) + format(self.clean_img, self.volatile_img)) if verbose: print >> sys.stderr, "--> Copying the template's DispVM prerun script..." - retcode = subprocess.call (["cp", src_template_vm.dir_path + '/dispvm-prerun.sh', self.dir_path + '/dispvm-prerun.sh']) + retcode = subprocess.call (["cp", src_vm.dir_path + '/dispvm-prerun.sh', self.dir_path + '/dispvm-prerun.sh']) if retcode != 0: raise IOError ("Error while copying DispVM prerun script") - if verbose: - print >> sys.stderr, "--> Copying the template's appmenus templates dir:\n{0} ==>\n{1}".\ - format(src_template_vm.appmenus_templates_dir, self.appmenus_templates_dir) - shutil.copytree (src_template_vm.appmenus_templates_dir, self.appmenus_templates_dir) - - if os.path.exists(src_template_vm.dir_path + '/' + qubes_whitelisted_appmenus): - if verbose: - print >> sys.stderr, "--> Copying whitelisted apps list: {0}".\ - format(self.dir_path + '/' + qubes_whitelisted_appmenus) - shutil.copy(src_template_vm.dir_path + '/' + qubes_whitelisted_appmenus, - self.dir_path + '/' + qubes_whitelisted_appmenus) - - if os.path.exists(src_template_vm.dir_path + '/vm-' + qubes_whitelisted_appmenus): - if verbose: - print >> sys.stderr, "--> Copying default whitelisted apps list: {0}".\ - format(self.dir_path + '/vm-' + qubes_whitelisted_appmenus) - shutil.copy(src_template_vm.dir_path + '/vm-' + qubes_whitelisted_appmenus, - self.dir_path + '/vm-' + qubes_whitelisted_appmenus) - - icon_path = "/usr/share/qubes/icons/template.png" - if verbose: - print >> sys.stderr, "--> Creating icon symlink: {0} -> {1}".format(self.icon_path, icon_path) - os.symlink (icon_path, self.icon_path) - # Create root-cow.img self.commit_changes(verbose=verbose) - # Create appmenus - self.create_appmenus(verbose, source_template = src_template_vm) - def create_appmenus(self, verbose, source_template = None): if source_template is None: source_template = self.template_vm diff --git a/dom0/qvm-tools/qvm-clone-template b/dom0/qvm-tools/qvm-clone similarity index 69% rename from dom0/qvm-tools/qvm-clone-template rename to dom0/qvm-tools/qvm-clone index b8155b02..161b2633 100755 --- a/dom0/qvm-tools/qvm-clone-template +++ b/dom0/qvm-tools/qvm-clone @@ -21,13 +21,14 @@ # from qubes.qubes import QubesVmCollection +from qubes.qubes import QubesAppVm, QubesTemplateVm from qubes.qubes import QubesException from optparse import OptionParser; import sys def main(): - usage = "usage: %prog [options] \n"\ - "Clones an existing template by copying all its disk files" + usage = "usage: %prog [options] \n"\ + "Clones an existing VM by copying all its disk files" parser = OptionParser (usage) parser.add_option ("-q", "--quiet", action="store_false", dest="verbose", default=True) @@ -44,8 +45,8 @@ def main(): qvm_collection.lock_db_for_writing() qvm_collection.load() - src_tvm = qvm_collection.get_vm_by_name(srcname) - if src_tvm is None: + src_vm = qvm_collection.get_vm_by_name(srcname) + if src_vm is None: print >> sys.stderr, "ERROR: A VM with the name '{0}' does not exist in the system.".format(srcname) exit(1) @@ -53,15 +54,24 @@ def main(): print >> sys.stderr, "ERROR: A VM with the name '{0}' already exists in the system.".format(dstname) exit(1) - dst_tvm = qvm_collection.clone_templatevm(src_template_vm=src_tvm, - name=dstname, + dst_vm = None + if isinstance(src_vm, QubesTemplateVm): + dst_vm = qvm_collection.add_new_templatevm(name=dstname, + dir_path=options.dir_path, installed_by_rpm=False) + elif isinstance(src_vm, QubesAppVm): + dst_vm = qvm_collection.add_new_appvm(name=dstname, template_vm=src_vm.template_vm, + updateable=src_vm.updateable, label=src_vm.label, dir_path=options.dir_path) + else: + print >> sys.stderr, "ERROR: Clone not supported for this type of VM" + exit(1) try: - dst_tvm.clone_disk_files (src_template_vm=src_tvm, verbose=options.verbose) + dst_vm.clone_attrs(src_vm) + dst_vm.clone_disk_files (src_vm=src_vm, verbose=options.verbose) except (IOError, OSError) as err: print >> sys.stderr, "ERROR: {0}".format(err) - qvm_collection.pop(dst_tvm.qid) + qvm_collection.pop(dst_vm.qid) exit (1) qvm_collection.save() From 7c9075d09b8273468d90afe4ead727875c5722dd Mon Sep 17 00:00:00 2001 From: Marek Marczykowski Date: Fri, 2 Mar 2012 16:07:31 +0100 Subject: [PATCH 5/8] dom0/qvm-network: allow to set persistent MAC for VM --- dom0/qvm-core/qubes.py | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/dom0/qvm-core/qubes.py b/dom0/qvm-core/qubes.py index 9fa51f79..92d09164 100755 --- a/dom0/qvm-core/qubes.py +++ b/dom0/qvm-core/qubes.py @@ -210,6 +210,7 @@ class QubesVm(object): uses_default_kernel = True, kernelopts = "", uses_default_kernelopts = True, + mac = None, services = None): @@ -226,6 +227,8 @@ class QubesVm(object): if netvm_vm is not None: netvm_vm.connected_vms[qid] = self + self._mac = mac + # We use it in remove from disk to avoid removing rpm files (for templates) self.installed_by_rpm = installed_by_rpm @@ -386,6 +389,17 @@ class QubesVm(object): return None return "vif{0}.+".format(self.xid) + @property + def mac(self): + if self._mac is not None: + return self._mac + else: + return "00:16:3E:5E:6C:{qid:02X}".format(qid=self.qid) + + @mac.setter + def mac(self, new_mac): + self._mac = new_mac + def is_updateable(self): return self.updateable @@ -804,7 +818,7 @@ class QubesVm(object): args['maxmem'] = str(self.maxmem) args['vcpus'] = str(self.vcpus) if self.netvm_vm is not None: - args['netdev'] = "'mac=00:16:3E:5E:6C:{qid:02X},script=/etc/xen/scripts/vif-route-qubes,ip={ip}".format(ip=self.ip, qid=self.qid) + args['netdev'] = "'mac={mac},script=/etc/xen/scripts/vif-route-qubes,ip={ip}".format(ip=self.ip, mac=self.mac) if self.netvm_vm.qid != 0: args['netdev'] += ",backend={0}".format(self.netvm_vm.name) args['netdev'] += "'" @@ -1392,6 +1406,8 @@ class QubesVm(object): 'uses_default_netvm' ]: if hasattr(self, prop): attrs[prop] = str(self.__getattribute__(prop)) + if self._mac is not None: + attrs["mac"] = str(self._mac) attrs["netvm_qid"] = str(self.netvm_vm.qid) if self.netvm_vm is not None else "none" attrs["template_qid"] = str(self.template_vm.qid) if self.template_vm and not self.is_updateable() else "none" attrs["label"] = self.label.name @@ -2397,7 +2413,7 @@ class QubesVmCollection(dict): "installed_by_rpm", "updateable", "internal", "uses_default_netvm", "label", "memory", "vcpus", "pcidevs", "maxmem", "kernel", "uses_default_kernel", "kernelopts", "uses_default_kernelopts", - "services" ) + "mac", "services" ) for attribute in common_attr_list: kwargs[attribute] = element.get(attribute) From 7a3b9d003348ae8bf791c75ec6cbd26879435da2 Mon Sep 17 00:00:00 2001 From: Marek Marczykowski Date: Fri, 2 Mar 2012 16:17:18 +0100 Subject: [PATCH 6/8] dom0/qvm-prefs: add missing exit(1) on invalid arguments --- dom0/qvm-tools/qvm-prefs | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/dom0/qvm-tools/qvm-prefs b/dom0/qvm-tools/qvm-prefs index 08fbc0b8..37041a3a 100755 --- a/dom0/qvm-tools/qvm-prefs +++ b/dom0/qvm-tools/qvm-prefs @@ -28,6 +28,7 @@ from optparse import OptionParser import subprocess import os import sys +import re def do_list(vm): label_width = 18 @@ -57,6 +58,7 @@ def do_list(vm): print fmt.format ("vcpus", str(vm.vcpus)) print fmt.format ("memory", vm.memory) print fmt.format ("maxmem", vm.maxmem) + print fmt.format ("MAC", "%s%s" % (vm.mac, " (auto)" if vm._mac is None else "")) if vm.template_vm is not None: print fmt.format ("kernel", "%s (from template)" % vm.kernel) elif vm.uses_default_kernel: @@ -86,18 +88,38 @@ def set_label(vms, vm, args): def set_memory(vms, vm, args): if len (args) != 1: print >> sys.stderr, "Missing memory argument!" + exit (1) vm.memory = int(args[0]) def set_maxmem(vms, vm, args): if len (args) != 1: print >> sys.stderr, "Missing maxmem argument!" + exit (1) vm.maxmem = int(args[0]) +def set_mac(vms, vm, args): + if len (args) != 1: + print >> sys.stderr, "Missing MAC argument!" + exit (1) + + if not re.match("[0-9a-fA-F:]{17}|auto", args[0]): + print >> sys.stderr, "Invalid MAC argument!" + print >> sys.stderr, "Possible values:" + print >> sys.stderr, "1) auto" + print >> sys.stderr, "2) MAC in format: XX:XX:XX:XX:XX:XX" + exit (1) + + mac = args[0] + if mac == "auto": + mac = None + vm.mac = mac + def set_pcidevs(vms, vm, args): if len (args) != 1: print >> sys.stderr, "Missing pcidevs argument!" + exit (1) vm.pcidevs = list(eval(args[0])) @@ -301,6 +323,7 @@ properties = { "vcpus" : set_vcpus, "kernelopts": set_kernelopts, "name": set_name, + "mac": set_mac, } From b4b639cbb3ff26aca107dfaf16d2ad8dc6bb05b8 Mon Sep 17 00:00:00 2001 From: Marek Marczykowski Date: Fri, 2 Mar 2012 16:28:15 +0100 Subject: [PATCH 7/8] dom0/core: clone support for HVM --- dom0/qvm-core/qubes.py | 9 +++++++++ dom0/qvm-tools/qvm-clone | 4 +++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/dom0/qvm-core/qubes.py b/dom0/qvm-core/qubes.py index e8dd8e1b..d1194c54 100755 --- a/dom0/qvm-core/qubes.py +++ b/dom0/qvm-core/qubes.py @@ -2118,6 +2118,15 @@ class QubesHVm(QubesVm): def is_appvm(self): return True + def get_clone_attrs(self): + attrs = super(QubesHVm, self).get_clone_attrs() + attrs.remove('kernel') + attrs.remove('uses_default_kernel') + attrs.remove('kernelopts') + attrs.remove('uses_default_kernelopts') + attrs.remove('maxmem') + return attrs + def create_on_disk(self, verbose, source_template = None): if dry_run: return diff --git a/dom0/qvm-tools/qvm-clone b/dom0/qvm-tools/qvm-clone index 161b2633..89e13d03 100755 --- a/dom0/qvm-tools/qvm-clone +++ b/dom0/qvm-tools/qvm-clone @@ -21,7 +21,7 @@ # from qubes.qubes import QubesVmCollection -from qubes.qubes import QubesAppVm, QubesTemplateVm +from qubes.qubes import QubesAppVm, QubesTemplateVm, QubesHVm from qubes.qubes import QubesException from optparse import OptionParser; import sys @@ -62,6 +62,8 @@ def main(): dst_vm = qvm_collection.add_new_appvm(name=dstname, template_vm=src_vm.template_vm, updateable=src_vm.updateable, label=src_vm.label, dir_path=options.dir_path) + elif isinstance(src_vm, QubesHVm): + dst_vm = qvm_collection.add_new_hvm(name=dstname, label=src_vm.label) else: print >> sys.stderr, "ERROR: Clone not supported for this type of VM" exit(1) From b7b5260bd76735614e77ead44630ef816713f290 Mon Sep 17 00:00:00 2001 From: Marek Marczykowski Date: Fri, 2 Mar 2012 16:29:56 +0100 Subject: [PATCH 8/8] dom0/core: preserve MAC setting on clone --- dom0/qvm-core/qubes.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dom0/qvm-core/qubes.py b/dom0/qvm-core/qubes.py index 92d09164..4c56cf2d 100755 --- a/dom0/qvm-core/qubes.py +++ b/dom0/qvm-core/qubes.py @@ -937,7 +937,8 @@ class QubesVm(object): def get_clone_attrs(self): return ['kernel', 'uses_default_kernel', 'netvm_vm', 'uses_default_netvm', \ - 'memory', 'maxmem', 'kernelopts', 'uses_default_kernelopts', 'services', 'vcpus'] + 'memory', 'maxmem', 'kernelopts', 'uses_default_kernelopts', 'services', 'vcpus', \ + '_mac'] def clone_attrs(self, src_vm): for prop in self.get_clone_attrs():