diff --git a/meta-arm-autonomy/README.md b/meta-arm-autonomy/README.md index 78a7281f..560a783d 100644 --- a/meta-arm-autonomy/README.md +++ b/meta-arm-autonomy/README.md @@ -67,6 +67,9 @@ This layer is adding the following recipes and classes: * [xen-devicetree](documentation/xen-devicetree.md): this is a recipe to modify a device tree blob to add information required to boot xen and a Dom0 linux. +* [xenguest-mkimage](documentation/xenguest-mkimage.md): this is a tool to + create and modify images to be used as Xen guests. + Contributing ------------ This project has not put in place a process for contributions currently. If you diff --git a/meta-arm-autonomy/documentation/xenguest-mkimage.md b/meta-arm-autonomy/documentation/xenguest-mkimage.md new file mode 100644 index 00000000..d77fd48b --- /dev/null +++ b/meta-arm-autonomy/documentation/xenguest-mkimage.md @@ -0,0 +1,119 @@ +Xenguest mkimage +================ + +Introduction +------------ + +xenguest-mkimage is a tool to create and modify images to be used as Guest with +Xen. It defines a format to store completely defined guests as a file or as +a directory and provides options to create and modify those images. + +A xenguest image contains all elements required to create a xen guest. +This is the base elements like a Xen configuration and a Linux kernel binary +but also some more advanced elements like init scripts or a disk definition. + +The format is made to be deployable easily by storing everything in a single +file and provide tools to easily manipulate the images. It can also easily be +extended to have features like encryption or signature of images, updates or +complex configurations by providing features to have init script that will be +executed on the host embedded inside the image. + +Xenguest images content +----------------------- + +### params.cfg + +This file contains parameters that can be used by tools to configure some +functionalities on the host. This can be used by init scripts to have +configurable parameters as it is sourced before calling init scripts. + +### guest.cfg and guest.d + +guest.cfg is the main xen configuration and guest.d contains optional +configuration parts. All those will be merged into one final xen configuration +before starting the guest. + +### files +This directory contains files that can be used by the xen configuration, for +example the binary of the kernel referenced in xen configuration). +This is where the kernel binary, the dtb or a ramdisk will be stored. + +### init.pre, init.d and init.post +These directories contain init scripts that will be executed on the host +during the guest startup. Those must be shell scripts and each directory +contains scripts called at a different time: + - init.pre: scripts executed before the guest is created. This can be used + to prepare some features required to create the guest in xen or to + generate part of the xen configuration dynamically. + - init.d: scripts executed when the guest has been created but before it is + started. This can be used to do some xenstore operations or configure the + guest behaviour using xl, for example. + - init.post: scripts executed just after starting the guest. This can be + used to configure things created by xen for the guest like network + network interfaces. + +When a directory contains several scripts, those will be called in alphabetical +order. + +### disk.cfg and disk-files +disk.cfg contains the guest disk description (disk size and disk partitions). +The file contains the following entries: +- `DISK_SIZE=X`: size of the disk to create in GB +- `DISK_PARTX=SIZE:FS:CONTENT`: create a partition number X (1 to 4) with a + size of SIZE GB, format it with filesystem FS (can be ext2, ext3, ext4, vfat + or swap) and extract CONTENT as initial partition content + (.tar[.gz|.xz|.bz2] file or img file to be dumped in the partition). FS and + CONTENT can be empty. + +The disk-files contain files to be used for initializing the disk partitions +content. Those should be used to create a LVM or a physical disk and initialize +it (create partitions, format them and put the initial content). + +Usage +----- + +xenguest-mkimage is a shell script which must be called like this: +`xenguest-mkimage OPERATION XENGUEST [OPTIONS]` + +### Operations +- create: create a xenguest image. If XENGUEST is an existing empty directory, + the image is created as a directory otherwise it will be created as a file. +- check: verify that XENGUEST is a valid xenguest image. +- update: modify a xenguest image (see --help for a list of operations). +- pack: pack a xenguest image directory into a xenguest image file. The file to + be created must be given as 3rd argument. +- extract: extract a xenguest image file into a directory. The destination + directory must be given as 3rd argument. +- dump-xenconfig: dump xenguest image xen configuration. +- dump-diskconfig: dump xenguest image disk configuration. +- dump-paramsconfig: dump xenguest image parameters configuration. + +For a detailed help on available operations, please use: +`xenguest-mkimage --help` + +### Options +- --kernel=FILE: add kernel FILE as guest kernel. This is both adding the file + to the image and modifying the xen configuration to use it. +- --xen-memory=SIZE: set the guest memory size in MB. +- --xen-extra: add a kernel command line argument. This can be called several + times to add several command line options. +- --xen-device-tree=FILE: add dtb FILE as device tree. This both adding the + file to the image and modifying the xen configuration to use it. +- --init-script=FILE: add guest init script. The script is embedded inside the + image file. Several script can be added and the basename of FILE is used to + distinguish them (calling the option twice with the same file will update the + script in the image with the second one). + --disk-size=SIZE: set the guest disk size to SIZE in GB. Calling this with 0 + disable the guest disk. +- --disk-add-part=NUM:SIZE:FS:CONTENT: This is adding a partition to the + xenguest image disk. The partition is described with the arguments: + - NUM: partition number. + - SIZE: partition size in GB. + - FS: filesystem to format the partition with. This can be ext2, ext3, ext4, + vfat of swap. If empty the partition is not formated. + - CONTENT: tar of img file to use to initialize the partition. The file must + be added to the image using --disk-add-file=FILE:CONTENT. + +For a detailed help on available options, please use: +`xenguest-mkimage OPERATION --help` + diff --git a/meta-arm-autonomy/recipes-extended/xenguest/files/xenguest-mkimage b/meta-arm-autonomy/recipes-extended/xenguest/files/xenguest-mkimage new file mode 100755 index 00000000..4c1b4a87 --- /dev/null +++ b/meta-arm-autonomy/recipes-extended/xenguest/files/xenguest-mkimage @@ -0,0 +1,772 @@ +#!/bin/bash +# This script must be used to manipulate xenguest images +# +# xenguest image topology: +# params.cfg: guest global configuration file. Only edited using this script. +# guest.cfg: xen main configuration file. Only edited using this script. +# guest.d: directory contains files with custom xen configuration entries +# which are appended to guest.cfg before starting the guest +# files: directory where files used by xen configuration are stored +# disk.cfg: guest disk configuration file. Only edited using this script. +# (dtb, kernel image, etc) +# disk: directory where files for disk creation are stored +# init.[pre,d,post]: directories containing init pre, base and post scripts +set -u +set -e + +this="$0" + +IMAGE_TMPDIR="" + +usage() { + cat < /dev/null 2>&1 || echo "error") + if [ -n "${res}" ]; then + echo "Error: File ${tstfile} is not a valid xenguest" + exit 1 + fi + elif [ -d ${tstfile} ]; then + if [ ! -f ${tstfile}/guest.cfg -o ! -f ${tstfile}/disk.cfg -o \ + ! ${tstfile}/params.cfg ]; then + echo "Error: Directory ${tstfile} is not a valid xenguest" + exit 1 + fi + fi +} + +params_config_reset() { + cat < ${IMAGE_TMPDIR}/params.cfg +# Xenguest-image guest global configuration +# +# !! This file must not be modified manually !! +# +# You can use xenguest-image to modify parameters. +# + +# Guest auto boot during Dom0 init +GUEST_AUTOBOOT="1" +EOF +} + +params_config_setparam() { + param="${1}" + shift + value="$@" + + if [ -z "$value" ]; then + sed -i "/.*${param}=.*/d" ${IMAGE_TMPDIR}/params.cfg + elif grep -e "^${param}=" ${IMAGE_TMPDIR}/params.cfg > /dev/null; then + sed -i "s/${param}=\".*\"/${param}=\"${value}\"/" \ + ${IMAGE_TMPDIR}/params.cfg + else + echo "${param}=\"${value}\"" >> ${IMAGE_TMPDIR}/params.cfg + fi +} + +xen_config_reset() { + cat < ${IMAGE_TMPDIR}/guest.cfg +# Xenguest-image main configuraiton +# +# !! This file must not be modified manually !! +# +# You can use xenguest-image to modify parameters. +# +# You can add custom entries to configuration in the guest.d directory. + +# Guest name (set by manager when guest is created) +# name = "" + +# Guest memory size in MB +memory = 1024 + +# Number of VCPUS +vcpus = 1 + +# Guest command line +extra = "earlyprintk=xenboot console=hvc0 rw" + +# Guest root filesystem device (from guest point of view) +# root = "/dev/xvda2" + +# Disk that will be used by the guest (set by manager when guest is created) +# disk = ['phy:/dev/vg-xen/guestname,xvda,w'] + +EOF +} + +get_param_file() { + param="${1}" + + if grep ${param} ${IMAGE_TMPDIR}/guest.cfg > /dev/null 2>&1; then + echo "${IMAGE_TMPDIR}/guest.cfg" + else + if [ ! -f ${IMAGE_TMPDIR}/guest.d/${param}.cfg ]; then + mkdir -p ${IMAGE_TMPDIR}/guest.d + echo "# ${param} = \"\"" > ${IMAGE_TMPDIR}/guest.d/${param}.cfg + fi + echo "${IMAGE_TMPDIR}/guest.d/${param}.cfg" + fi +} + +xen_config_disable_param() { + param="${1}" + dst=$(get_param_file ${param}) + + sed -i "s@.*\(${param} = .*\)\$@# \1@" ${dst} +} + +xen_config_set_number() { + param="${1}" + shift + value="$@" + dst=$(get_param_file ${param}) + + sed -i "s@.*${param} = .*@${param} = ${value}@" ${dst} +} + +xen_config_set_string() { + param="${1}" + shift + value="$@" + dst=$(get_param_file ${param}) + + sed -i "s@.*${param} = .*@${param} = \"${value}\"@" ${dst} +} + +xen_config_append_string() { + param="${1}" + shift + value="$@" + dst=$(get_param_file ${param}) + + sed -i "s@.*${param} = \"\([^\"]*\)\"@${param} = \"\1 ${value}\"@" ${dst} +} + +xen_config_set_list() { + param="${1}" + shift + value=$(echo $@ | tr " " ",") + dst=$(get_param_file ${param}) + + sed -i "s@.*${param} = .*@${param} = ['${value}']@" ${dst} +} + +disk_config_reset() { + echo "DISK_SIZE=\"0\"" > ${IMAGE_TMPDIR}/disk.cfg + echo "DISK_DEVICE=\"\"" >> ${IMAGE_TMPDIR}/disk.cfg +} + +disk_config_rm_part() { + partid=$1 + sed -i "/DISK_PART${partid}=.*/d" ${IMAGE_TMPDIR}/disk.cfg +} + +disk_config_add_part() { + partconf="${1}" + partid=$(echo ${partconf} | sed -e "s/:.*//") + partinfo=$(echo ${partconf} | sed -e "s/[^:]*://") + + # Make sure we don't add the same partition twice + disk_config_rm_part ${partid} + echo "DISK_PART${partid}=\"${partinfo}\"" >> \ + ${IMAGE_TMPDIR}/disk.cfg +} + +# We need an action as first argument +action="${1:-}" + +if [ -z "${action}" ]; then + echo "Error: No ACTION provided" + usage + exit 1 +fi + +# Only help does not require a xenguest argument so treat this first +# while there we also check that user is asking for a supported action +case $action in + help|--help|-h|-?) + usage + exit 0 + ;; + check|create|update|pack|partial) + ;; + dump-xenconfig|dump-diskconfig|dump-init|dump-paramsconfig) + ;; + extract|extract-config|extract-disk-file) + ;; + *) + echo "Error: Invalid action $action" + exit 1 + ;; +esac + +# Second argument should be the file name or directory +guestfile="${2:-}" + +# Handle user asking for help on a specific action +case $guestfile in + help|--help|-h|-?) + usage-${action} + exit 0 + ;; +esac + +if [ -z "${guestfile}" ]; then + echo "Error: no GUESTFILE provided" + usage + exit 1 +fi + +shift 2 + +case ${action} in + check) + check_image ${guestfile} + echo "Image is OK" + exit 0 + ;; + dump-paramsconfig) + check_image ${guestfile} + echo "Guest configuration:" + if [ -f ${guestfile} ]; then + tar -xOf ${guestfile} ./params.cfg + else + cat ${guestfile}/params.cfg + fi + exit 0 + ;; + dump-xenconfig) + check_image ${guestfile} + echo "Xen configuration:" + if [ -f ${guestfile} ]; then + tar -xOf ${guestfile} ./guest.cfg + tar -xOf ${guestfile} ./guest.d 2> /dev/null || true + else + cat ${guestfile}/guest.cfg + cat ${guestfile}/guest.d/* 2> /dev/null || true + fi + echo + exit 0 + ;; + dump-diskconfig) + check_image ${guestfile} + echo "Disk configuration:" + if [ -f ${guestfile} ]; then + tar -xOf ${guestfile} ./disk.cfg + else + cat ${guestfile}/disk.cfg + fi + echo + exit 0 + ;; + dump-init) + check_image ${guestfile} + for init in init.d init-pre init-post; do + echo "=== ${init} ===" + if [ -f ${guestfile} ]; then + tar -xOf ${guestfile} ./${init} 2> /dev/null || \ + echo "No ${init} scripts." + else + cat ${guestfile}/${init}/* 2> /dev/null || \ + echo "No ${init} scripts." + fi + echo "===============" + echo + done + exit 0 + ;; + pack) + check_image ${guestfile} + if [ ! -d ${guestfile} ]; then + echo "Error: Pack can only be done on a xenguest directory" + exit 1 + fi + + if [ -z "${1:-}" ] || [ -f ${1} ]; then + echo "Error: No destination file or already existing file" + exit 1 + fi + + tar -C ${guestfile} -cf ${1} . + exit 0 + ;; + extract) + check_image ${guestfile} + if [ -d ${guestfile} ]; then + echo "Error: Cannot extract config from xenguest directory" + exit 1 + fi + + if [ -z "${1:-}" ] || [ ! -d ${1} ]; then + echo "Error: No destination directory for image extract" + exit 1 + fi + + tar -C ${1} -xf ${guestfile} + exit 0 + ;; + extract-config) + check_image ${guestfile} + if [ -d ${guestfile} ]; then + echo "Error: Cannot extract config from xenguest directory" + exit 1 + fi + + if [ -z "${1:-}" ] || [ ! -d ${1} ]; then + echo "Error: No destination directory for config extract" + exit 1 + fi + + #extract all but disk files + tar -C ${1} --exclude='./disk' -xf ${guestfile} + exit 0 + ;; + extract-disk-file) + check_image ${guestfile} + + if [ -d ${guestfile} ]; then + echo "Error: Cannot extract disk file from xenguest directory" >&2 + exit 1 + fi + + if [ -z "${1:-}" ]; then + echo "Error: No file to extract" >&2 + exit 1 + fi + + tar -xOf ${guestfile} ./disk/${1} + exit 0 + ;; + create) + if [ -f ${guestfile} ]; then + echo "Error: File ${guestfile} already exist" + exit 1 + elif [ -d ${guestfile} ]; then + if [ -n "$(ls -A ${guestfile})" ]; then + echo "Error: Directory ${guestfile} is not empty" + exit 1 + fi + IMAGE_TMPDIR=$(realpath -m ${guestfile}) + else + IMAGE_TMPDIR=$(mktemp -d) + fi + + # Create initial content + params_config_reset + xen_config_reset + disk_config_reset + ;; + update) + check_image ${guestfile} + + if [ -f ${guestfile} ]; then + # Extract the image to update it + IMAGE_TMPDIR=$(mktemp -d) + tar -C ${IMAGE_TMPDIR} -xf ${guestfile} + else + IMAGE_TMPDIR=$(realpath -m ${guestfile}) + fi + ;; + partial) + if [ -e ${guestfile} -a ! -d ${guestfile} ]; then + echo "Error: Invalid partial output directory" + exit 1 + fi + mkdir -p ${guestfile} + IMAGE_TMPDIR=$(realpath -m ${guestfile}) + ;; + *) + echo "Invalid action ${action}" + usage + exit 1 + ;; +esac + +# Process command line arguments +for arg in "${@}"; do + case ${arg} in + --*=*) + optarg=$(echo ${arg} | sed -e "s/[^=]*=//") + ;; + *) + optarg="" + ;; + esac + + case ${arg} in + --guest-reset-config) + params_config_reset + ;; + --set-param=*=*) + param_name=$(echo $optarg | sed -e "s/=.*//") + param_value=$(echo $optarg | sed -e "s/[^=]*=//") + params_config_setparam "$param_name" "$param_value" + ;; + --set-param=*) + params_config_setparam "$optarg" + ;; + --xen-reset-config) + xen_config_create + ;; + --xen-name=*) + if [ -z "${optarg}" ]; then + xen_config_disable_param "name" + else + xen_config_set_string "name" "${optarg}" + fi + ;; + --xen-kernel=*) + if [ -z "${optarg}" ]; then + xen_config_disable_param "kernel" + rm -f ${IMAGE_TMPDIR}/files/kernel + else + if [ ! -f ${optarg} ]; then + echo "Error: invalid kernel file ${optarg}" + exit 1 + fi + xen_config_set_string "kernel" "files/kernel" + mkdir -p ${IMAGE_TMPDIR}/files + install -m 644 ${optarg} ${IMAGE_TMPDIR}/files/kernel + fi + ;; + --xen-memory=*) + xen_config_set_number "memory" ${optarg} + ;; + --xen-vcpus=*) + xen_config_set_number "vcpus" ${optarg} + ;; + --xen-clean-extra) + xen_config_set_string "extra" "" + ;; + --xen-extra=*) + xen_config_append_string "extra" ${optarg} + ;; + --xen-root=*) + if [ -z "${optarg}" ]; then + xen_config_disable_param "root" + else + xen_config_set_string "root" "${optarg}" + fi + ;; + --xen-device-tree=*) + if [ -z "${optarg}" ]; then + xen_config_disable_param "device_tree" + rm -f ${IMAGE_TMPDIR}/files/guest.dtb + else + if [ ! -f ${optarg} ]; then + echo "Error: invalid dtb file ${optarg}" + exit 1 + fi + xen_config_set_string "device_tree" "files/guest.dtb" + mkdir -p ${IMAGE_TMPDIR}/files + install -m 644 ${optarg} ${IMAGE_TMPDIR}/files/guest.dtb + fi + ;; + --xen-disk=*) + if [ -z "${optarg}" ]; then + xen_config_disable_param "disk" + else + xen_config_set_list "disk" "phy:${optarg}" "xvda" "w" + fi + ;; + --xen-append=*) + if [ ! -f ${optarg} ]; then + echo "Error: invalid xen append file ${optarg}" + exit 1 + fi + mkdir -p ${IMAGE_TMPDIR}/guest.d + install -m 755 ${optarg} ${IMAGE_TMPDIR}/guest.d/. + ;; + --xen-add-file=*) + src=$(echo "${optarg}" | sed -e "s/:.*//") + dst=$(echo "${optarg}" | sed -e "s/.*://") + if [ ! -f ${src} ]; then + echo "Error: Invalid file: ${src}" + rm -rf ${IMAGE_TMPDIR} + exit 1 + fi + if [ -z "${dst}" ]; then + dst=$(basename ${src}) + fi + mkdir -p ${IMAGE_TMPDIR}/files/$(dirname ${dst}) + cp -f ${src} ${IMAGE_TMPDIR}/files/${dst} + ;; + --xen-rm-file=*) + rm -f ${IMAGE_TMPDIR}/files/${optarg} + ;; + --init-script=*|--init-pre=*|--init-post=*) + dst="" + case $arg in + --init-script=*) + dst="init.d" + ;; + --init-pre=*) + dst="init.pre" + ;; + --init-post=*) + dst="init.post" + ;; + esac + if [ ! -f ${optarg} ]; then + echo "${optarg} does not point to a valid file" + exit 1 + else + mkdir -p ${IMAGE_TMPDIR}/${dst} + install -m 755 ${optarg} ${IMAGE_TMPDIR}/${dst}/. + fi + ;; + --disk-reset-config) + disk_config_reset + ;; + --disk-size=*) + sed -i "s/DISK_SIZE=.*/DISK_SIZE=\"${optarg}\"/" \ + ${IMAGE_TMPDIR}/disk.cfg + ;; + --disk-device=*) + sed -i "s/DISK_DEVICE=.*/DISK_SIZE=\"${optarg}\"/" \ + ${IMAGE_TMPDIR}/disk.cfg + ;; + --disk-add-part=*) + disk_config_add_part ${optarg} + ;; + --disk-rm-part=*) + disk_config_rm_part ${optarg} + ;; + --disk-add-file=*) + src=$(echo "${optarg}" | sed -e "s/:.*//") + dst=$(echo "${optarg}" | sed -e "s/.*://") + if [ ! -f ${src} ]; then + echo "Error: Invalid disk file: ${src}" + rm -rf ${IMAGE_TMPDIR} + exit 1 + fi + if [ -z "${dst}" ]; then + dst=$(basename ${src}) + fi + mkdir -p ${IMAGE_TMPDIR}/disk/$(dirname ${dst}) + cp -f ${src} ${IMAGE_TMPDIR}/disk/${dst} + ;; + --disk-rm-file=*) + rm -f ${IMAGE_TMPDIR}/disk/${optarg} + ;; + *) + echo "Unsupported command: ${arg}" + exit 1 + ;; + esac +done + +if [ ! -d ${guestfile} ]; then + # If the original guest was in a file we need to repack the file + # with the changes we did on it in the IMAGE_TMPDIR + rm -f ${guestfile} + tar -C ${IMAGE_TMPDIR} -cf ${guestfile} . + rm -rf ${IMAGE_TMPDIR} +fi + diff --git a/meta-arm-autonomy/recipes-extended/xenguest/xenguest-mkimage.bb b/meta-arm-autonomy/recipes-extended/xenguest/xenguest-mkimage.bb new file mode 100644 index 00000000..6ea5fb10 --- /dev/null +++ b/meta-arm-autonomy/recipes-extended/xenguest/xenguest-mkimage.bb @@ -0,0 +1,31 @@ +# Xenguest mkimage recipe +# +# xenguest-mkimage is a tool to create/modify images to be used as xen guests +# Produced images contains a xen configuration and several optional components +# (kernel, device-tree, disk definition and files, init scripts) which all +# together fully define a full xen guest + +DESCRIPTION = "Xenguest mkimage tool" +LICENSE = "MIT" + +SRC_URI = "file://xenguest-mkimage" + +LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302" + +S = "${WORKDIR}" + +# Can be built native also to produce xenguest images during Yocto build +BBCLASSEXTEND = "native" + +do_configure[noexec] = "1" +do_compile[noexec] = "1" + +do_install() { + install -d -m 755 ${D}${bindir} + install -m 755 xenguest-mkimage ${D}${bindir}/. +} + +# We need bash and tar +RDEPENDS_${PN} = "bash tar" +FILES_${PN} = "${bindir}/xenguest-mkimage" +