diff --git a/image_creator/disk.py b/image_creator/disk.py
index 2fc7a73d6d1a6448ed980cbc787c8c3a628faf5d..0b02d0504006dcb36afa4f064536dc57452ba015 100644
--- a/image_creator/disk.py
+++ b/image_creator/disk.py
@@ -18,7 +18,7 @@
"""Module hosting the Disk class."""
from image_creator.util import get_command, try_fail_repeat, free_space, \
- FatalError, image_info
+ FatalError, create_snapshot
from image_creator.bundle_volume import BundleVolume
from image_creator.image import Image
@@ -156,32 +156,18 @@ class Disk(object):
self.out.warn("Snapshotting ignored for host bundling mode.")
return self.file
- mode = os.stat(self.file).st_mode
- device = self.file if stat.S_ISBLK(mode) else self._losetup(self.file)
- size = int(blockdev('--getsz', device))
- virtual_size = (image_info(device)['virtual-size'] + 511) // 512
-
- # If the virtual_size is bigger than the block device size, we need to
- # create a bigger block device padded with zeros, otherwise QEMU will
- # freeze if it tries to enlarge the underlying file.
- if virtual_size > size:
- zeros = virtual_size - size
- tablefd, table = tempfile.mkstemp()
- try:
- try:
- os.write(tablefd, '0 %d linear %s 0\n' % (size, device))
- os.write(tablefd, '%d %d zero\n' % (size, zeros))
- finally:
- os.close(tablefd)
- padded = 'snf-image-creator-padded-%s' % uuid.uuid4().hex
- dmsetup('create', padded, table)
- self._add_cleanup(try_fail_repeat, dmsetup, 'remove', padded)
- finally:
- os.unlink(table)
- device = "/dev/mapper/%s" % padded
- size = virtual_size
-
self.out.output("Snapshotting media source ...", False)
+
+ # Create a qcow2 snapshot for image files
+ if not stat.S_ISBLK(os.stat(self.file).st_mode):
+ snapshot = create_snapshot(self.file, self.tmp)
+ self._add_cleanup(os.unlink, snapshot)
+ self.out.success('done')
+ return snapshot
+
+ # Create a device-mapper snapshot for block devices
+ size = int(blockdev('--getsz', self.file))
+
cowfd, cow = tempfile.mkstemp(dir=self.tmp)
os.close(cowfd)
self._add_cleanup(os.unlink, cow)
@@ -193,8 +179,8 @@ class Disk(object):
tablefd, table = tempfile.mkstemp()
try:
try:
- os.write(tablefd,
- "0 %d snapshot %s %s n 8\n" % (size, device, cowdev))
+ os.write(tablefd, "0 %d snapshot %s %s n 8\n" %
+ (size, self.file, cowdev))
finally:
os.close(tablefd)
diff --git a/image_creator/image.py b/image_creator/image.py
index 88074090b4bf20e5ce724d53068df409570197f0..da3777df927ee34c828b19044972863e2514ebe9 100644
--- a/image_creator/image.py
+++ b/image_creator/image.py
@@ -224,6 +224,7 @@ class Image(object):
def __enter__(self):
return img.device if img.info['format'] == 'raw' else \
img.nbd.connect(readonly)
+
def __exit__(self, exc_type, exc_value, traceback):
if img.info['format'] != 'raw':
img.nbd.disconnect()
diff --git a/image_creator/util.py b/image_creator/util.py
index 4d0fb34dff833bb22608c2396ed30e903589052b..dfae024be68350b58352b8c00e491ef9905e75d7 100644
--- a/image_creator/util.py
+++ b/image_creator/util.py
@@ -24,6 +24,7 @@ import time
import os
import re
import json
+import tempfile
from sh import qemu_img
from sh import qemu_nbd
from sh import modprobe
@@ -40,6 +41,16 @@ def image_info(image):
return json.loads(str(info))
+def create_snapshot(source, target_dir):
+ """Returns a qcow2 snapshot of an image file"""
+
+ snapfd, snap = tempfile.mkstemp(prefix='snapshot-', dir=target_dir)
+ os.close(snapfd)
+ qemu_img('create', '-f', 'qcow2', '-o',
+ 'backing_file=%s' % os.path.abspath(source), snap)
+ return snap
+
+
def get_command(command):
"""Return a file system binary command"""
def find_sbin_command(command, exception):