#!/usr/bin/python3 """ Creates an empemeral image and patches it """ # Script /etc/libvirt/hooks/qemu # Don't forget to execute service libvirt-bin restart # Also see https://www.libvirt.org/hooks.html # This script make clean VM for each start using base image import os import subprocess import sys import tempfile import xml.etree.ElementTree as et from jinja2 import Environment, exceptions, FileSystemLoader BACKINGSTORE_PATH = '/var/lib/libvirt/backingstore/' IMAGES_PATH = '/var/lib/libvirt/images/' EPHEMERAL_PATH = '/var/lib/libvirt/ephemeral/' CLOUDINIT_PATH = '/mnt/nfsimages' VENDOR_EXT = '.vendor' USER_EXT = '.user' CLOUDINIT_EXT = '.iso' xml = None def parse_xml(): """ Parse XML provided in stdin """ global xml if xml is None: data = sys.stdin.read() xml = et.fromstring(data) return xml def find_base(domain): """ Convert VM name into hostname (part before first _) """ return domain.split("_", 2)[0] def prepare_files(domain, env_): """ Create the ephemeral images """ xml = parse_xml() base = find_base(domain) for disk in xml.iterfind("./devices/disk[@device='disk']/source"): disk = disk.get('file') if not os.path.exists(os.path.join(BACKINGSTORE_PATH, base + '.qcow2')): continue if not disk.startswith(EPHEMERAL_PATH): continue cmd = ['/usr/bin/qemu-img', 'create', '-b', os.path.join(BACKINGSTORE_PATH, base + '.qcow2'), '-f', 'qcow2', '-F', 'qcow2', disk] subprocess.call(cmd) # resize cmd = ['/usr/bin/qemu-img', 'resize', disk, "+20G" ] subprocess.call(cmd) try: x = xml.find("./metadata/{http://buildbot.net/}auth") username, password, master = (x.attrib["username"], x.attrib["password"], x.attrib["master"]) except Exception: username = domain password = domain master = None for cdrom in xml.iterfind("./devices/disk[@device='cdrom']/source"): cdrom = cdrom.get('file') if not cdrom.startswith(EPHEMERAL_PATH): continue # cloud init if not os.path.exists(os.path.join(BACKINGSTORE_PATH, base + USER_EXT)): continue hostname = os.uname()[1].replace('.', '_') userdata = { 'guest': hostname, 'hostname': domain, 'buildbot_username': username, 'buildbot_password': password, 'buildbot_master': master } with tempfile.NamedTemporaryFile(mode='w+t') as temp_file: # jinja user data try: temp_file.write(env_.get_template(domain + USER_EXT). render(userdata)) except exceptions.TemplateNotFound: temp_file.write(env_.get_template(base + USER_EXT). render(userdata)) temp_file.flush() # make ISO cmd = ['/usr/bin/cloud-localds', # '-V', os.path.join(CLOUDINIT_PATH, # domain + VENDOR_EXT), # '-H', HOSTNAME, os.path.join(cdrom), temp_file.name ] subprocess.call(cmd) def cleanup_files(): """ Delete the ephemeral images """ xml = parse_xml() for disk in xml.iterfind("./devices/disk[@device='disk']/source"): disk = disk.get('file') if disk.startswith(EPHEMERAL_PATH): try: os.remove(disk) except FileNotFoundError: pass for cdrom in xml.iterfind("./devices/disk[@device='cdrom']/source"): cdrom = cdrom.get('file') if cdrom.startswith(EPHEMERAL_PATH): try: os.remove(cdrom) except FileNotFoundError: pass if __name__ == "__main__": try: # pylint: disable=unbalanced-tuple-unpacking VIR_DOMAIN, ACTION = sys.argv[1:3] except IndexError: sys.exit(1) ENV = Environment(autoescape=False, loader=FileSystemLoader(BACKINGSTORE_PATH), trim_blocks=False) if ACTION in ["prepare"]: cleanup_files() prepare_files(VIR_DOMAIN, ENV) if ACTION in ["release"]: cleanup_files()