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 :| -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list