179 lines
4.9 KiB
Python
179 lines
4.9 KiB
Python
#!/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, "+10G"
|
|
]
|
|
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
|
|
|
|
|
|
def get_buildbot_password():
|
|
"""
|
|
Retrieve buildbot password for guest
|
|
"""
|
|
fname = '/etc/libvirt/buildbot.pwd'
|
|
with open(fname) as file_handle:
|
|
content = [line.rstrip('\n') for line in file_handle]
|
|
try:
|
|
return content[0]
|
|
except (TypeError, IndexError):
|
|
return "password"
|
|
|
|
|
|
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()
|