summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/virtBootstrap/sources/docker_source.py10
-rw-r--r--src/virtBootstrap/sources/file_source.py14
-rw-r--r--src/virtBootstrap/utils.py146
3 files changed, 102 insertions, 68 deletions
diff --git a/src/virtBootstrap/sources/docker_source.py b/src/virtBootstrap/sources/docker_source.py
index 2dadb42..a6ea3e6 100644
--- a/src/virtBootstrap/sources/docker_source.py
+++ b/src/virtBootstrap/sources/docker_source.py
@@ -272,7 +272,15 @@ class DockerSource(object):
elif self.output_format == 'qcow2':
self.progress("Extracting container layers into qcow2 images",
value=50, logger=logger)
- utils.extract_layers_in_qcow2(self.layers, dest, self.progress)
+
+ img = utils.BuildImage(
+ layers=self.layers,
+ dest=dest,
+ progress=self.progress
+ )
+ img.create_base_layer()
+ img.create_backing_chains()
+
else:
raise Exception("Unknown format:" + self.output_format)
diff --git a/src/virtBootstrap/sources/file_source.py b/src/virtBootstrap/sources/file_source.py
index 412db8a..69f024c 100644
--- a/src/virtBootstrap/sources/file_source.py
+++ b/src/virtBootstrap/sources/file_source.py
@@ -64,14 +64,16 @@ class FileSource(object):
utils.untar_layers(layer, dest, self.progress)
elif self.output_format == 'qcow2':
- # Remove the old path
- file_name = os.path.basename(self.path)
- qcow2_file = os.path.realpath('{}/{}.qcow2'.format(dest,
- file_name))
-
self.progress("Extracting files into qcow2 image", value=0,
logger=logger)
- utils.create_qcow2(self.path, qcow2_file)
+
+ img = utils.BuildImage(
+ layers=layer,
+ dest=dest,
+ progress=self.progress
+ )
+ img.create_base_layer()
+
else:
raise Exception("Unknown format:" + self.output_format)
diff --git a/src/virtBootstrap/utils.py b/src/virtBootstrap/utils.py
index be9133c..5fb5d8c 100644
--- a/src/virtBootstrap/utils.py
+++ b/src/virtBootstrap/utils.py
@@ -33,6 +33,7 @@ import tempfile
import logging
import re
+import guestfs
import passlib.hosts
# pylint: disable=invalid-name
@@ -42,6 +43,8 @@ logger = logging.getLogger(__name__)
DEFAULT_OUTPUT_FORMAT = 'dir'
# Default virtual size of qcow2 image
DEF_QCOW2_SIZE = '5G'
+DEF_BASE_IMAGE_SIZE = 5 * 1024 * 1024 * 1024
+
if os.geteuid() == 0:
LIBVIRT_CONN = "lxc:///"
DEFAULT_IMG_DIR = "/var/lib/virt-bootstrap/docker_images"
@@ -51,6 +54,88 @@ else:
DEFAULT_IMG_DIR += "/.local/share/virt-bootstrap/docker_images"
+class BuildImage(object):
+ """
+ Use guestfs-python to create qcow2 disk images.
+ """
+
+ def __init__(self, layers, dest, progress):
+ """
+ @param tar_files: Tarballs to be converted to qcow2 images
+ @param dest: Directory where the qcow2 images will be created
+ @param progress: Instance of the progress module
+ """
+ self.g = guestfs.GuestFS(python_return_dict=True)
+ self.layers = layers
+ self.nlayers = len(layers)
+ self.dest = dest
+ self.progress = progress
+ self.qcow2_files = []
+
+ def create_base_layer(self, fmt='qcow2', size=DEF_BASE_IMAGE_SIZE):
+ """
+ Create and format qcow2 disk image which represnts the base layer.
+ """
+ self.qcow2_files = [os.path.join(self.dest, 'layer-0.qcow2')]
+ self.progress("Creating base layer", logger=logger)
+ self.g.disk_create(self.qcow2_files[0], fmt, size)
+ self.g.add_drive(self.qcow2_files[0], format=fmt)
+ self.g.launch()
+ self.progress("Formating disk image", logger=logger)
+ self.g.mkfs("ext3", '/dev/sda')
+ self.extract_layer(0, '/dev/sda')
+ # Shutdown qemu instance to avoid hot-plugging of devices.
+ self.g.shutdown()
+
+ def create_backing_chains(self):
+ """
+ Convert other layers to qcow2 images linked as backing chains.
+ """
+ for i in range(1, self.nlayers):
+ self.qcow2_files.append(
+ os.path.join(self.dest, 'layer-%d.qcow2' % i)
+ )
+ self.progress(
+ "Creating image (%d/%d)" % (i + 1, self.nlayers),
+ logger=logger
+ )
+ self.g.disk_create(
+ filename=self.qcow2_files[i],
+ format='qcow2',
+ size=-1,
+ backingfile=self.qcow2_files[i - 1],
+ backingformat='qcow2'
+ )
+ self.g.add_drive(self.qcow2_files[i], format='qcow2')
+ self.g.launch()
+ devices = self.g.list_devices()
+ # Tar-in layers (skip the base layer)
+ for index in range(1, self.nlayers):
+ self.extract_layer(index, devices[index - 1])
+ self.g.shutdown()
+
+ def extract_layer(self, index, dev):
+ """
+ Extract tarball of layer to device
+ """
+ tar_file, tar_size = self.layers[index]
+ log_layer_extract(
+ tar_file, tar_size, index + 1, self.nlayers, self.progress
+ )
+ self.tar_in(dev, tar_file)
+
+ def tar_in(self, dev, tar_file):
+ """
+ Common pattern used to tar-in archive into image
+ """
+ self.g.mount(dev, '/')
+ # Restore extended attributes, SELinux contexts and POSIX ACLs
+ # from tar file.
+ self.g.tar_in(tar_file, '/', get_compression_type(tar_file),
+ xattrs=True, selinux=True, acls=True)
+ self.g.umount('/')
+
+
def get_compression_type(tar_file):
"""
Get compression type of tar file.
@@ -212,67 +297,6 @@ def get_mime_type(path):
return output.read().decode('utf-8').split()[1]
-def create_qcow2(tar_file, layer_file, backing_file=None, size=DEF_QCOW2_SIZE):
- """
- Create qcow2 image from tarball.
- """
- qemu_img_cmd = ["qemu-img", "create", "-f", "qcow2", layer_file, size]
-
- if not backing_file:
- logger.info("Creating base qcow2 image")
- execute(qemu_img_cmd)
-
- logger.info("Formatting qcow2 image")
- execute(['virt-format',
- '--format=qcow2',
- '--partition=none',
- '--filesystem=ext3',
- '-a', layer_file])
- else:
- # Add backing chain
- qemu_img_cmd.insert(2, "-b")
- qemu_img_cmd.insert(3, backing_file)
-
- logger.info("Creating qcow2 image with backing chain")
- execute(qemu_img_cmd)
-
- # Extract tarball using "tar-in" command from libguestfs
- tar_in_cmd = ["guestfish",
- "-a", layer_file,
- '-m', '/dev/sda',
- 'tar-in', tar_file, "/"]
-
- # Check if tarball is compressed
- compression = get_compression_type(tar_file)
- if compression is not None:
- tar_in_cmd.append('compress:' + compression)
-
- # Execute virt-tar-in command
- execute(tar_in_cmd)
-
-
-def extract_layers_in_qcow2(layers_list, dest_dir, progress):
- """
- Extract docker layers in qcow2 images with backing chains.
- """
- qcow2_backing_file = None
-
- nlayers = len(layers_list)
- for index, layer in enumerate(layers_list):
- tar_file, tar_size = layer
- log_layer_extract(tar_file, tar_size, index + 1, nlayers, progress)
-
- # Name format for the qcow2 image
- qcow2_layer_file = "{}/layer-{}.qcow2".format(dest_dir, index)
- # Create the image layer
- create_qcow2(tar_file, qcow2_layer_file, qcow2_backing_file)
- # Keep the file path for the next layer
- qcow2_backing_file = qcow2_layer_file
-
- # Update progress value
- progress(value=(float(index + 1) / nlayers * 50) + 50)
-
-
def get_image_dir(no_cache=False):
"""
Get the directory where image layers are stored.