The virt-sandbox-image tool and its supporting code has been split into a separate libvirt-sandbox-image GIT repository. This allows its build system and distribution to work in the normal python way, and have a release lifecycle independent of the main libvirt-sandbox package. https://libvirt.org/git/?p=libvirt-sandbox-image.git;a=summary Signed-off-by: Daniel P. Berrangé <berrange@xxxxxxxxxx> --- autobuild.sh | 3 +- bin/virt-sandbox-image | 8 - configure.ac | 4 - libvirt-sandbox.spec.in | 9 - libvirt-sandbox/image/Makefile.am | 10 - libvirt-sandbox/image/__init__.py | 0 libvirt-sandbox/image/cli.py | 282 ----------- libvirt-sandbox/image/sources/Makefile.am | 10 - libvirt-sandbox/image/sources/__init__.py | 0 libvirt-sandbox/image/sources/base.py | 160 ------- libvirt-sandbox/image/sources/docker.py | 690 --------------------------- libvirt-sandbox/image/sources/virtbuilder.py | 109 ----- libvirt-sandbox/image/template.py | 133 ------ m4/virt-win32.m4 | 5 - 14 files changed, 1 insertion(+), 1422 deletions(-) delete mode 100755 bin/virt-sandbox-image delete mode 100644 libvirt-sandbox/image/Makefile.am delete mode 100644 libvirt-sandbox/image/__init__.py delete mode 100644 libvirt-sandbox/image/cli.py delete mode 100644 libvirt-sandbox/image/sources/Makefile.am delete mode 100644 libvirt-sandbox/image/sources/__init__.py delete mode 100644 libvirt-sandbox/image/sources/base.py delete mode 100755 libvirt-sandbox/image/sources/docker.py delete mode 100755 libvirt-sandbox/image/sources/virtbuilder.py delete mode 100644 libvirt-sandbox/image/template.py diff --git a/autobuild.sh b/autobuild.sh index 9cd4ca2..c176fea 100755 --- a/autobuild.sh +++ b/autobuild.sh @@ -54,8 +54,7 @@ if [ -x /usr/bin/i686-pc-mingw32-gcc ]; then ../configure \ --build=$(uname -m)-pc-linux \ --host=i686-pc-mingw32 \ - --prefix="$AUTOBUILD_INSTALL_ROOT/i686-pc-mingw32/sys-root/mingw" \ - --without-python + --prefix="$AUTOBUILD_INSTALL_ROOT/i686-pc-mingw32/sys-root/mingw" make make install diff --git a/bin/virt-sandbox-image b/bin/virt-sandbox-image deleted file mode 100755 index 61346ef..0000000 --- a/bin/virt-sandbox-image +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- - -from libvirt_sandbox.image import cli -import sys - -if __name__ == '__main__': - sys.exit(cli.main()) diff --git a/configure.ac b/configure.ac index 28305d2..19d4523 100644 --- a/configure.ac +++ b/configure.ac @@ -130,13 +130,9 @@ dnl Should be in m4/virt-gettext.m4 but intltoolize is too dnl dumb to find it there IT_PROG_INTLTOOL([0.35.0]) -AM_PATH_PYTHON([3]) - AC_OUTPUT(Makefile libvirt-sandbox/Makefile libvirt-sandbox/tests/Makefile - libvirt-sandbox/image/Makefile - libvirt-sandbox/image/sources/Makefile bin/Makefile examples/Makefile docs/Makefile diff --git a/libvirt-sandbox.spec.in b/libvirt-sandbox.spec.in index 125a361..0374d9e 100644 --- a/libvirt-sandbox.spec.in +++ b/libvirt-sandbox.spec.in @@ -36,14 +36,7 @@ BuildRequires: zlib-devel >= 1.2.0, zlib-static BuildRequires: libtirpc-devel BuildRequires: rpcgen %endif -Requires: python3-rpm -# For virsh lxc-enter-namespace command -Requires: libvirt-client >= %{libvirt_version} -Requires: systemd >= 198 -Requires: pygobject3-base -Requires: libselinux-python3 Requires: %{name}-libs = %{version}-%{release} -Requires: %{_bindir}/virt-builder %package libs Group: Development/Libraries @@ -102,8 +95,6 @@ rm -rf $RPM_BUILD_ROOT %files %defattr(-,root,root,-) %{_bindir}/virt-sandbox -%{_bindir}/virt-sandbox-image -%{python3_sitelib}/libvirt_sandbox %{_mandir}/man1/virt-sandbox.1* %files libs -f %{name}.lang diff --git a/libvirt-sandbox/image/Makefile.am b/libvirt-sandbox/image/Makefile.am deleted file mode 100644 index 60afbf3..0000000 --- a/libvirt-sandbox/image/Makefile.am +++ /dev/null @@ -1,10 +0,0 @@ - -SUBDIRS = sources - -pythonsandboxdir = $(pythondir)/libvirt_sandbox -pythonsandbox_DATA = __init__.py - -pythonimagedir = $(pythondir)/libvirt_sandbox/image -pythonimage_DATA = __init__.py cli.py template.py - -EXTRA_DIST = $(pythonimage_DATA) diff --git a/libvirt-sandbox/image/__init__.py b/libvirt-sandbox/image/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/libvirt-sandbox/image/cli.py b/libvirt-sandbox/image/cli.py deleted file mode 100644 index 605183c..0000000 --- a/libvirt-sandbox/image/cli.py +++ /dev/null @@ -1,282 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -# Authors: Daniel P. Berrange <berrange@xxxxxxxxxx> -# Eren Yagdiran <erenyagdiran@xxxxxxxxx> -# -# Copyright (C) 2013-2015 Red Hat, Inc. -# Copyright (C) 2015 Universitat Politècnica de Catalunya. -# -# 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 argparse -import gettext -import hashlib -import json -import os -import os.path -import re -import shutil -import sys -import subprocess -import random -import string - -from libvirt_sandbox.image import template - -if os.geteuid() == 0: - default_template_dir = "/var/lib/libvirt/templates" - default_image_dir = "/var/lib/libvirt/images" -else: - default_template_dir = os.environ['HOME'] + "/.local/share/libvirt/templates" - default_image_dir = os.environ['HOME'] + "/.local/share/libvirt/images" - -debug = False -verbose = False - -gettext.bindtextdomain("libvirt-sandbox", "/usr/share/locale") -gettext.textdomain("libvirt-sandbox") -try: - gettext.install("libvirt-sandbox", - localedir="/usr/share/locale", - codeset = 'utf-8') -except IOError: - import __builtin__ - __builtin__.__dict__['_'] = unicode - - -def debug(msg): - sys.stderr.write(msg) - -def info(msg): - sys.stdout.write(msg) - -def get_template_dir(args): - tmpl = template.Template.from_uri(args.template) - return "%s/%s" % (args.template_dir, tmpl.source) - -def purge(args): - tmpl = template.Template.from_uri(args.template) - source = tmpl.get_source_impl() - source.delete_template(template=tmpl, - templatedir=get_template_dir(args)) - -def prepare(args): - tmpl = template.Template.from_uri(args.template) - source = tmpl.get_source_impl() - source.create_template(template=tmpl, - templatedir=get_template_dir(args), - connect=args.connect) - -def random_domain_name(tmpl): - randomid = ''.join(random.choice(string.ascii_lowercase) for i in range(10)) - return re.sub('[^a-z0-9-]', '_', tmpl.path[1:], re.I) + ":" + randomid - -def run(args): - if args.connect is not None: - check_connect(args.connect) - - tmpl = template.Template.from_uri(args.template) - source = tmpl.get_source_impl() - template_dir = get_template_dir(args) - - # Create the template image if needed - if not source.has_template(tmpl, template_dir): - prepare(args) - - name = args.name - if name is None: - name = random_domain_name(tmpl) - - diskfile = source.get_disk(template=tmpl, - templatedir=template_dir, - imagedir=args.image_dir, - sandboxname=name) - - commandToRun = source.get_command(tmpl, template_dir, args.args) - if len(commandToRun) == 0: - commandToRun = ["/bin/sh"] - cmd = ['virt-sandbox', '--name', name] - if args.connect is not None: - cmd.append("-c") - cmd.append(args.connect) - params = ['-m','host-image:/=%s,format=qcow2' % diskfile] - - networkArgs = args.network - if networkArgs is not None: - params.append('-N') - params.append(networkArgs) - - allEnvs = source.get_env(tmpl, template_dir) - envArgs = args.env - if envArgs is not None: - allEnvs = allEnvs + envArgs - for env in allEnvs: - envsplit = env.split("=") - envlen = len(envsplit) - if envlen == 2: - params.append("--env") - params.append(env) - else: - pass - - cmd = cmd + params + ['--'] + commandToRun - subprocess.call(cmd) - os.unlink(diskfile) - source.post_run(tmpl, template_dir, name) - -def list_cached(args): - tmpls = [] - if args.source is not None: - tmpls.extend(template.Template.get_all(args.source, - "%s/%s" % (args.template_dir, args.source))) - else: - for source in ["docker", "virt-builder"]: - tmpls.extend(template.Template.get_all(source, - "%s/%s" % (args.template_dir, source))) - for tmpl in tmpls: - print (tmpl) - -def requires_template(parser): - parser.add_argument("template", - help=_("URI of the template")) - -def requires_name(parser): - parser.add_argument("-n","--name", - help=_("Name of the running sandbox")) - -def requires_debug(parser): - parser.add_argument("-d","--debug", - default=False, action="store_true", - help=_("Run in debug mode")) - -def check_connect(connectstr): - supportedDrivers = ['lxc:///','qemu:///session','qemu:///system'] - if not connectstr in supportedDrivers: - raise ValueError("URI '%s' is not supported by virt-sandbox-image" % connectstr) - return True - -def requires_connect(parser): - parser.add_argument("-c","--connect", - help=_("Connect string for libvirt")) - -def requires_template_dir(parser): - global default_template_dir - parser.add_argument("-t","--template-dir", - default=default_template_dir, - help=_("Template directory for saving templates")) - -def requires_image_dir(parser): - global default_image_dir - parser.add_argument("-I","--image-dir", - default=default_image_dir, - help=_("Image directory for saving images")) - -def gen_command_parser(subparser, name, helptext): - parser = subparser.add_parser( - name, help=helptext, - formatter_class=argparse.RawDescriptionHelpFormatter, - epilog=""" - -Example supported URI formats: - - docker:///ubuntu?tag=15.04 - docker://username:password@xxxxxxxxxxxxxxx/private/image - docker://registry.access.redhat.com/rhel6 - virt-builder:///fedora-20 -""") - return parser - -def gen_purge_args(subparser): - parser = gen_command_parser(subparser, "purge", - _("Purge cached template")) - requires_template(parser) - requires_template_dir(parser) - parser.set_defaults(func=purge) - -def gen_prepare_args(subparser): - parser = gen_command_parser(subparser, "prepare", - _("Prepare local template")) - requires_template(parser) - requires_connect(parser) - requires_template_dir(parser) - parser.set_defaults(func=prepare) - -def gen_run_args(subparser): - parser = gen_command_parser(subparser, "run", - _("Run an instance of a template")) - requires_name(parser) - requires_template(parser) - requires_connect(parser) - requires_template_dir(parser) - requires_image_dir(parser) - parser.add_argument("args", - nargs=argparse.REMAINDER, - help=_("command arguments to run")) - parser.add_argument("-N","--network", - help=_("Network params for running template")) - parser.add_argument("-e","--env",action="append", - help=_("Environment params for running template")) - - parser.set_defaults(func=run) - -def gen_list_args(subparser): - parser = gen_command_parser(subparser, "list", - _("List locally cached images")) - requires_template_dir(parser) - - parser.add_argument("-s","--source", - help=_("Name of the template source")) - - parser.set_defaults(func=list_cached) - -def main(): - parser = argparse.ArgumentParser(description="Sandbox Container Image Tool") - - requires_debug(parser) - - subparser = parser.add_subparsers(help=_("commands")) - subparser.required = True - subparser.dest = "command" - gen_purge_args(subparser) - gen_prepare_args(subparser) - gen_run_args(subparser) - gen_list_args(subparser) - - args = parser.parse_args() - if args.debug: - args.func(args) - sys.exit(0) - else: - try: - args.func(args) - sys.exit(0) - except KeyboardInterrupt as e: - sys.exit(0) - except ValueError as e: - sys.stderr.write("%s: %s\n" % (sys.argv[0], e)) - sys.stderr.flush() - sys.exit(1) - except IOError as e: - sys.stderr.write("%s: %s\n" % (sys.argv[0], e.filename)) - sys.stderr.flush() - sys.exit(1) - except OSError as e: - sys.stderr.write("%s: %s\n" % (sys.argv[0], e)) - sys.stderr.flush() - sys.exit(1) - except Exception as e: - print (e) - sys.exit(1) diff --git a/libvirt-sandbox/image/sources/Makefile.am b/libvirt-sandbox/image/sources/Makefile.am deleted file mode 100644 index 817baa0..0000000 --- a/libvirt-sandbox/image/sources/Makefile.am +++ /dev/null @@ -1,10 +0,0 @@ - -pythonimagedir = $(pythondir)/libvirt_sandbox/image/sources -pythonimage_DATA = \ - __init__.py \ - base.py \ - docker.py \ - virtbuilder.py \ - $(NULL) - -EXTRA_DIST = $(pythonimage_DATA) diff --git a/libvirt-sandbox/image/sources/__init__.py b/libvirt-sandbox/image/sources/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/libvirt-sandbox/image/sources/base.py b/libvirt-sandbox/image/sources/base.py deleted file mode 100644 index 0fc9243..0000000 --- a/libvirt-sandbox/image/sources/base.py +++ /dev/null @@ -1,160 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright (C) 2015 Universitat Politècnica de Catalunya. -# Copyright (C) 2015 Red Hat, Inc. -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2.1 of the License, or (at your option) any later version. -# -# This library 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 -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -# -# Author: Eren Yagdiran <erenyagdiran@xxxxxxxxx> - -from abc import ABCMeta, abstractmethod -import subprocess - -class Source(): - '''The Source class defines the base interface for - all image provider source implementations. An image - provide source is able to download templates from - a repository, convert them to a host specific image - format and report commands used to invoke them.''' - - __metaclass__ = ABCMeta - def __init__(self): - pass - - @abstractmethod - def list_templates(self, templatedir): - """ - :param templatedir: local directory path in which to store the template - - Get a list of all templates that are locally cached - - :returns: a list of libvirt_sandbox.template.Template objects - """ - pass - - @abstractmethod - def has_template(self, template, templatedir): - """ - :param template: libvirt_sandbox.template.Template object - :param templatedir: local directory path in which to store the template - - Check if a template has already been created. - """ - pass - - @abstractmethod - def create_template(self, template, templatedir, - connect=None): - """ - :param template: libvirt_sandbox.template.Template object - :param templatedir: local directory path in which to store the template - :param connect: libvirt connection URI - - Create a set of local disk images populated with the content - of a template. The images creation process will be isolated - inside a sandbox using the requested libvirt connection URI. - """ - pass - - @abstractmethod - def delete_template(self, template, templatedir): - """ - :param template: libvirt_sandbox.template.Template object - :param templatedir: local directory path from which to delete template - - Delete all local files associated with the template - """ - pass - - @abstractmethod - def get_command(self, template, templatedir, userargs): - """ - :param template: libvirt_sandbox.template.Template object - :param templatedir: local directory path in which templates are stored - :param userargs: user specified arguments to run - - Get the command line to invoke in the container. If userargs - is specified, then this should override the default args in - the image""" - pass - - @abstractmethod - def get_disk(self, template, templatedir, imagedir, sandboxname): - """ - :param template: libvirt_sandbox.template.Template object - :param templatedir: local directory path in which to find template - :param imagedir: local directory in which to storage disk image - - Creates an instance private disk image, backed by the content - of a template. - """ - pass - - @abstractmethod - def get_env(self, template, templatedir): - """ - :param template: libvirt_sandbox.template.Template object - :param templatedir: local directory path in which to find template - - Get the dict of environment variables to set - """ - pass - - def post_run(self, template, templatedir, imagename): - """ - :param template: libvirt_sandbox.template.Template object - :param templatedir: local directory path in which to find template - :param imagename: name of the image that just stopped running - - Hook called after the image has been stopped. By default is doesn't - do anything, subclasses can override this to do some additional - cleanup. - """ - pass - - - # Utility functions to share between the sources. - - def format_disk(self,disk,format,connect): - cmd = ['virt-sandbox'] - if connect is not None: - cmd.append("-c") - cmd.append(connect) - cmd.append("-p") - params = ['--disk=file:disk_image=%s,format=%s' %(disk,format), - '/sbin/mkfs.ext3', - '/dev/disk/by-tag/disk_image'] - cmd = cmd + params - subprocess.check_call(cmd) - - def extract_tarball(self, diskfile, format, tarfile, connect): - cmd = ['virt-sandbox'] - if connect is not None: - cmd.append("-c") - cmd.append(connect) - cmd.append("-p") - compression = "" - if tarfile.endswith(".gz"): - compression = "z" - params = ['-m', - 'host-image:/mnt=%s,format=%s' % (diskfile, format), - '--', - '/bin/tar', - 'xf%s' % compression, - '%s' % tarfile, - '-C', - '/mnt'] - cmd = cmd + params - subprocess.check_call(cmd) diff --git a/libvirt-sandbox/image/sources/docker.py b/libvirt-sandbox/image/sources/docker.py deleted file mode 100755 index eaf41fc..0000000 --- a/libvirt-sandbox/image/sources/docker.py +++ /dev/null @@ -1,690 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright (C) 2015 Universitat Politècnica de Catalunya. -# Copyright (C) 2015 Red Hat, Inc. -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2.1 of the License, or (at your option) any later version. -# -# This library 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 -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -# -# Author: Eren Yagdiran <erenyagdiran@xxxxxxxxx> -# - -import sys -import json -import traceback -import os -import subprocess -import shutil -import urllib.error -import urllib.parse -import urllib.request -import hashlib -from abc import ABCMeta, abstractmethod -import copy -from libvirt_sandbox.image.template import Template - -from . import base - -class DockerConfParser(): - - def __init__(self,jsonfile): - with open(jsonfile) as json_file: - self.json_data = json.load(json_file) - def getCommand(self): - return self.json_data['config']['Cmd'] - def getEntrypoint(self): - return self.json_data['config']['Entrypoint'] - def getEnvs(self): - lst = self.json_data['config']['Env'] - if lst is not None and isinstance(lst,list): - return lst - else: - return [] - -class DockerImage(): - - def __init__(self, repo, name, tag=None): - - self.repo = repo - self.name = name - self.tag = tag - - if self.tag is None: - self.tag = "latest" - - if self.repo is None: - self.repo = "library" - - def __repr__(self): - return "%s/%s,tag=%s" % (self.repo, self.name, self.tag) - - @classmethod - def from_template(cls, template): - bits = template.path[1:].split("/") - if len(bits) == 1: - return cls(repo=None, - name=bits[0], - tag=template.params.get("tag")) - elif len(bits) == 2: - return cls(repo=bits[0], - name=bits[1], - tag=template.params.get("tag")) - else: - raise Exception("Expected image name, or repo & image name for path, not '%s'", - template.path) - - -class DockerAuth(): - - __metaclass__ = ABCMeta - def __init__(self): - pass - - @abstractmethod - def prepare_req(self, req): - pass - - @abstractmethod - def process_res(self, res): - pass - - @abstractmethod - def process_err(self, err): - return False - - -class DockerAuthNop(DockerAuth): - - def prepare_req(self, req): - pass - - def process_res(self, res): - pass - - def process_err(self, err): - return False - - -class DockerAuthBasic(DockerAuth): - - def __init__(self, username, password): - self.username = username - self.password = password - self.token = None - - def prepare_req(self, req): - if self.username is not None: - auth = base64.encodestring( - '%s:%s' % (self.username, self.password)).replace('\n', '') - - req.add_header("Authorization", "Basic %s" % auth) - - req.add_header("X-Docker-Token", "true") - - def process_res(self, res): - self.token = res.info().get('X-Docker-Token') - - def process_err(self, err): - return False - - -class DockerAuthToken(DockerAuth): - - def __init__(self, token): - self.token = token - - def prepare_req(self, req): - req.add_header("Authorization", "Token %s" % self.token) - - def process_res(self, res): - pass - - def process_err(self, err): - return False - - -class DockerAuthBearer(DockerAuth): - - def __init__(self): - self.token = None - - def prepare_req(self, req): - if self.token is not None: - req.add_header("Authorization", "Bearer %s" % self.token) - - def process_res(self, res): - pass - - def process_err(self, err): - method = err.headers.get("WWW-Authenticate", None) - if method is None: - return False - - if not method.startswith("Bearer "): - return False - - challenge = method[7:] - - bits = challenge.split(",") - attrs = {} - for bit in bits: - subbit = bit.split("=") - attrs[subbit[0]] = subbit[1][1:-1] - - url = attrs["realm"] - del attrs["realm"] - if "error" in attrs: - del attrs["error"] - - params = "&".join([ - "%s=%s" % (attr, attrs[attr]) - for attr in attrs.keys() - ]) - if params != "": - url = url + "?" + params - - req = urllib.request.Request(url=url) - req.add_header("Accept", "application/json") - - res = urllib.request.urlopen(req) - data = json.loads(res.read()) - self.token = data["token"] - return True - - -class DockerRegistry(): - - def __init__(self, uri_base): - - self.uri_base = list(urllib.parse.urlparse(uri_base)) - self.auth_handler = DockerAuthNop() - - def set_auth_handler(self, auth_handler): - self.auth_handler = auth_handler - - def supports_v2(self): - try: - (data, res) = self.get_json("/v2/") - ver = res.info().get("Docker-Distribution-Api-Version") - except urllib.error.HTTPError as e: - ver = e.headers.get("Docker-Distribution-Api-Version", None) - - if ver is None: - return False - return ver.startswith("registry/2") - - def set_server(self, server): - self.uri_base[1] = server - - @classmethod - def from_template(cls, template): - protocol = template.protocol - hostname = template.hostname - port = template.port - - if protocol is None: - protocol = "https" - if hostname is None: - hostname = "index.docker.io" - - if port is None: - server = hostname - else: - server = "%s:%s" % (hostname, port) - - url = urllib.parse.urlunparse((protocol, server, "", None, None, None)) - - return cls(url) - - def get_url(self, path, headers=None): - url_bits = copy.copy(self.uri_base) - url_bits[2] = path - url = urllib.parse.urlunparse(url_bits) - debug("Fetching %s..." % url) - - req = urllib.request.Request(url=url) - - if headers is not None: - for h in headers.keys(): - req.add_header(h, headers[h]) - - self.auth_handler.prepare_req(req) - - try: - res = urllib.request.urlopen(req) - self.auth_handler.process_res(res) - return res - except urllib.error.HTTPError as e: - if e.code == 401: - retry = self.auth_handler.process_err(e) - if retry: - debug("Re-Fetching %s..." % url) - self.auth_handler.prepare_req(req) - res = urllib.request.urlopen(req) - self.auth_handler.process_res(res) - return res - else: - debug("Not re-fetching") - raise - else: - raise - - def save_data(self, path, dest, checksum=None): - try: - res = self.get_url(path) - - datalen = res.info().get("Content-Length") - if datalen is not None: - datalen = int(datalen) - - csum = None - if checksum is not None: - csum = hashlib.sha256() - - pattern = [".", "o", "O", "o"] - patternIndex = 0 - donelen = 0 - - with open(dest, "wb") as f: - while 1: - buf = res.read(1024*64) - if not buf: - break - if csum is not None: - csum.update(buf) - f.write(buf) - - if datalen is not None: - donelen = donelen + len(buf) - debug("\x1b[s%s (%5d Kb of %5d Kb)\x1b8" % ( - pattern[patternIndex], (donelen/1024), (datalen/1024) - )) - patternIndex = (patternIndex + 1) % 4 - - debug("\x1b[K") - if csum is not None: - csumstr = "sha256:" + csum.hexdigest() - if csumstr != checksum: - debug("FAIL checksum '%s' does not match '%s'" % (csumstr, checksum)) - os.remove(dest) - raise IOError("Checksum '%s' for data does not match '%s'" % (csumstr, checksum)) - debug("OK\n") - return res - except Exception as e: - debug("FAIL %s\n" % str(e)) - raise - - def get_json(self, path): - try: - headers = {} - headers["Accept"] = "application/json" - res = self.get_url(path, headers) - data = json.loads(res.read()) - debug("OK\n") - return (data, res) - except Exception as e: - debug("FAIL %s\n" % str(e)) - raise - - -class DockerSource(base.Source): - - def _check_cert_validate(self): - major = sys.version_info.major - SSL_WARNING = "SSL certificates couldn't be validated by default. You need to have 2.7.9/3.4.3 or higher" - SSL_WARNING +="\nSee https://bugs.python.org/issue22417\n" - py2_7_9_hexversion = 34015728 - py3_4_3_hexversion = 50594800 - if (major == 2 and sys.hexversion < py2_7_9_hexversion) or (major == 3 and sys.hexversion < py3_4_3_hexversion): - sys.stderr.write(SSL_WARNING) - - def _was_downloaded(self, image, templatedir): - try: - self._get_image_list(image, templatedir) - return True - except Exception: - return False - - def list_templates(self, templatedir): - indexes = [] - try: - imagedirs = os.listdir(templatedir) - except OSError: - return [] - - for imagetagid in imagedirs: - indexfile = templatedir + "/" + imagetagid + "/index.json" - if os.path.exists(indexfile): - with open(indexfile,"r") as f: - index = json.load(f) - indexes.append(index) - - return [ - Template(source="docker", - protocol=None, - hostname=None, - port=None, - username=None, - password=None, - path="/%s/%s" % (index.get("repo", "library"), index["name"]), - params={ - "tag": index.get("tag", "latest"), - }) for index in indexes] - - def has_template(self, template, templatedir): - try: - image = DockerImage.from_template(template) - configfile, diskfile = self._get_template_data(image, templatedir) - return os.path.exists(diskfile) - except Exception: - return False - - def download_template(self, image, template, templatedir): - try: - createdFiles = [] - createdDirs = [] - - self._download_template_impl(image, template, templatedir, createdFiles, createdDirs) - except Exception as e: - for f in createdFiles: - try: - os.remove(f) - except: - pass - for d in createdDirs: - try: - shutil.rmtree(d) - except: - pass - raise - - def _download_template_impl(self, image, template, templatedir, createdFiles, createdDirs): - self._check_cert_validate() - - registry = DockerRegistry.from_template(template) - registry.set_auth_handler(DockerAuthBearer()) - if registry.supports_v2(): - self._download_template_impl_v2(registry, image, template, templatedir, createdFiles, createdDirs) - else: - self._download_template_impl_v1(registry, image, template, templatedir, createdFiles, createdDirs) - - def _download_template_impl_v1(self, registry, image, template, templatedir, createdFiles, createdDirs): - basicauth = DockerAuthBasic(template.username, template.password) - registry.set_auth_handler(basicauth) - try: - (data, res) = registry.get_json("/v1/repositories/%s/%s/images" % ( - image.repo, image.name, - )) - except urllib.error.HTTPError as e: - raise ValueError(["Image '%s' does not exist" % template]) - - registryendpoint = res.info().get('X-Docker-Endpoints') - - if basicauth.token is not None: - registry.set_auth_handler(DockerAuthToken(basicauth.token)) - else: - registry.set_auth_handler(DockerAuthNop()) - - (data, res) = registry.get_json("/v1/repositories/%s/%s/tags" %( - image.repo, image.name - )) - - if image.tag not in data: - raise ValueError(["Tag '%s' does not exist for image '%s'" % - (image.tag, template)]) - imagetagid = data[image.tag] - - (data, res) = registry.get_json("/v1/images/" + imagetagid + "/ancestry") - - if data[0] != imagetagid: - raise ValueError(["Expected first layer id '%s' to match image id '%s'", - data[0], imagetagid]) - - for layerid in data: - layerdir = templatedir + "/" + layerid - if not os.path.exists(layerdir): - os.makedirs(layerdir) - createdDirs.append(layerdir) - - jsonfile = layerdir + "/template.json" - datafile = layerdir + "/template.tar.gz" - - if not os.path.exists(jsonfile) or not os.path.exists(datafile): - res = registry.save_data("/v1/images/" + layerid + "/json", - jsonfile) - createdFiles.append(jsonfile) - - registry.save_data("/v1/images/" + layerid + "/layer", - datafile) - createdFiles.append(datafile) - - index = { - "repo": image.repo, - "name": image.name, - "tag": image.tag, - } - - indexfile = templatedir + "/" + imagetagid + "/index.json" - with open(indexfile, "w") as f: - f.write(json.dumps(index)) - - - def _download_template_impl_v2(self, registry, image, template, templatedir, createdFiles, createdDirs): - (manifest, res) = registry.get_json( "/v2/%s/%s/manifests/%s" % ( - image.repo, image.name, image.tag)) - - layerChecksums = [ - layer["blobSum"] for layer in manifest["fsLayers"] - ] - layers = [ - json.loads(entry["v1Compatibility"]) for entry in manifest["history"] - ] - - for i in range(len(layerChecksums)): - layerChecksum = layerChecksums[i] - config = layers[i] - - layerdir = templatedir + "/" + config["id"] - if not os.path.exists(layerdir): - os.makedirs(layerdir) - createdDirs.append(layerdir) - - jsonfile = layerdir + "/template.json" - datafile = layerdir + "/template.tar.gz" - - with open(jsonfile, "w") as fh: - fh.write(json.dumps(config)) - - registry.save_data("/v2/%s/%s/blobs/%s" % ( - image.repo, image.name, layerChecksum), - datafile, checksum=layerChecksum) - - - index = { - "repo": image.repo, - "name": image.name, - "tag": image.tag, - } - - indexfile = templatedir + "/" + layers[0]["id"] + "/index.json" - with open(indexfile, "w") as f: - f.write(json.dumps(index)) - - - def create_template(self, template, templatedir, connect=None): - image = DockerImage.from_template(template) - - if not self._was_downloaded(image, templatedir): - self.download_template(image, template, templatedir) - - imagelist = self._get_image_list(image, templatedir) - imagelist.reverse() - - parentImage = None - for imagetagid in imagelist: - templateImage = templatedir + "/" + imagetagid + "/template.qcow2" - cmd = ["qemu-img","create","-f","qcow2"] - if parentImage is not None: - cmd.append("-o") - cmd.append("backing_fmt=qcow2,backing_file=%s" % parentImage) - cmd.append(templateImage) - if parentImage is None: - cmd.append("10G") - subprocess.check_call(cmd) - - if parentImage is None: - self.format_disk(templateImage, "qcow2", connect) - - path = templatedir + "/" + imagetagid + "/template." - self.extract_tarball(path + "qcow2", - "qcow2", - path + "tar.gz", - connect) - parentImage = templateImage - - def _get_image_list(self, image, templatedir): - imageparent = {} - imagenames = {} - imagedirs = [] - try: - imagedirs = os.listdir(templatedir) - except OSError: - pass - for imagetagid in imagedirs: - indexfile = templatedir + "/" + imagetagid + "/index.json" - if os.path.exists(indexfile): - with open(indexfile,"r") as f: - index = json.load(f) - thisimage = DockerImage(index.get("repo"), - index["name"], - index.get("tag")) - imagenames[str(thisimage)] = imagetagid - jsonfile = templatedir + "/" + imagetagid + "/template.json" - if os.path.exists(jsonfile): - with open(jsonfile,"r") as f: - data = json.load(f) - parent = data.get("parent",None) - if parent: - imageparent[imagetagid] = parent - if str(image) not in imagenames: - raise ValueError(["Image %s does not exist locally" % image]) - imagetagid = imagenames[str(image)] - imagelist = [] - while imagetagid != None: - imagelist.append(imagetagid) - parent = imageparent.get(imagetagid,None) - imagetagid = parent - return imagelist - - def delete_template(self, template, templatedir): - image = DockerImage.from_template(template) - - imageusage = {} - imageparent = {} - imagenames = {} - imagedirs = [] - try: - imagedirs = os.listdir(templatedir) - except OSError: - pass - for imagetagid in imagedirs: - indexfile = templatedir + "/" + imagetagid + "/index.json" - if os.path.exists(indexfile): - with open(indexfile,"r") as f: - index = json.load(f) - thisimage = DockerImage(index.get("repo"), - index["name"], - index.get("tag")) - imagenames[str(thisimage)] = imagetagid - jsonfile = templatedir + "/" + imagetagid + "/template.json" - if os.path.exists(jsonfile): - with open(jsonfile,"r") as f: - data = json.load(f) - - parent = data.get("parent",None) - if parent: - if parent not in imageusage: - imageusage[parent] = [] - imageusage[parent].append(imagetagid) - imageparent[imagetagid] = parent - - - if str(image) not in imagenames: - raise ValueError(["Image %s does not exist locally" % image]) - imagetagid = imagenames[str(image)] - while imagetagid != None: - debug("Remove %s\n" % imagetagid) - parent = imageparent.get(imagetagid,None) - - indexfile = templatedir + "/" + imagetagid + "/index.json" - if os.path.exists(indexfile): - os.remove(indexfile) - jsonfile = templatedir + "/" + imagetagid + "/template.json" - if os.path.exists(jsonfile): - os.remove(jsonfile) - datafile = templatedir + "/" + imagetagid + "/template.tar.gz" - if os.path.exists(datafile): - os.remove(datafile) - imagedir = templatedir + "/" + imagetagid - shutil.rmtree(imagedir) - - if parent: - if len(imageusage[parent]) != 1: - debug("Parent %s is shared\n" % parent) - parent = None - imagetagid = parent - - def _get_template_data(self, image, templatedir): - imageList = self._get_image_list(image, templatedir) - toplayer = imageList[0] - diskfile = templatedir + "/" + toplayer + "/template.qcow2" - configfile = templatedir + "/" + toplayer + "/template.json" - return configfile, diskfile - - def get_disk(self, template, templatedir, imagedir, sandboxname): - image = DockerImage.from_template(template) - configfile, diskfile = self._get_template_data(image, templatedir) - tempfile = imagedir + "/" + sandboxname.split('/')[-1] + ".qcow2" - if not os.path.exists(imagedir): - os.makedirs(imagedir) - cmd = ["qemu-img","create","-q","-f","qcow2"] - cmd.append("-o") - cmd.append("backing_fmt=qcow2,backing_file=%s" % diskfile) - cmd.append(tempfile) - subprocess.check_call(cmd) - return tempfile - - def get_command(self, template, templatedir, userargs): - image = DockerImage.from_template(template) - configfile, diskfile = self._get_template_data(image, templatedir) - configParser = DockerConfParser(configfile) - cmd = configParser.getCommand() - entrypoint = configParser.getEntrypoint() - if entrypoint is None: - entrypoint = [] - if cmd is None: - cmd = [] - if userargs is not None and len(userargs) > 0: - return entrypoint + userargs - else: - return entrypoint + cmd - - def get_env(self, template, templatedir): - image = DockerImage.from_template(template) - configfile, diskfile = self._get_template_data(image, templatedir) - configParser = DockerConfParser(configfile) - return configParser.getEnvs() - -def debug(msg): - sys.stderr.write(msg) diff --git a/libvirt-sandbox/image/sources/virtbuilder.py b/libvirt-sandbox/image/sources/virtbuilder.py deleted file mode 100755 index 1b32083..0000000 --- a/libvirt-sandbox/image/sources/virtbuilder.py +++ /dev/null @@ -1,109 +0,0 @@ -# -# Copyright (C) 2015 SUSE LLC -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2.1 of the License, or (at your option) any later version. -# -# This library 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 -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -# -# Author: Cedric Bosdonnat <cbosdonnat@xxxxxxxx> -# - -import os -import os.path -import subprocess - -from . import base -from libvirt_sandbox.image.template import Template - - -class VirtBuilderSource(base.Source): - - def _get_template_name(self, template): - # We shouldn't have '/' in the names, but let's make sure - # nobody can try to alter the folders structure later. - return template.path[1:].replace('/', '_') - - def has_template(self, template, templatedir): - imagepath = "%s/%s.qcow2" % (templatedir, template.path) - return os.path.exists(imagepath) - - def create_template(self, template, templatedir, connect=None): - if not os.path.exists(templatedir): - os.makedirs(templatedir) - - # Get the image using virt-builder - templatename = self._get_template_name(template) - imagepath_original = "%s/%s-original.qcow2" % (templatedir, templatename) - imagepath = "%s/%s.qcow2" % (templatedir, templatename) - cmd = ["virt-builder", templatename, "--no-network", - "-o", imagepath_original, "--format", "qcow2"] - subprocess.check_call(cmd) - - try: - # We need to convert this image into a single partition one. - tarfile = "%s/%s.tar" % (templatedir, templatename) - cmd = ["virt-tar-out", "-a", imagepath_original, "/", tarfile] - subprocess.check_call(cmd) - - cmd = ["qemu-img", "create", "-q", "-f", "qcow2", imagepath, "10G"] - subprocess.check_call(cmd) - - self.format_disk(imagepath, "qcow2", connect) - self.extract_tarball(imagepath, "qcow2", tarfile, connect) - - finally: - os.unlink(imagepath_original) - os.unlink(tarfile) - - def list_templates(self, templatedir): - files = [] - try: - imagefiles = os.listdir(templatedir) - except OSError: - return [] - - for filename in imagefiles: - if not filename.endswith(".qcow2"): - continue - files.append(filename[0:-6]) - - return [ - Template(source="virt-builder", - protocol=None, - hostname=None, - port=None, - username=None, - password=None, - path="/%s" % filename, - params={}) for filename in files] - - def delete_template(self, template, templatedir): - os.unlink("%s/%s.qcow2" % (templatedir, self._get_template_name(template))) - - def get_command(self, template, templatedir, userargs): - return userargs - - def get_disk(self,template, templatedir, imagedir, sandboxname): - diskfile = "%s/%s.qcow2" % (templatedir, self._get_template_name(template)) - tempfile = imagedir + "/" + sandboxname + ".qcow2" - if not os.path.exists(imagedir): - os.makedirs(imagedir) - cmd = ["qemu-img", "create", "-q", - "-f", "qcow2", - "-o", "backing_fmt=qcow2,backing_file=%s" % diskfile, - tempfile] - subprocess.check_call(cmd) - return tempfile - - def get_env(self,template, templatedir): - return [] diff --git a/libvirt-sandbox/image/template.py b/libvirt-sandbox/image/template.py deleted file mode 100644 index ab2ea29..0000000 --- a/libvirt-sandbox/image/template.py +++ /dev/null @@ -1,133 +0,0 @@ -# -# -*- coding: utf-8 -*- -# Authors: Daniel P. Berrange <berrange@xxxxxxxxxx> -# -# Copyright (C) 2015 Red Hat, 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 urllib.parse -import importlib -import re - -class Template(object): - - def __init__(self, - source, protocol, - hostname, port, - username, password, - path, params): - """ - :param source: template source name - :param protocol: network transport protocol or None - :param hostname: registry hostname or None - :param port: registry port or None - :param username: username or None - :param password: password or None - :param path: template path identifier - :param params: template parameters - - docker:///ubuntu - - docker+https://index.docker.io/ubuntu?tag=latest - - virt-builder:///fedora-20 - """ - - self.source = source - self.protocol = protocol - self.hostname = hostname - self.port = port - self.username = username - self.password = password - self.path = path - self.params = params - if self.params is None: - self.params = {} - - @classmethod - def _get_source_impl(klass, source): - try: - p = re.compile("\W") - sourcemod = "".join(p.split(source)) - sourcename = "".join([i.capitalize() for i in p.split(source)]) - - mod = importlib.import_module( - "libvirt_sandbox.image.sources." + sourcemod) - classname = sourcename + "Source" - classimpl = getattr(mod, classname) - return classimpl() - except Exception as e: - print (e) - raise Exception("Invalid source: '%s'" % source) - - def get_source_impl(self): - if self.source == "": - raise Exception("Missing scheme in image URI") - - return self._get_source_impl(self.source) - - def __repr__(self): - if self.protocol is not None: - scheme = self.source + "+" + self.protocol - else: - scheme = self.source - if self.hostname: - if self.port: - netloc = "%s:%d" % (self.hostname, self.port) - else: - netloc = self.hostname - - if self.username: - if self.password: - auth = self.username + ":" + self.password - else: - auth = self.username - netloc = auth + "@" + netloc - else: - netloc = None - - query = "&".join([key + "=" + self.params[key] for key in self.params.keys()]) - ret = urllib.parse.urlunparse((scheme, netloc, self.path, None, query, None)) - return ret - - @classmethod - def from_uri(klass, uri): - o = urllib.parse.urlparse(uri) - - idx = o.scheme.find("+") - if idx == -1: - source = o.scheme - protocol = None - else: - source = o.scheme[0:idx] - protocol = o.scheme[idx + 1:] - - query = {} - if o.query is not None and o.query != "": - for param in o.query.split("&"): - (key, val) = param.split("=") - query[key] = val - return klass(source, protocol, - o.hostname, o.port, - o.username, o.password, - o.path, query) - - @classmethod - def get_all(klass, source, templatedir): - impl = klass._get_source_impl(source) - - return impl.list_templates(templatedir) diff --git a/m4/virt-win32.m4 b/m4/virt-win32.m4 index d088ce8..8b908e7 100644 --- a/m4/virt-win32.m4 +++ b/m4/virt-win32.m4 @@ -4,15 +4,11 @@ AC_DEFUN([LIBVIRT_SANDBOX_WIN32],[ dnl for now since I'm not supporting mingw at present. - RWMJ CYGWIN_EXTRA_LDFLAGS= CYGWIN_EXTRA_LIBADD= - CYGWIN_EXTRA_PYTHON_LIBADD= MINGW_EXTRA_LDFLAGS= case "$host" in *-*-cygwin*) CYGWIN_EXTRA_LDFLAGS="-no-undefined" CYGWIN_EXTRA_LIBADD="${INTLLIBS}" - if test "x$PYTHON_VERSION" != "x"; then - CYGWIN_EXTRA_PYTHON_LIBADD="-L/usr/lib/python${PYTHON_VERSION}/config -lpython${PYTHON_VERSION}" - fi ;; *-*-mingw*) MINGW_EXTRA_LDFLAGS="-no-undefined" @@ -20,6 +16,5 @@ AC_DEFUN([LIBVIRT_SANDBOX_WIN32],[ esac AC_SUBST([CYGWIN_EXTRA_LDFLAGS]) AC_SUBST([CYGWIN_EXTRA_LIBADD]) - AC_SUBST([CYGWIN_EXTRA_PYTHON_LIBADD]) AC_SUBST([MINGW_EXTRA_LDFLAGS]) ]) -- 2.14.3 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list