From 6db640dbfee52049cbe827f98da31b983092dee5 Mon Sep 17 00:00:00 2001 From: Marek Marczykowski Date: Sun, 27 Feb 2011 00:06:46 +0100 Subject: [PATCH 1/6] 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..d7d77eac --- /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) 2010 Joanna Rutkowska +# +# 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() From 2fa145ab362da9632078d60113f79262bd4de901 Mon Sep 17 00:00:00 2001 From: Marek Marczykowski Date: Mon, 28 Feb 2011 18:52:55 +0100 Subject: [PATCH 2/6] block-snapshot fixes --- common/block-snapshot | 51 ++++++++++++++++++++++++++----------------- 1 file changed, 31 insertions(+), 20 deletions(-) mode change 100644 => 100755 common/block-snapshot diff --git a/common/block-snapshot b/common/block-snapshot old mode 100644 new mode 100755 index a08a10c5..a668935c --- a/common/block-snapshot +++ b/common/block-snapshot @@ -7,7 +7,6 @@ dir=$(dirname "$0") . "$dir/block-common.sh" - get_dev() { dev=$1 @@ -26,10 +25,10 @@ get_dev() { fatal "Unable to lookup $file: dev: $devnum inode: $inode" fi - dev_list=$(losetup -a | grep ' \[0*'${dev}'\]:'${inode} | cut -d : -f 1) - for dev in $dev_list; do + dev_list=$(losetup -a | grep ' \[0*'${devnum}'\]:'${inode} | cut -d : -f 1) + for loopdev in $dev_list; do # found existing loop to this file - echo $dev + echo $loopdev return done @@ -141,40 +140,52 @@ case "$command" in fatal "No device node to remove" fi + if [ ! -e "$node" ]; then + fatal "Device $node does not exists" + fi + claim_lock "block" use_count=$(dmsetup info $node|grep Open|awk '{print $3}') # do not remove snapshot if snapshot origin is still present - if [ "${node/snapshot/}" != "$node" -a - -e "/dev/mapper/origin-$(echo $node|cut -d- -f2)" ]; then - ((use_count++)) + if [ "${node/snapshot/}" != "$node" -a -e "/dev/mapper/origin-$(echo $node|cut -d- -f2)" ]; then + use_count=1 fi if [ "$use_count" -gt 0 ]; then log info "Device $node still in use - not removing" + release_lock "block" exit 0 fi # get list of used (loop) devices - deps="$(dmsetup deps $node | cut -d: -f2 | sed -e 's#(7, \([0-9]+\))#/dev/loop\1#g')" + deps="$(dmsetup deps $node | cut -d: -f2 | sed -e 's#(7, \([0-9]\+\))#/dev/loop\1#g')" - # remove unused snapshots - for snap in /dev/mapper/snapshot-$(echo $node|cut -d- -f2); do - use_count=$(dmsetup info $snap|grep Open|awk '{print $3}') - if [ $use_count -eq 0 ]; then - # unused snapshot - remove it - deps="$deps $(dmsetup deps $snap | cut -d: -f2 | sed -e 's#(7, \([0-9]+\))#/dev/loop\1#g')" - dmsetup remove $snap - fi - done - - do_or_die dmsetup remove $node + # if this is origin + if [ "${node/origin/}" != "$node" ]; then + # remove unused snapshots + for snap in /dev/mapper/snapshot-$(echo $node|cut -d- -f2)-*; do + use_count=$(dmsetup info $snap|grep Open|awk '{print $3}') + if [ "$use_count" -eq 0 ]; then + # unused snapshot - remove it + deps="$deps $(dmsetup deps $snap | cut -d: -f2 | sed -e 's#(7, \([0-9]\+\))#/dev/loop\1#g')" + log debug "Removing $snap" + dmsetup remove $snap + fi + done + fi + + if [ -e $node ]; then + log debug "Removing $node" + dmsetup remove $node + fi # try to free loop devices for dev in $deps; do if [ -b "$dev" ]; then - losetup -d $dev 2> /dev/null + log debug "Removing $dev" + losetup -d $dev || true 2> /dev/null fi done From 5d4a27a0bba04ac36d65abee3ec707d62bde8e80 Mon Sep 17 00:00:00 2001 From: Marek Marczykowski Date: Wed, 2 Mar 2011 11:35:36 +0100 Subject: [PATCH 3/6] Add backup files to gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 4d22dcb6..228b7abe 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ rpm/ *.pyc +*~ From 143f1519a8dbfa93b5a6c83565aff0fbb22f3bf2 Mon Sep 17 00:00:00 2001 From: Marek Marczykowski Date: Wed, 2 Mar 2011 11:52:19 +0100 Subject: [PATCH 4/6] Add typo in qvm-template-commit As in original classes... --- dom0/qvm-tools/qvm-template-commit | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dom0/qvm-tools/qvm-template-commit b/dom0/qvm-tools/qvm-template-commit index d7d77eac..438fbb6a 100755 --- a/dom0/qvm-tools/qvm-template-commit +++ b/dom0/qvm-tools/qvm-template-commit @@ -46,7 +46,7 @@ def main(): print "A VM with the name '{0}' does not exist in the system.".format(vmname) exit(1) - if not vm.is_templatevm(): + if not vm.is_templete(): print "A VM '{0}' is not template.".format(vmname) exit(1) From cf39d17fa03efb787e1b7f9e5fec80ac0632e065 Mon Sep 17 00:00:00 2001 From: Marek Marczykowski Date: Wed, 2 Mar 2011 11:55:54 +0100 Subject: [PATCH 5/6] Add BR to core-appvm.spec --- rpm_spec/core-appvm.spec | 2 ++ 1 file changed, 2 insertions(+) diff --git a/rpm_spec/core-appvm.spec b/rpm_spec/core-appvm.spec index c9a009d1..e901a12c 100644 --- a/rpm_spec/core-appvm.spec +++ b/rpm_spec/core-appvm.spec @@ -34,6 +34,8 @@ URL: http://www.qubes-os.org Requires: /usr/bin/xenstore-read Requires: fedora-release = 13 Requires: /usr/bin/mimeopen +BuildRequires: gcc +BuildRequires: xen-devel Provides: qubes-core-vm %define _builddir %(pwd)/appvm From c3bf11062fcada300947bc2da4a640374a7a8e99 Mon Sep 17 00:00:00 2001 From: Marek Marczykowski Date: Wed, 2 Mar 2011 11:58:22 +0100 Subject: [PATCH 6/6] gitignore files - add build products --- .gitignore | 2 ++ appvm/.gitignore | 3 +++ common/.gitignore | 1 + dom0/restore/.gitignore | 3 +++ 4 files changed, 9 insertions(+) create mode 100644 appvm/.gitignore create mode 100644 common/.gitignore create mode 100644 dom0/restore/.gitignore diff --git a/.gitignore b/.gitignore index 228b7abe..e2440642 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ rpm/ *.pyc +*.pyo *~ +*.o diff --git a/appvm/.gitignore b/appvm/.gitignore new file mode 100644 index 00000000..edd6d099 --- /dev/null +++ b/appvm/.gitignore @@ -0,0 +1,3 @@ +qubes_add_pendrive_script +qubes_penctl +qvm-open-in-dvm diff --git a/common/.gitignore b/common/.gitignore new file mode 100644 index 00000000..03034a95 --- /dev/null +++ b/common/.gitignore @@ -0,0 +1 @@ +meminfo-writer diff --git a/dom0/restore/.gitignore b/dom0/restore/.gitignore new file mode 100644 index 00000000..0535ab8a --- /dev/null +++ b/dom0/restore/.gitignore @@ -0,0 +1,3 @@ +qubes_restore +xenfreepages +xenstore-watch