From 14aaccbc5f811cdcfc3b430b7d582db7586df24a Mon Sep 17 00:00:00 2001 From: Marek Marczykowski Date: Sun, 27 Feb 2011 00:06:46 +0100 Subject: [PATCH] Update TemplateVM with running AppVM: part 2 - support for template modify in qvm-core - tool for commit changes to template --- dom0/qvm-core/qubes.py | 41 ++++++++++++++---- dom0/qvm-tools/qvm-prefs | 2 + dom0/qvm-tools/qvm-template-commit | 67 ++++++++++++++++++++++++++++++ 3 files changed, 101 insertions(+), 9 deletions(-) create mode 100755 dom0/qvm-tools/qvm-template-commit diff --git a/dom0/qvm-core/qubes.py b/dom0/qvm-core/qubes.py index 55ab2d6b..cc8bfd7f 100755 --- a/dom0/qvm-core/qubes.py +++ b/dom0/qvm-core/qubes.py @@ -624,13 +624,14 @@ class QubesTemplateVm(QubesVm): dir_path = kwargs["dir_path"] - # TempleteVM doesn't use root-cow image if root_img is not None and os.path.isabs(root_img): self.root_img = root_img else: self.root_img = dir_path + "/" + ( root_img if root_img is not None else default_root_img) + self.rootcow_img = dir_path + "/" + default_rootcow_img + if private_img is not None and os.path.isabs(private_img): self.private_img = private_img else: @@ -711,6 +712,14 @@ class QubesTemplateVm(QubesVm): 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 "--> Copying the template's root COW image:\n{0} ==>\n{1}".\ + format(src_template_vm.rootcow_img, self.rootcow_img) + # We prefer to use Linux's cp, because it nicely handles sparse files + retcode = subprocess.call (["cp", src_template_vm.rootcow_img, self.rootcow_img]) if retcode != 0: raise IOError ("Error while copying {0} to {1}".\ format(src_template_vm.root_img, self.root_img)) @@ -779,13 +788,29 @@ class QubesTemplateVm(QubesVm): if not self.is_updateable(): raise QubesException ("Cannot start Template VM that is marked \"nonupdatable\"") - # First ensure that none of our appvms is running: - for appvm in self.appvms.values(): - if appvm.is_running(): - raise QubesException ("Cannot start TemplateVM when one of its AppVMs is running!") + # TODO?: check if none of running appvms are outdated return super(QubesTemplateVm, self).start(debug_console=debug_console, verbose=verbose) + def commit_changes (self): + + assert not self.is_running(), "Attempt to commit changes on running Template VM!" + + print "--> Commiting template updates... COW: {0}...".format (self.rootcow_img) + + if dry_run: + return + if os.path.exists (self.rootcow_img): + os.remove (self.rootcow_img) + + + f_cow = open (self.rootcow_img, "w") + f_root = open (self.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 create_xml_element(self): element = xml.etree.ElementTree.Element( "QubesTemplateVm", @@ -795,6 +820,7 @@ class QubesTemplateVm(QubesVm): conf_file=self.conf_file, appvms_conf_file=self.appvms_conf_file, root_img=self.root_img, + rootcow_img=self.rootcow_img, private_img=self.private_img, uses_default_netvm=str(self.uses_default_netvm), netvm_qid=str(self.netvm_vm.qid) if self.netvm_vm is not None else "none", @@ -1194,10 +1220,6 @@ class QubesAppVm(QubesVm): if self.is_running(): raise QubesException("VM is already running!") - # First ensure that our template is *not* running: - if self.template_vm.is_running(): - raise QubesException ("Cannot start AppVM when its template is running!") - if not self.is_updateable(): self.reset_cow_storage() self.reset_swap_cow_storage() @@ -1728,3 +1750,4 @@ class QubesDaemonPidfile(object): self.remove_pidfile() +# vim:sw=4:et: diff --git a/dom0/qvm-tools/qvm-prefs b/dom0/qvm-tools/qvm-prefs index f2c77367..f9c129ff 100755 --- a/dom0/qvm-tools/qvm-prefs +++ b/dom0/qvm-tools/qvm-prefs @@ -42,6 +42,8 @@ def do_list(vm): print fmt.format ("config", vm.conf_file) if not vm.is_appvm(): print fmt.format ("root img", vm.root_img) + if vm.is_templete(): + print fmt.format ("root COW img", vm.rootcow_img) if vm.is_appvm(): print fmt.format ("root img", vm.template_vm.root_img) print fmt.format ("root COW img", vm.rootcow_img) diff --git a/dom0/qvm-tools/qvm-template-commit b/dom0/qvm-tools/qvm-template-commit new file mode 100755 index 00000000..d0b574bd --- /dev/null +++ b/dom0/qvm-tools/qvm-template-commit @@ -0,0 +1,67 @@ +#!/usr/bin/python2.6 +# +# The Qubes OS Project, http://www.qubes-os.org +# +# Copyright (C) 2011 Marek Marczykowski +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# + +from qubes.qubes import QubesVmCollection +from qubes.qubes import QubesException +from optparse import OptionParser +import subprocess + +qubes_guid_path = "/usr/bin/qubes_guid" + +def main(): + usage = "usage: %prog [options] " + parser = OptionParser (usage) + + (options, args) = parser.parse_args () + if (len (args) != 1): + parser.error ("You must specify VM name!") + vmname = args[0] + + qvm_collection = QubesVmCollection() + qvm_collection.lock_db_for_reading() + qvm_collection.load() + qvm_collection.unlock_db() + + vm = qvm_collection.get_vm_by_name(vmname) + if vm is None: + print "A VM with the name '{0}' does not exist in the system.".format(vmname) + exit(1) + + if not vm.is_templatevm(): + print "A VM '{0}' is not template.".format(vmname) + exit(1) + + if vm.is_running(): + print "You must stop VM first." + exit(1) + + try: + vm.verify_files() + vm.commit_changes() + except (IOError, OSError, QubesException) as err: + print "ERROR: {0}".format(err) + exit (1) + + exit (0) + + +main()