debian-forge/assemblers/org.osbuild.qemu
Tom Gundersen 34098bf6c6 assembler: rename qcow2 to qemu and add support for more formats
Opt in to supporting the most common ones, if we want to support more
we can add support as the need arises.

Signed-off-by: Tom Gundersen <teg@jklm.no>
2019-09-29 19:05:55 +02:00

91 lines
3.6 KiB
Python
Executable file

#!/usr/bin/python3
import contextlib
import json
import os
import socket
import subprocess
import sys
import osbuild.remoteloop as remoteloop
@contextlib.contextmanager
def mount(source, dest, *options):
os.makedirs(dest, 0o755, True)
subprocess.run(["mount", *options, source, dest], check=True)
try:
yield
finally:
subprocess.run(["umount", "-R", dest], check=True)
@contextlib.contextmanager
def mount_api(dest):
with mount("/dev", f"{dest}/dev", "-o", "rbind"), \
mount("/proc", f"{dest}/proc", "-o", "rbind"), \
mount("/sys", f"{dest}/sys", "-o", "rbind"), \
mount("none", f"{dest}/run", "-t", "tmpfs"):
yield
@contextlib.contextmanager
def loop_device(loop_client, image, size, offset=0):
fd = os.open(image, os.O_RDWR)
devname = loop_client.create_device(fd, offset=offset, sizelimit=size)
os.close(fd)
path = f"/dev/{devname}"
try:
yield path
finally:
os.unlink(path)
def main(tree, output_dir, options, loop_client):
fmt = options["format"]
filename = options["filename"]
root_fs_uuid = options["root_fs_uuid"]
size = options["size"]
# sfdisk works on sectors of 512 bytes and ignores excess space - be explicit about this
if size % 512 != 0:
raise ValueError("`size` must be a multiple of sector size (512)")
if fmt not in ["raw", "qcow2", "vdi", "vmdk"]:
raise ValueError("`format` must be one of raw, qcow, vdi, vmdk")
image = f"/var/tmp/osbuild-image.raw"
mountpoint = f"/tmp/osbuild-mnt"
# Create an empty image file
subprocess.run(["truncate", "--size", str(size), image], check=True)
# Set up the partition table of the image
partition_table = "label: mbr\nbootable, type=83"
subprocess.run(["sfdisk", "-q", image], input=partition_table, encoding='utf-8', check=True)
r = subprocess.run(["sfdisk", "--json", image], stdout=subprocess.PIPE, encoding='utf-8', check=True)
partition_table = json.loads(r.stdout)
partition = partition_table["partitiontable"]["partitions"][0]
partition_offset = partition["start"] * 512
partition_size = partition["size"] * 512
# Populate the first partition of the image with an ext4 fs and fill it with the contents of the
# tree we are operating on.
subprocess.run(["mkfs.ext4", "-U", root_fs_uuid, "-E", f"offset={partition_offset}", image,
f"{int(partition_size / 1024)}k"], input="y", encoding='utf-8', check=True)
# Mount the created image as a loopback device
with loop_device(loop_client, image, partition_offset) as loop_block, \
loop_device(loop_client, image, partition_size, partition_offset) as loop_part, \
mount(loop_part, mountpoint):
# Copy the tree into the target image
subprocess.run(["cp", "-a", f"{tree}/.", mountpoint], check=True)
# Install grub2 into the boot sector of the image, and copy the grub2 imagise into /boot/grub2
with mount_api(mountpoint):
subprocess.run(["chroot", mountpoint, "grub2-install", "--no-floppy",
"--modules=part_msdos", "--target=i386-pc", loop_block], check=True)
subprocess.run(["qemu-img", "convert", "-O", fmt, "-c", image, f"{output_dir}/{filename}"], check=True)
if __name__ == '__main__':
args = json.load(sys.stdin)
sock = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_PASSCRED, 1)
sock.connect("/run/osbuild/api/remoteloop")
ret = main(args["tree"], args["output_dir"], args["options"], remoteloop.LoopClient(sock))
sys.exit(ret)