[libvirt] [sandbox 08/11] virt-sandbox-image: add a virt-builder source

Daniel P. Berrange berrange at redhat.com
Wed Sep 23 08:27:49 UTC 2015


On Wed, Sep 23, 2015 at 09:53:38AM +0200, Cédric Bosdonnat wrote:
> Allow virt-sandbox-image to pull templates from virt-builder and run
> sandboxes on top of them.
> ---
>  libvirt-sandbox.spec.in                            |   1 +
>  libvirt-sandbox/image/cli.py                       |   1 +
>  libvirt-sandbox/image/sources/Makefile.am          |   1 +
>  libvirt-sandbox/image/sources/VirtBuilderSource.py | 136 +++++++++++++++++++++
>  libvirt-sandbox/image/template.py                  |   2 +
>  5 files changed, 141 insertions(+)
>  create mode 100644 libvirt-sandbox/image/sources/VirtBuilderSource.py

> +class VirtBuilderSource(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 download_template(self, template, templatedir):
> +        # We don't do anything here: virt-builder will do it for us in
> +        # the create action
> +        pass

I guess I could see us invoking virt-builder in the download step to
pull down the image and format it to qcow2.

> +
> +    def _check_disk_format(self,format):
> +        supportedFormats = ['qcow2', 'raw']
> +        if not format in supportedFormats:
> +            raise ValueError(["Unsupported image format %s" % format])
> +
> +    def create_template(self, template, templatedir,
> +                        connect=None, format=None):
> +        # FIXME Force qcow2 format: do we really want people to mess with that?
> +        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.%s" % (templatedir, templatename, format)
> +        cmd = ["virt-builder", templatename,
> +               "-o", imagepath_original, "--format", "qcow2"]
> +        subprocess.call(cmd)

eg, this bit could live in download, and then the following bit stay
in create. Honestly the distinction is kind of blury though. I wonder
if this is a sign that we should kill off the distinction between
download + create. The key idea behind having the separate commands
is that users may want to "prime the cache" for images they expect
to need to run in the future, so saving time later. This doesn't
require us to have download+create separate though - we could just
as easily have a single "prepare" command that combines them both.



> +
> +        # 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.call(cmd)
> +
> +        os.unlink(imagepath_original)
> +
> +        cmd = ["qemu-img", "create", "-q", "-f", format, imagepath, "10G"]
> +        subprocess.call(cmd)
> +
> +        self.format_disk(imagepath, format, connect)
> +        self._extract_tarball(imagepath, format, tarfile, connect)
> +        os.unlink(tarfile)
> +
> +
> +    def delete_template(self, template, templatedir):
> +        # Check for running sandboxes using this template before killing it
> +        images = self._get_template_uses(template, templatedir)
> +        if len(images) != 0:
> +            imagesStr = ", ".join(images)
> +            raise ValueError(["Images still using the template: %s" % imagesStr])
> +
> +        os.unlink("%s/%s.qcow2" % (templatedir, self._get_template_name(template)))
> +        os.unlink("%s/%s.uses" % (templatedir, self._get_template_name(template)))
> +
> +    def _get_template_uses(self, template, templatedir):
> +        uses = open("%s/%s.uses" % (templatedir, self._get_template_name(template)), "r")
> +        lines = uses.read()
> +        uses.close()
> +        return [l for l in lines.split("\n") if l != ""]
> +
> +    def post_run(self, template, templatedir, imagename):
> +        images = self._get_template_uses(template, templatedir)
> +        images.remove(imagename)
> +
> +        uses = open("%s/%s.uses" % (templatedir, self._get_template_name(template)), "w")
> +        uses.write("\n".join(images))
> +        uses.close()
> +
> +    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.call(cmd)
> +
> +        # Add the image to the uses files
> +        uses = open("%s/%s.uses" % (templatedir,
> +                                    self._get_template_name(template)), "a")
> +        uses.write("%s\n" % sandboxname)
> +        uses.close()

Ah interest, so with the DockerSource we don't actually currently do
anything to track usage in this way. We only track usage wrt to the
template layers - not running VM instances.

Technically I don't think we actually need to track usage from running
VMs as you illustrate. If a VM is running with an template, then QEMU/
qemu-nbd will hold an open file descriptor for the backing file it is
usage. So even if we delete the image from the cache, it won't harm
running VMs thanks to POSIX close/unlink semantics.

> +        return tempfile
> +
> +    def get_env(self,template, templatedir):
> +        return []
> +
> +    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")
> +        params = ['-m',
> +                  'host-image:/mnt=%s,format=%s' % (diskfile, format),
> +                  '--',
> +                  '/bin/tar',
> +                  'xf',
> +                  '%s' % tarfile,
> +                  '-C',
> +                  '/mnt']
> +        cmd = cmd + params
> +        subprocess.call(cmd)

Seems we can share this method with the DockerSource too,


Regards,
Daniel
-- 
|: http://berrange.com      -o-    http://www.flickr.com/photos/dberrange/ :|
|: http://libvirt.org              -o-             http://virt-manager.org :|
|: http://autobuild.org       -o-         http://search.cpan.org/~danberr/ :|
|: http://entangle-photo.org       -o-       http://live.gnome.org/gtk-vnc :|




More information about the libvir-list mailing list