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()