summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorRadostin Stoyanov <rstoyanov1@gmail.com>2017-08-26 21:42:06 +0100
committerRadostin Stoyanov <rstoyanov1@gmail.com>2017-08-28 17:00:19 +0100
commite98df68ded6515c3f6d8f8854697c15407aff000 (patch)
treec6f0f087b89b6f0966025e7441fb0f01765dc25c /src
parent2bb76d45f14ae1bce4240bd3c1eeabcb44f663ac (diff)
downloadvirt-bootstrap.git-e98df68ded6515c3f6d8f8854697c15407aff000.tar.gz
virt-bootstrap.git-e98df68ded6515c3f6d8f8854697c15407aff000.tar.xz
virt-bootstrap.git-e98df68ded6515c3f6d8f8854697c15407aff000.zip
Add virt-builder source
Add implementation for virt-builder source which aims to create container root file system from VM image build with virt-builder. Usage examples: $ virt-bootstrap virt-builder://fedora-25 /tmp/foo $ virt-bootstrap virt-builder://ubuntu-16.04 /tmp/bar --root-password secret $ virt-bootstrap virt-builder://fedora-25 /tmp/foo -f qcow2 --idmap 0:1000:10 $ sudo virt-bootstrap virt-builder://fedora-25 /tmp/foo --idmap 0:1000:10 Tests are also introduced along with the implementation. They cover creation of root file system and UID/GID mapping for 'dir' and 'qcow2' output format by mocking the build_image() method to avoid the time consuming call to virt-builder which might also require network connection with function which creates dummy disk image. Setting root password is handled by virt-builder and hence the introduced test only ensures that the password string is passed correctly.
Diffstat (limited to 'src')
-rw-r--r--src/virtBootstrap/sources/__init__.py1
-rw-r--r--src/virtBootstrap/sources/virt_builder_source.py154
-rwxr-xr-xsrc/virtBootstrap/virt_bootstrap.py2
3 files changed, 156 insertions, 1 deletions
diff --git a/src/virtBootstrap/sources/__init__.py b/src/virtBootstrap/sources/__init__.py
index e891e9b..be6b25c 100644
--- a/src/virtBootstrap/sources/__init__.py
+++ b/src/virtBootstrap/sources/__init__.py
@@ -24,3 +24,4 @@ sources - Class definitions which process container image or
from virtBootstrap.sources.file_source import FileSource
from virtBootstrap.sources.docker_source import DockerSource
+from virtBootstrap.sources.virt_builder_source import VirtBuilderSource
diff --git a/src/virtBootstrap/sources/virt_builder_source.py b/src/virtBootstrap/sources/virt_builder_source.py
new file mode 100644
index 0000000..628544f
--- /dev/null
+++ b/src/virtBootstrap/sources/virt_builder_source.py
@@ -0,0 +1,154 @@
+# -*- coding: utf-8 -*-
+# Authors: Radostin Stoyanov <rstoyanov1@gmail.com>
+#
+# Copyright (C) 2017 Radostin Stoyanov
+#
+# 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 3 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, see <http://www.gnu.org/licenses/>.
+
+"""
+VirtBuilderSource aim is to extract the root file system from VM image
+build with virt-builder from template.
+"""
+
+import os
+import logging
+import subprocess
+import tempfile
+
+import guestfs
+from virtBootstrap import utils
+
+
+# pylint: disable=invalid-name
+# Create logger
+logger = logging.getLogger(__name__)
+
+
+class VirtBuilderSource(object):
+ """
+ Extract root file system from image build with virt-builder.
+ """
+ def __init__(self, **kwargs):
+ """
+ Create container rootfs by building VM from virt-builder template
+ and extract the rootfs.
+
+ @param uri: Template name
+ @param fmt: Format used to store the output [dir, qcow2]
+ @param uid_map: Mappings for UID of files in rootfs
+ @param gid_map: Mappings for GID of files in rootfs
+ @param root_password: Root password to set in rootfs
+ @param progress: Instance of the progress module
+ """
+ # Parsed URIs:
+ # - "virt-builder:///<template>"
+ # - "virt-builder://<template>"
+ # - "virt-builder:/<template>"
+ self.template = kwargs['uri'].netloc or kwargs['uri'].path[1:]
+ self.output_format = kwargs.get('fmt', utils.DEFAULT_OUTPUT_FORMAT)
+ self.uid_map = kwargs.get('uid_map', [])
+ self.gid_map = kwargs.get('gid_map', [])
+ self.root_password = kwargs.get('root_password', None)
+ self.progress = kwargs['progress'].update_progress
+
+ def build_image(self, output_file):
+ """
+ Build VM from virt-builder template
+ """
+ cmd = ['virt-builder', self.template,
+ '-o', output_file,
+ '--no-network',
+ '--delete', '/dev/*',
+ '--delete', '/boot/*',
+ # Comment out all lines in fstab
+ '--edit', '/etc/fstab:s/^/#/']
+ if self.root_password is not None:
+ cmd += ['--root-password', "password:%s" % self.root_password]
+ self.run_builder(cmd)
+
+ def run_builder(self, cmd):
+ """
+ Execute virt-builder command
+ """
+ subprocess.check_call(cmd)
+
+ def unpack(self, dest):
+ """
+ Build image and extract root file system
+
+ @param dest: Directory path where output files will be stored.
+ """
+
+ with tempfile.NamedTemporaryFile(prefix='bootstrap_') as tmp_file:
+ if self.output_format == 'dir':
+ self.progress("Building image", value=0, logger=logger)
+ self.build_image(tmp_file.name)
+ self.progress("Extracting rootfs", value=50, logger=logger)
+ g = guestfs.GuestFS(python_return_dict=True)
+ g.add_drive_opts(tmp_file.name, readonly=False, format='raw')
+ g.launch()
+
+ # Get the device with file system
+ root_dev = g.inspect_os()
+ if not root_dev:
+ raise Exception("No file system was found")
+ g.mount(root_dev[0], '/')
+
+ # Extract file system to destination directory
+ g.copy_out('/', dest)
+
+ g.umount('/')
+ g.shutdown()
+
+ self.progress("Extraction completed successfully!",
+ value=100, logger=logger)
+ logger.info("Files are stored in: %s", dest)
+
+ elif self.output_format == 'qcow2':
+ output_file = os.path.join(dest, 'layer-0.qcow2')
+
+ self.progress("Building image", value=0, logger=logger)
+ self.build_image(tmp_file.name)
+
+ self.progress("Extracting rootfs", value=50, logger=logger)
+ g = guestfs.GuestFS(python_return_dict=True)
+ g.add_drive_opts(tmp_file.name, readonly=True, format='raw')
+ # Create qcow2 disk image
+ g.disk_create(
+ filename=output_file,
+ format='qcow2',
+ size=os.path.getsize(tmp_file.name)
+ )
+ g.add_drive_opts(output_file, readonly=False, format='qcow2')
+ g.launch()
+ # Get the device with file system
+ root_dev = g.inspect_os()
+ if not root_dev:
+ raise Exception("No file system was found")
+ output_dev = g.list_devices()[1]
+ # Copy the file system to the new qcow2 disk
+ g.copy_device_to_device(root_dev[0], output_dev, sparse=True)
+ g.shutdown()
+
+ # UID/GID mapping
+ if self.uid_map or self.gid_map:
+ logger.info("Mapping UID/GID")
+ utils.map_id_in_image(1, dest, self.uid_map, self.gid_map)
+
+ self.progress("Extraction completed successfully!", value=100,
+ logger=logger)
+ logger.info("Image is stored in: %s", output_file)
+
+ else:
+ raise Exception("Unknown format:" + self.output_format)
diff --git a/src/virtBootstrap/virt_bootstrap.py b/src/virtBootstrap/virt_bootstrap.py
index e387842..fe95f5e 100755
--- a/src/virtBootstrap/virt_bootstrap.py
+++ b/src/virtBootstrap/virt_bootstrap.py
@@ -62,7 +62,7 @@ def get_source(source_type):
Get object which match the source type
"""
try:
- class_name = "%sSource" % source_type.capitalize()
+ class_name = "%sSource" % source_type.title().replace('-', '')
clazz = getattr(sources, class_name)
return clazz
except Exception: