core: split VM images handling to separate class

This will ease handling different types of VMM (which can require
different image types, location etc).
This commit is contained in:
Marek Marczykowski-Górecki 2013-07-29 03:51:39 +02:00
parent d5cb05fdc6
commit 0a1f3d0a44
8 changed files with 386 additions and 235 deletions

View File

@ -347,6 +347,12 @@ class QubesVm(object):
else:
assert self.root_img is not None, "Missing root_img for standalone VM!"
self.storage = defaults["storage_class"](self)
if hasattr(self, 'kernels_dir'):
self.storage.modules_img = os.path.join(self.kernels_dir,
"modules.img")
self.storage.modules_img_rw = self.kernel is None
# fire hooks
for hook in self.hooks_init:
hook(self)
@ -854,33 +860,17 @@ class QubesVm(object):
return qubes.qubesutils.get_disk_usage(self.private_img)
def get_private_img_sz(self):
if not os.path.exists(self.private_img):
return 0
return os.path.getsize(self.private_img)
return self.storage.get_private_img_sz()
def resize_private_img(self, size):
assert size >= self.get_private_img_sz(), "Cannot shrink private.img"
f_private = open (self.private_img, "a+b")
f_private.truncate (size)
f_private.close ()
# resize the image
self.storage.resize_private_img(size)
# and then the filesystem
retcode = 0
if self.is_running():
# find loop device
p = subprocess.Popen (["sudo", "losetup", "--associated", self.private_img],
stdout=subprocess.PIPE)
result = p.communicate()
m = re.match(r"^(/dev/loop\d+):\s", result[0])
if m is None:
raise QubesException("ERROR: Cannot find loop device!")
loop_dev = m.group(1)
# resize loop device
subprocess.check_call(["sudo", "losetup", "--set-capacity", loop_dev])
retcode = self.run("while [ \"`blockdev --getsize64 /dev/xvdb`\" -lt {0} ]; do ".format(size) +
"head /dev/xvdb > /dev/null; sleep 0.2; done; resize2fs /dev/xvdb", user="root", wait=True)
if retcode != 0:
@ -983,23 +973,6 @@ class QubesVm(object):
for hook in self.hooks_create_xenstore_entries:
hook(self, xid=xid)
def _format_disk_dev(self, path, script, vdev, rw=True, type="disk", domain=None):
template = " <disk type='block' device='{type}'>\n" \
" <driver name='phy'/>\n" \
" <source dev='{path}'/>\n" \
" <target dev='{vdev}' bus='xen'/>\n" \
"{params}" \
" </disk>\n"
params = ""
if not rw:
params += " <readonly/>\n"
if domain:
params += " <domain name='%s'/>\n" % domain
if script:
params += " <script path='%s'/>\n" % script
return template.format(path=path, vdev=vdev, type=type,
params=params)
def _format_net_dev(self, ip, mac, backend):
template = " <interface type='ethernet'>\n" \
" <mac address='{mac}'/>\n" \
@ -1023,17 +996,6 @@ class QubesVm(object):
slot=dev_match.group(2),
fun=dev_match.group(3))
def get_rootdev(self):
if self.template:
return self._format_disk_dev(
"{dir}/root.img:{dir}/root-cow.img".format(
dir=self.template.dir_path),
"block-snapshot", "xvda", False)
else:
return self._format_disk_dev(
"{dir}/root.img".format(dir=self.dir_path),
None, "xvda", True)
def get_config_params(self):
args = {}
args['name'] = self.name
@ -1070,17 +1032,7 @@ class QubesVm(object):
args['netdev'] = ''
args['disable_network1'] = '<!--';
args['disable_network2'] = '-->';
args['rootdev'] = self.get_rootdev()
args['privatedev'] = \
self._format_disk_dev("{dir}/private.img".format(dir=self.dir_path),
None, "xvdb", True)
args['volatiledev'] = \
self._format_disk_dev("{dir}/volatile.img".format(dir=self.dir_path),
None, "xvdc", True)
if hasattr(self, 'kernel'):
args['otherdevs'] = \
self._format_disk_dev("{dir}/modules.img".format(dir=self.kernels_dir),
None, "xvdd", self.kernel is None)
args.update(self.storage.get_config_params())
if hasattr(self, 'kernelopts'):
args['kernelopts'] = self.kernelopts
if self.debug:
@ -1139,37 +1091,9 @@ class QubesVm(object):
if dry_run:
return
old_umask = os.umask(002)
if verbose:
print >> sys.stderr, "--> Creating directory: {0}".format(self.dir_path)
os.mkdir (self.dir_path)
if verbose:
print >> sys.stderr, "--> Creating the VM config file: {0}".format(self.conf_file)
template_priv = source_template.private_img
if verbose:
print >> sys.stderr, "--> 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))
self.storage.create_on_disk(verbose, source_template)
if self.updateable:
template_root = source_template.root_img
if verbose:
print >> sys.stderr, "--> Copying the template's root image: {0}".\
format(template_root)
# We prefer to use Linux's cp, because it nicely handles sparse files
retcode = subprocess.call (["cp", template_root, self.root_img])
if retcode != 0:
raise IOError ("Error while copying {0} to {1}".\
format(template_root, self.root_img))
kernels_dir = source_template.kernels_dir
if verbose:
print >> sys.stderr, "--> Copying the kernel (set kernel \"none\" to use it): {0}".\
@ -1180,15 +1104,10 @@ class QubesVm(object):
shutil.copy(os.path.join(kernels_dir, f),
os.path.join(self.dir_path, vm_files["kernels_subdir"], f))
# Create volatile.img
self.reset_volatile_storage(source_template = source_template, verbose=verbose)
if verbose:
print >> sys.stderr, "--> Creating icon symlink: {0} -> {1}".format(self.icon_path, self.label.icon_path)
os.symlink (self.label.icon_path, self.icon_path)
os.umask(old_umask)
# fire hooks
for hook in self.hooks_create_on_disk:
hook(self, verbose, source_template=source_template)
@ -1224,29 +1143,7 @@ class QubesVm(object):
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))
self.storage.clone_disk_files(src_vm, verbose)
if src_vm.icon_path is not None and self.icon_path is not None:
if os.path.exists (src_vm.dir_path):
@ -1268,20 +1165,7 @@ class QubesVm(object):
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 self.updateable and not os.path.exists (self.root_img):
raise QubesException (
"VM root image file doesn't exist: {0}".\
format(self.root_img))
if not os.path.exists (self.private_img):
raise QubesException (
"VM private image file doesn't exist: {0}".\
format(self.private_img))
self.storage.verify_files()
if not os.path.exists (os.path.join(self.kernels_dir, 'vmlinuz')):
raise QubesException (
@ -1293,49 +1177,12 @@ class QubesVm(object):
"VM initramfs does not exists: {0}".\
format(os.path.join(self.kernels_dir, 'initramfs')))
if not os.path.exists (os.path.join(self.kernels_dir, 'modules.img')):
raise QubesException (
"VM kernel modules image does not exists: {0}".\
format(os.path.join(self.kernels_dir, 'modules.img')))
# fire hooks
for hook in self.hooks_verify_files:
hook(self)
return True
def reset_volatile_storage(self, source_template = None, verbose = False):
assert not self.is_running(), "Attempt to clean volatile image of running VM!"
if source_template is None:
source_template = self.template
# Only makes sense on template based VM
if source_template is None:
# For StandaloneVM create it only if not already exists (eg after backup-restore)
if not os.path.exists(self.volatile_img):
if verbose:
print >> sys.stderr, "--> Creating volatile image: {0}...".format (self.volatile_img)
f_root = open (self.root_img, "r")
f_root.seek(0, os.SEEK_END)
root_size = f_root.tell()
f_root.close()
subprocess.check_call([system_path["prepare_volatile_img_cmd"], self.volatile_img, str(root_size / 1024 / 1024)])
return
if verbose:
print >> sys.stderr, "--> Cleaning volatile image: {0}...".format (self.volatile_img)
if dry_run:
return
if os.path.exists (self.volatile_img):
os.remove (self.volatile_img)
if hasattr(source_template, 'clean_volatile_img'):
retcode = subprocess.call (["tar", "xf", source_template.clean_volatile_img, "-C", self.dir_path])
if retcode != 0:
raise IOError ("Error while unpacking {0} to {1}".\
format(source_template.clean_volatile_img, self.volatile_img))
def remove_from_disk(self):
if dry_run:
return
@ -1344,7 +1191,7 @@ class QubesVm(object):
for hook in self.hooks_remove_from_disk:
hook(self)
shutil.rmtree (self.dir_path)
self.storage.remove_from_disk()
def write_firewall_conf(self, conf):
defaults = self.get_firewall_conf()
@ -1727,7 +1574,7 @@ class QubesVm(object):
print >> sys.stderr, "--> Starting NetVM {0}...".format(self.netvm.name)
self.netvm.start(verbose = verbose, start_guid = start_guid, notify_function = notify_function)
self.reset_volatile_storage(verbose=verbose)
self.storage.prepare_for_vm_startup(verbose=verbose)
if verbose:
print >> sys.stderr, "--> Loading the VM (type = {0})...".format(self.type)

