Commit 7af9410a authored by Daniel P. Berrangé's avatar Daniel P. Berrangé

Delete virt-sandbox-image tool now in separate repo

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=summarySigned-off-by: 's avatarDaniel P. Berrangé <berrange@redhat.com>
parent c46de9af
......@@ -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
......
bin_PROGRAMS = virt-sandbox
bin_SCRIPTS = virt-sandbox-image
EXTRA_DIST = $(bin_SCRIPTS)
man1_MANS = \
......
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from libvirt_sandbox.image import cli
import sys
if __name__ == '__main__':
sys.exit(cli.main())
......@@ -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
......
......@@ -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
......
......@@ -2,7 +2,7 @@
EXTRA_DIST = libvirt-sandbox.sym
CLEANFILES =
SUBDIRS = tests image
SUBDIRS = tests
rundir = $(localstatedir)/run
......
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)
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Authors: Daniel P. Berrange <berrange@redhat.com>
# Eren Yagdiran <erenyagdiran@gmail.com>
#
# 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@index.docker.io/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)
pythonimagedir = $(pythondir)/libvirt_sandbox/image/sources
pythonimage_DATA = \
__init__.py \
base.py \
docker.py \
virtbuilder.py \
$(NULL)
EXTRA_DIST = $(pythonimage_DATA)
# -*- 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@gmail.com>
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)
This diff is collapsed.
#
# 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@suse.com>
#
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 []
#
# -*- coding: utf-8 -*-
# Authors: Daniel P. Berrange <berrange@redhat.com>
#
# 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))