[virt-tools-list] [virt-bootstrap] [PATCH v4 11/26] Create qcow2 images with python-guestfs

Radostin Stoyanov rstoyanov1 at gmail.com
Thu Aug 3 13:13:09 UTC 2017


---
 src/virtBootstrap/utils.py | 96 ++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 96 insertions(+)

diff --git a/src/virtBootstrap/utils.py b/src/virtBootstrap/utils.py
index e05a83f..19aaa17 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,99 @@ else:
     DEFAULT_IMG_DIR += "/.local/share/virt-bootstrap/docker_images"
 
 
+class Build_QCOW2_Image(object):
+    """
+    Create qcow2 image with backing chains from list of tar files.
+    """
+    def __init__(self, **kwargs):
+        """
+        Initialize guestfs
+
+        @param tar_files: List of paths to tar files from which to the rootfs
+        @param dest: Destination directory where qcow2 images will be stored
+        @param progress: Instance of the progress module
+        """
+        self.tar_files = kwargs['tar_files']
+        if not isinstance(self.tar_files, list):
+            raise ValueError(
+                'tar_files must be list not %s' % type(self.tar_files)
+            )
+        self.progress = kwargs['progress']
+        self.fmt = 'qcow2'
+        self.qcow2_files = [os.path.join(kwargs['dest'], 'layer-%s.qcow2' % i)
+                            for i in range(len(self.tar_files))]
+
+        self.g = guestfs.GuestFS(python_return_dict=True)
+        self.create_base_qcow2_layer(self.tar_files[0], self.qcow2_files[0])
+        if len(self.tar_files) > 1:
+            self.create_backing_chains()
+        self.g.shutdown()
+
+    def create_disk(self, qcow2_file, backingfile=None, readonly=False):
+        """
+        Create and add qcow2 disk image.
+        """
+        if backingfile is not None:
+            size = -1
+            backingformat = self.fmt
+        else:
+            size = DEF_BASE_IMAGE_SIZE
+            backingformat = None
+
+        self.g.disk_create(qcow2_file, self.fmt, size, backingfile,
+                           backingformat)
+        self.g.add_drive_opts(qcow2_file, readonly, self.fmt)
+
+    def tar_in(self, tar_file, dev):
+        """
+        Extract tar file in disk device.
+        """
+        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)
+        # Shutdown guestfs instance to avoid hot-plugging of devices.
+        self.g.umount('/')
+
+    def create_base_qcow2_layer(self, tar_file, qcow2_file):
+        """
+        Create and format base qcow2 layer.
+
+        Do this separatelly when extracting multiple layers to avoid
+        hot-plugging of devices.
+        """
+        self.progress("Creating base layer", logger=logger)
+        self.create_disk(qcow2_file)
+        self.g.launch()
+        dev = self.g.list_devices()[0]
+        self.progress("Formating disk image", logger=logger)
+        self.g.mkfs("ext3", dev)
+        self.tar_in(tar_file, dev)
+        self.progress("Extracting content of base layer", logger=logger)
+        self.g.shutdown()
+
+    def create_backing_chains(self):
+        """
+        Create backing chains for all layers after following the first
+        and tar-in the content.
+        """
+        for i in range(1, len(self.tar_files)):
+            self.progress("Creating layer %d" % i, logger=logger)
+            self.create_disk(
+                self.qcow2_files[i],
+                backingfile=self.qcow2_files[i - 1]
+            )
+
+        self.g.launch()
+        devices = self.g.list_devices()
+        # Iterate trough tar files of layers and skip the base layer
+        for i, tar_file in enumerate(self.tar_files[1:]):
+            self.progress("Extracting content of layer %d" % (i + 1),
+                          logger=logger)
+            self.tar_in(tar_file, devices[i])
+
+
 def get_compression_type(tar_file):
     """
     Get compression type of tar file.
-- 
2.13.3




More information about the virt-tools-list mailing list