View File

@ -51,6 +51,7 @@ class QubesTemplateVm(QubesVm):
attrs_config['rootcow_img'] = {
'func': lambda x: os.path.join(self.dir_path, vm_files["rootcow_img"]) }
# Clean image for root-cow and swap (AppVM side)
# TODO: not used anymore - clean up when all references removed
attrs_config['clean_volatile_img'] = {
'func': lambda x: os.path.join(self.dir_path, vm_files["clean_volatile_img"]) }
@ -76,35 +77,12 @@ class QubesTemplateVm(QubesVm):
def get_firewall_defaults(self):
return { "rules": list(), "allow": False, "allowDns": False, "allowIcmp": False, "allowYumProxy": True }
def get_rootdev(self):
return self._format_disk_dev(
"{dir}/root.img:{dir}/root-cow.img".format(
dir=self.dir_path),
"block-origin", "xvda", True)
def clone_disk_files(self, src_vm, verbose):
if dry_run:
return
super(QubesTemplateVm, self).clone_disk_files(src_vm=src_vm, verbose=verbose)
if verbose:
print >> sys.stderr, "--> Copying the template's clean volatile image:\n{0} ==>\n{1}".\
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_vm.clean_volatile_img, self.clean_volatile_img])
if retcode != 0:
raise IOError ("Error while copying {0} to {1}".\
format(src_vm.clean_volatile_img, self.clean_volatile_img))
if verbose:
print >> sys.stderr, "--> Copying the template's volatile image:\n{0} ==>\n{1}".\
format(self.clean_volatile_img, self.volatile_img)
# We prefer to use Linux's cp, because it nicely handles sparse files
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_img, self.volatile_img))
# Create root-cow.img
self.commit_changes(verbose=verbose)
@ -112,42 +90,10 @@ class QubesTemplateVm(QubesVm):
super(QubesTemplateVm, self).post_rename(old_name)
old_dirpath = os.path.join(os.path.dirname(self.dir_path), old_name)
# TODO: clean_volatile_img not used anymore
self.clean_volatile_img = self.clean_volatile_img.replace(old_dirpath, self.dir_path)
self.rootcow_img = self.rootcow_img.replace(old_dirpath, self.dir_path)
def verify_files(self):
if dry_run:
return
super(QubesTemplateVm, self).verify_files()
if not os.path.exists (self.volatile_img):
raise QubesException (
"VM volatile image file doesn't exist: {0}".\
format(self.volatile_img))
if not os.path.exists (self.clean_volatile_img):
raise QubesException (
"Clean VM volatile image file doesn't exist: {0}".\
format(self.clean_volatile_img))
return True
def reset_volatile_storage(self, verbose = False):
assert not self.is_running(), "Attempt to clean volatile image of running Template VM!"
if verbose:
print >> sys.stderr, "--> Cleaning volatile image: {0}...".format (self.volatile_img)
if dry_run:
return
if os.path.exists (self.volatile_img):
os.remove (self.volatile_img)
retcode = subprocess.call (["tar", "xf", self.clean_volatile_img, "-C", self.dir_path])
if retcode != 0:
raise IOError ("Error while unpacking {0} to {1}".\
format(self.template.clean_volatile_img, self.volatile_img))
def commit_changes (self, verbose = False):
if not vmm.offline_mode:
@ -158,16 +104,7 @@ class QubesTemplateVm(QubesVm):
if dry_run:
return
if os.path.exists (self.rootcow_img):
os.rename (self.rootcow_img, self.rootcow_img + '.old')
old_umask = os.umask(002)
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()
os.umask(old_umask)
self.storage.commit_template_changes()
register_qubes_vm_class(QubesTemplateVm)

