--- 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 _______________________________________________ virt-tools-list mailing list virt-tools-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/virt-tools-list