diff options
author | Radostin Stoyanov <rstoyanov1@gmail.com> | 2017-06-16 11:08:13 +0100 |
---|---|---|
committer | Cédric Bosdonnat <cbosdonnat@suse.com> | 2017-06-16 18:03:59 +0200 |
commit | 31e38232b7857bbf190a1234976e93f521fbffe0 (patch) | |
tree | a56ef3d188b50be6cdb24f3c9e0d070aace16e9e /sources.py | |
parent | 8a3e52019315a00f8fee222ecdc803a914608068 (diff) | |
download | virt-bootstrap.git-31e38232b7857bbf190a1234976e93f521fbffe0.tar.gz virt-bootstrap.git-31e38232b7857bbf190a1234976e93f521fbffe0.tar.xz virt-bootstrap.git-31e38232b7857bbf190a1234976e93f521fbffe0.zip |
Move source files in src/virtBootstrap
Preparatory commit before setup.py introduction.
Diffstat (limited to 'sources.py')
-rw-r--r-- | sources.py | 276 |
1 files changed, 0 insertions, 276 deletions
diff --git a/sources.py b/sources.py deleted file mode 100644 index 015e0b4..0000000 --- a/sources.py +++ /dev/null @@ -1,276 +0,0 @@ -#!/usr/bin/python -# Authors: Cedric Bosdonnat <cbosdonnat@suse.com> -# -# Copyright (C) 2017 SUSE, Inc. -# -# 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 2 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, write to the Free Software -# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -# - -import hashlib -import json -import shutil -import tempfile -import getpass -import os -import logging -from subprocess import call, CalledProcessError, PIPE, Popen - -# Default virtual size of qcow2 image -DEF_QCOW2_SIZE = '5G' -# default_image_dir - Path where Docker images (tarballs) will be stored -if os.geteuid() == 0: - virt_sandbox_connection = "lxc:///" - default_image_dir = "/var/lib/virt-bootstrap/docker_images" -else: - virt_sandbox_connection = "qemu:///session" - default_image_dir = \ - os.environ['HOME'] + "/.local/share/virt-bootstrap/docker_images" - - -def checksum(path, sum_type, sum_expected): - algorithm = getattr(hashlib, sum_type) - try: - fd = open(path, 'rb') - content = fd.read() - fd.close() - - actual = algorithm(content).hexdigest() - return actual == sum_expected - except Exception: - return False - - -def safe_untar(src, dest): - # Extract tarball in LXC container for safety - virt_sandbox = ['virt-sandbox', - '-c', virt_sandbox_connection, - '-m', 'host-bind:/mnt=' + dest] # Bind destination folder - - # Compression type is auto detected from tar - # Exclude files under /dev to avoid "Cannot mknod: Operation not permitted" - params = ['--', '/bin/tar', 'xf', src, '-C', '/mnt', '--exclude', 'dev/*'] - if call(virt_sandbox + params) != 0: - logging.error(_('virt-sandbox exit with non-zero code. ' - 'Please check if "libvirtd" is running.')) - - -def get_layer_info(digest, image_dir): - sum_type, sum_value = digest.split(':') - layer_file = "{}/{}.tar".format(image_dir, sum_value) - return (sum_type, sum_value, layer_file) - - -def untar_layers(layers_list, image_dir, dest_dir): - for layer in layers_list: - sum_type, sum_value, layer_file = get_layer_info(layer['digest'], - image_dir) - logging.info('Untar layer file: ({}) {}'.format(sum_type, layer_file)) - - # Verify the checksum - if not checksum(layer_file, sum_type, sum_value): - raise Exception("Digest not matching: " + layer['digest']) - - # Extract layer tarball into destination directory - safe_untar(layer_file, dest_dir) - - -def get_mime_type(path): - """ - Get the mime type of a file. - """ - return Popen(["/usr/bin/file", "--mime-type", path], - stdout=PIPE).communicate()[0].split()[1] - - -def create_qcow2(tar_file, layer_file, backing_file=None, size=DEF_QCOW2_SIZE): - qemu_img_cmd = ["qemu-img", "create", "-f", "qcow2", layer_file, size] - - if not backing_file: - logging.info("Create base qcow2 image") - check_call(qemu_img_cmd) - - logging.info("Format qcow2 image") - check_call(['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) - - logging.info("Crate qcow2 image with backing chain") - check_call(qemu_img_cmd) - - # Get mime type of archive - mime_tar_file = get_mime_type(tar_file) - logging.debug("Detected mime type of archive: %s", mime_tar_file) - - # Extract tarball using "tar-in" command from libguestfs - tar_in_cmd = ["guestfish", - "-a", layer_file, - '-m', '/dev/sda', - 'tar-in', tar_file, "/"] - - compression_fmts = {'x-gzip': 'gzip', 'gzip': 'gzip', - 'x-xz': 'xz', - 'x-bzip2': 'bzip2', - 'x-compress': 'compress', - 'x-lzop': 'lzop'} - - # Check if tarball is compressed - mime_parts = mime_tar_file.split('/') - if mime_parts[0] == 'application' and \ - mime_parts[1] in compression_fmts: - tar_in_cmd.append('compress:' + compression_fmts[mime_parts[1]]) - - # Execute virt-tar-in command - check_call(tar_in_cmd) - - -def extract_layers_in_qcow2(layers_list, image_dir, dest_dir): - qcow2_backing_file = None - - for index, layer in enumerate(layers_list): - # Get layer file information - sum_type, sum_value, tar_file = \ - get_layer_info(layer['digest'], image_dir) - - logging.info('Untar layer file: ({}) {}'.format(sum_type, tar_file)) - - # Verify the checksum - if not checksum(tar_file, sum_type, sum_value): - raise Exception("Digest not matching: " + layer['digest']) - - # 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 - - -class FileSource: - def __init__(self, url, username, password, fmt, insecure, no_cache): - self.path = url.path - self.output_format = fmt - - def unpack(self, dest): - ''' - Safely extract root filesystem from tarball - - @param dest: Directory path where the files to be extraced - ''' - if self.output_format == 'dir': - logging.info("Extracting files into destination directory") - safe_untar(self.path, dest) - - 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)) - - logging.info("Extracting files into qcow2 image") - create_qcow2(self.path, qcow2_file) - else: - raise Exception("Unknown format:" + self.output_format) - - logging.info("Extraction completed successfully!") - logging.info("Files are stored in: " + dest) - - -class DockerSource: - def __init__(self, url, username, password, fmt, insecure, no_cache): - ''' - Bootstrap root filesystem from Docker registry - - @param url: Address of source registry - @param username: Username to access source registry - @param password: Password to access source registry - @param fmt: Format used to store image [dir, qcow2] - @param insecure: Do not require HTTPS and certificate verification - @param no_cache: Whether to store downloaded images or not - ''' - - self.registry = url.netloc - self.image = url.path - self.username = username - self.password = password - self.output_format = fmt - self.insecure = insecure - self.no_cache = no_cache - if self.image and not self.image.startswith('/'): - self.image = '/' + self.image - self.url = "docker://" + self.registry + self.image - - def unpack(self, dest): - ''' - Extract image files from Docker image - - @param dest: Directory path where the files to be extraced - ''' - - if self.no_cache: - tmp_dest = tempfile.mkdtemp('virt-bootstrap') - images_dir = tmp_dest - else: - if not os.path.exists(default_image_dir): - os.makedirs(default_image_dir) - images_dir = default_image_dir - - try: - # Run skopeo copy into a tmp folder - # Note: we don't want to expose --src-cert-dir to users as - # they should place the certificates in the system - # folders for broader enablement - skopeo_copy = ["skopeo", "copy", self.url, "dir:"+images_dir] - - if self.insecure: - skopeo_copy.append('--src-tls-verify=false') - if self.username: - if not self.password: - self.password = getpass.getpass() - skopeo_copy.append('--src-creds={}:{}'.format(self.username, - self.password)) - # Run "skopeo copy" command - check_call(skopeo_copy) - - # Get the layers list from the manifest - mf = open(images_dir+"/manifest.json", "r") - manifest = json.load(mf) - - # Layers are in order - root layer first - # Reference: - # https://github.com/containers/image/blob/master/image/oci.go#L100 - if self.output_format == 'dir': - untar_layers(manifest['layers'], images_dir, dest) - elif self.output_format == 'qcow2': - extract_layers_in_qcow2(manifest['layers'], images_dir, dest) - else: - raise Exception("Unknown format:" + self.output_format) - - except Exception: - raise - - else: - logging.info("Download and extract completed!") - logging.info("Files are stored in: " + dest) - - finally: - # Clean up - if self.no_cache: - shutil.rmtree(tmp_dest) |