View File

@ -6,6 +6,7 @@ SETTINGS_SUFFIX = $(BACKEND_VMM)-$(OS)
all:
python -m compileall .
python -O -m compileall .
make -C storage all
install:
ifndef PYTHON_SITEPATH
@ -30,3 +31,4 @@ ifneq ($(BACKEND_VMM),)
test -r settings-$(SETTINGS_SUFFIX).pyo && \
cp settings-$(SETTINGS_SUFFIX).pyo $(DESTDIR)$(PYTHON_QUBESPATH)/settings.pyo
endif
make -C storage install

View File

@ -94,6 +94,11 @@ defaults = {
'dom0_update_check_interval': 6*3600,
'private_img_size': 2*1024*1024*1024,
'root_img_size': 10*1024*1024*1024,
'storage_class': None,
# how long (in sec) to wait for VMs to shutdown,
# before killing them (when used qvm-run with --wait option),
'shutdown_counter_max': 60,

21
core/storage/Makefile Normal file
View File

@ -0,0 +1,21 @@
OS ?= Linux
PYTHON_QUBESPATH = $(PYTHON_SITEPATH)/qubes
all:
python -m compileall .
python -O -m compileall .
install:
ifndef PYTHON_SITEPATH
$(error PYTHON_SITEPATH not defined)
endif
mkdir -p $(DESTDIR)$(PYTHON_QUBESPATH)/storage
cp __init__.py $(DESTDIR)$(PYTHON_QUBESPATH)/storage
cp __init__.py[co] $(DESTDIR)$(PYTHON_QUBESPATH)/storage
ifneq ($(BACKEND_VMM),)
if [ -r $(BACKEND_VMM).py ]; then \
cp $(BACKEND_VMM).py $(DESTDIR)$(PYTHON_QUBESPATH)/storage && \
cp $(BACKEND_VMM).py[co] $(DESTDIR)$(PYTHON_QUBESPATH)/storage; \
fi
endif

182
core/storage/__init__.py Normal file
View File

@ -0,0 +1,182 @@
#!/usr/bin/python2
#
# The Qubes OS Project, http://www.qubes-os.org
#
# Copyright (C) 2013 Marek Marczykowski <marmarek@invisiblethingslab.com>
#
# 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 __future__ import absolute_import
import os
import os.path
import re
import shutil
import subprocess
import sys
from qubes.qubes import vm_files,system_path,defaults
from qubes.qubes import QubesException
import qubes.qubesutils
class QubesVmStorage(object):
"""
Class for handling VM virtual disks. This is base class for all other
implementations, mostly with Xen on Linux in mind.
"""
def __init__(self, vm,
private_img_size = None,
root_img_size = None,
modules_img = None,
modules_img_rw = False):
self.vm = vm
self.vmdir = vm.dir_path
if private_img_size:
self.private_img_size = private_img_size
else:
self.private_img_size = defaults['private_img_size']
if root_img_size:
self.root_img_size = root_img_size
else:
self.root_img_size = defaults['root_img_size']
self.private_img = os.path.join(self.vmdir, vm_files["private_img"])
if self.vm.template:
self.root_img = self.vm.template.root_img
else:
self.root_img = os.path.join(self.vmdir, vm_files["root_img"])
self.volatile_img = os.path.join(self.vmdir, vm_files["volatile_img"])
# For now compute this path still in QubesVm
self.modules_img = modules_img
self.modules_img_rw = modules_img_rw
def get_config_params(self):
raise NotImplementedError
def _copy_file(self, source, destination):
"""
Effective file copy, preserving sparse files etc.
"""
# TODO: Windows support
# We prefer to use Linux's cp, because it nicely handles sparse files
retcode = subprocess.call (["cp", source, destination])
if retcode != 0:
raise IOError ("Error while copying {0} to {1}".\
format(source, destination))
def get_disk_utilization(self):
return qubes.qubesutils.get_disk_usage(self.vmdir)
def get_disk_utilization_private_img(self):
return qubes.qubesutils.get_disk_usage(self.private_img)
def get_private_img_sz(self):
if not os.path.exists(self.private_img):
return 0
return os.path.getsize(self.private_img)
def resize_private_img(self, size):
raise NotImplementedError
def create_on_disk_private_img(self, verbose, source_template = None):
raise NotImplementedError
def create_on_disk_root_img(self, verbose, source_template = None):
raise NotImplementedError
def create_on_disk(self, verbose, source_template = None):
if source_template is None:
source_template = self.vm.template
old_umask = os.umask(002)
if verbose:
print >> sys.stderr, "--> Creating directory: {0}".format(self.vmdir)
os.mkdir (self.vmdir)
self.create_on_disk_private_img(verbose, source_template)
self.create_on_disk_root_img(verbose, source_template)
self.reset_volatile_storage(verbose, source_template)
os.umask(old_umask)
def clone_disk_files(self, src_vm, verbose):
if verbose:
print >> sys.stderr, "--> Creating directory: {0}".format(self.vmdir)
os.mkdir (self.vmdir)
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)
self._copy_file(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)
self._copy_file(src_vm.root_img, self.root_img)
# TODO: modules?
def verify_files(self):
if not os.path.exists (self.vmdir):
raise QubesException (
"VM directory doesn't exist: {0}".\
format(self.vmdir))
if self.vm.updateable and not os.path.exists (self.root_img):
raise QubesException (
"VM root image file doesn't exist: {0}".\
format(self.root_img))
if not os.path.exists (self.private_img):
raise QubesException (
"VM private image file doesn't exist: {0}".\
format(self.private_img))
if self.modules_img is not None:
if not os.path.exists(self.modules_img):
raise QubesException (
"VM kernel modules image does not exists: {0}".\
format(self.modules_img))
def remove_from_disk(self):
shutil.rmtree (self.vmdir)
def reset_volatile_storage(self, verbose = False, source_template = None):
if source_template is None:
source_template = self.vm.template
# Re-create only for template based VMs
if source_template is not None:
if os.path.exists(self.volatile_img):
os.remove(self.volatile_img)
# For StandaloneVM create it only if not already exists (eg after backup-restore)
if not os.path.exists(self.volatile_img):
if verbose:
print >> sys.stderr, "--> Creating volatile image: {0}...".\
format(self.volatile_img)
subprocess.check_call([system_path["prepare_volatile_img_cmd"],
self.volatile_img, str(self.root_img_size / 1024 / 1024)])
def prepare_for_vm_startup(self, verbose):
self.reset_volatile_storage(verbose=verbose)
pass

154
core/storage/xen.py Normal file
View File

@ -0,0 +1,154 @@
#!/usr/bin/python2
#
# The Qubes OS Project, http://www.qubes-os.org
#
# Copyright (C) 2013 Marek Marczykowski <marmarek@invisiblethingslab.com>
#
# 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 __future__ import absolute_import
import os
import os.path
import subprocess
import sys
from qubes.storage import QubesVmStorage
from qubes.qubes import QubesException
class QubesXenVmStorage(QubesVmStorage):
"""
Class for VM storage of Xen VMs.
"""
def __init__(self, vm, **kwargs):
super(QubesXenVmStorage, self).__init__(vm, **kwargs)
self.root_dev = "xvda"
self.private_dev = "xvdb"
self.volatile_dev = "xvdc"
self.modules_dev = "xvdd"
def _format_disk_dev(self, path, script, vdev, rw=True, type="disk", domain=None):
template = " <disk type='block' device='{type}'>\n" \
" <driver name='phy'/>\n" \
" <source dev='{path}'/>\n" \
" <target dev='{vdev}' bus='xen'/>\n" \
"{params}" \
" </disk>\n"
params = ""
if not rw:
params += " <readonly/>\n"
if domain:
params += " <domain name='%s'/>\n" % domain
if script:
params += " <script path='%s'/>\n" % script
return template.format(path=path, vdev=vdev, type=type,
params=params)
def _get_rootdev(self):
if self.vm.is_template():
return self._format_disk_dev(
"{dir}/root.img:{dir}/root-cow.img".format(
dir=self.vmdir),
"block-origin", self.root_dev, True)
elif self.vm.template:
return self._format_disk_dev(
"{dir}/root.img:{dir}/root-cow.img".format(
dir=self.vm.template.vmdir),
"block-snapshot", self.root_dev, False)
else:
return self._format_disk_dev(
"{dir}/root.img".format(dir=self.vmdir),
None, self.root_dev, True)
def get_config_params(self):
args = {}
args['rootdev'] = self._get_rootdev()
args['privatedev'] = \
self._format_disk_dev(self.private_img,
None, self.private_dev, True)
args['volatiledev'] = \
self._format_disk_dev(self.volatile_img,
None, self.volatile_dev, True)
if self.modules_img is not None:
args['otherdevs'] = \
self._format_disk_dev(self.modules_img,
None, self.modules_dev, self.modules_img_rw)
return args
def create_on_disk_private_img(self, verbose, source_template = None):
if source_template:
template_priv = source_template.private_img
if verbose:
print >> sys.stderr, "--> Copying the template's private image: {0}".\
format(template_priv)
self._copy_file(template_priv, self.private_img)
else:
f_private = open (self.private_img, "a+b")
f_private.truncate (self.private_img_size)
f_private.close ()
def create_on_disk_root_img(self, verbose, source_template = None):
if source_template:
if not self.vm.updateable:
# just use template's disk
return
else:
template_root = source_template.root_img
if verbose:
print >> sys.stderr, "--> Copying the template's root image: {0}".\
format(template_root)
self._copy_file(template_root, self.root_img)
else:
f_root = open (self.root_img, "a+b")
f_root.truncate (self.root_img_size)
f_root.close ()
def resize_private_img(self, size):
f_private = open (self.private_img, "a+b")
f_private.truncate (size)
f_private.close ()
# find loop device if any
p = subprocess.Popen (["sudo", "losetup", "--associated", self.private_img],
stdout=subprocess.PIPE)
result = p.communicate()
m = re.match(r"^(/dev/loop\d+):\s", result[0])
if m is not None:
loop_dev = m.group(1)
# resize loop device
subprocess.check_call(["sudo", "losetup", "--set-capacity", loop_dev])
def commit_template_changes(self):
assert self.vm.is_template()
# TODO: move rootcow_img to this class; the same for vm.is_outdated()
if os.path.exists (self.vm.rootcow_img):
os.rename (self.vm.rootcow_img, self.vm.rootcow_img + '.old')
old_umask = os.umask(002)
f_cow = open (self.vm.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()
os.umask(old_umask)

View File

@ -197,6 +197,9 @@ fi
%{python_sitearch}/qubes/backup.py
%{python_sitearch}/qubes/backup.pyc
%{python_sitearch}/qubes/backup.pyo
%{python_sitearch}/qubes/storage/*.py
%{python_sitearch}/qubes/storage/*.pyc
%{python_sitearch}/qubes/storage/*.pyo
%{python_sitearch}/qubes/qmemman*.py*
%{python_sitearch}/qubes/modules/0*.py*
%{python_sitearch}/qubes/modules/__init__.py*