532 lines
15 KiB
Bash
Executable File
532 lines
15 KiB
Bash
Executable File
#!/bin/bash
|
|
#
|
|
# Copyright (c) 2022 Wind River Systems, Inc.
|
|
#
|
|
# SPDX-License-Identifier: Apache-2.0
|
|
#
|
|
# Utility to convert a StarlingX installation iso into a
|
|
# prestaged subcloud installation iso.
|
|
#
|
|
# Docker images can also be added to the iso ,removing the need
|
|
# for each subcloud to download the images independently.
|
|
# Docker images must be in 'docker save' format. Multiple container
|
|
# images can be captured in a single archive. No single archive may
|
|
# exceed 4 GB. Multiple archives can be provided. All archives
|
|
# must have the suffix 'tar.gz'.
|
|
|
|
|
|
# Error log print
|
|
function log_fatal {
|
|
echo "ERROR: $@" >&2 && exit 1
|
|
}
|
|
|
|
function log_error {
|
|
echo "ERROR: $@" >&2
|
|
}
|
|
|
|
# Info log
|
|
function log {
|
|
echo "INFO: $@" >&2
|
|
}
|
|
|
|
# Usage manual.
|
|
function usage {
|
|
cat <<ENDUSAGE
|
|
Utility to convert a StarlingX installation iso into a Debian prestaged
|
|
subcloud installation iso.
|
|
|
|
Usage:
|
|
$(basename $0) --input <input bootimage.iso>
|
|
--output <output bootimage.iso>
|
|
[ --images <images.tar.gz> ]
|
|
[ --patch <patch-name.patch> ]
|
|
[ --kickstart-patch <kickstart-enabler.patch> ]
|
|
[ --addon <ks-addon.cfg> ]
|
|
[ --param <param>=<value> ]
|
|
[ --default-boot <default menu option> ]
|
|
[ --timeout <menu timeout> ]
|
|
[ --force-install ]
|
|
|
|
--input <file>: Specify input ISO file
|
|
--output <file>: Specify output ISO file
|
|
--images <images.tar.gz>:
|
|
Specify a collection of docker images in 'docker save'
|
|
format. This option can be specified more than once,
|
|
or a comma separated list can be used.
|
|
Multiple images can be captured in a single archive.
|
|
No single archive may exceed 4 GB.
|
|
--patch <patch-name.patch>:
|
|
Specify software patch file(s).
|
|
Can be specified more than once, or provide a comma separated list.
|
|
--kickstart-patch <kickstart-enabler.patch>:
|
|
A patch to replace the prestaged installer kickstart.
|
|
Not to be included in the runtime patches.
|
|
|
|
--setup <file>: Specify ks-setup.cfg file.
|
|
--addon <file>: Specify ks-addon.cfg file.
|
|
--param <p=v>: Specify boot parameter(s).
|
|
Can be specified more than once, or provide a comma separated list.
|
|
Examples:
|
|
--param rootfs_device=nvme0n1,boot_device=nvme0n1
|
|
|
|
--param rootfs_device=/dev/disk/by-path/pci-0000:00:0d.0-ata-1.0
|
|
--param boot_device=/dev/disk/by-path/pci-0000:00:0d.0-ata-1.0
|
|
|
|
--default-boot <default menu option>:
|
|
Specify default boot menu option:
|
|
0 - Serial Console
|
|
1 - Graphical Console (default)
|
|
--timeout <menu timeout>:
|
|
Specify boot menu timeout, in seconds. (default 30)
|
|
A value of -1 will wait forever.
|
|
--force-install:
|
|
Force install the prestaged content even if there is already an
|
|
installation on the target.
|
|
ENDUSAGE
|
|
}
|
|
|
|
function cleanup {
|
|
# This is invoked from the trap handler.
|
|
common_cleanup
|
|
}
|
|
|
|
function check_requirements {
|
|
common_check_requirements mkisofs isohybrid cpio cp find
|
|
}
|
|
|
|
function mkdir_on_iso {
|
|
local dir="${1}"
|
|
|
|
local final_dir="${BUILDDIR}/${dir}"
|
|
|
|
mkdir -p "${final_dir}"
|
|
if [ $? -ne 0 ]; then
|
|
log_error "Error: mkdir_on_iso: Failed to create directory '${dir}'"
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
function normalized_path {
|
|
local path="${1}"
|
|
local default_fn="${2}"
|
|
|
|
local path_name="$(basename "${path}")"
|
|
local path_dir="$(dirname "${path}")"
|
|
|
|
# If 'path' ends in '/' then path was intended to be a directory
|
|
if [ "${path:(-1):1}" == "/" ]; then
|
|
# Drop the trailing '/'
|
|
path_dir="${path:0:(-1)}"
|
|
path_name="${default_fn}"
|
|
fi
|
|
|
|
# drop leading '.' from path_dir
|
|
if [ "${path_dir:0:1}" == "." ]; then
|
|
path_dir="${path_dir:1}"
|
|
fi
|
|
|
|
# drop leading '/' from path_dir
|
|
if [ "${path_dir:0:1}" == "/" ]; then
|
|
path_dir="${path_dir:1}"
|
|
fi
|
|
|
|
if [ -z "${path_dir}" ]; then
|
|
echo "${path_name}"
|
|
else
|
|
echo "${path_dir}/${path_name}"
|
|
fi
|
|
}
|
|
|
|
function copy_to_iso {
|
|
local src="${1}"
|
|
local dest="${2}"
|
|
local md5="${3}"
|
|
local overwrite="${4}"
|
|
|
|
local default_dest=
|
|
local final_dest=
|
|
local final_dest_dir=
|
|
local final_md5=
|
|
local final_md5_dir=
|
|
|
|
if [ -z "${src}" ] || [ -z "${dest}" ]; then
|
|
log_error "Error: copy_to_iso: missing argument"
|
|
exit 1
|
|
fi
|
|
|
|
if [ ! -f "${src}" ]; then
|
|
log_error "Error: copy_to_iso: source file doesn't exist '${src}'"
|
|
exit 1
|
|
fi
|
|
|
|
default_dest="$(basename "${src}")"
|
|
dest="$(normalized_path "${dest}" "${default_dest}")"
|
|
final_dest="${BUILDDIR}/${dest}"
|
|
final_dest_dir="$(dirname "${final_dest}")"
|
|
|
|
if [ ! -z "${md5}" ]; then
|
|
|
|
case "${md5}" in
|
|
y | Y | yes | YES )
|
|
# Use a default name, in same dir as dest
|
|
md5="$(dirname "${dest}")"
|
|
;;
|
|
esac
|
|
|
|
final_md5="${BUILDDIR}/${md5}"
|
|
fi
|
|
|
|
if [ -z "${overwrite}" ] || [ "${overwrite}" == 'n' ]; then
|
|
if [ -f "${final_dest}" ]; then
|
|
log_error "Error: copy_to_iso: destination already exists '${final_dest}'"
|
|
exit 1
|
|
fi
|
|
fi
|
|
|
|
if [ ! -d "${final_dest_dir}" ]; then
|
|
log_error "Error: copy_to_iso: destination directory does not exist '${final_dest_dir}'"
|
|
exit 1
|
|
fi
|
|
|
|
cp -f "${src}" "${final_dest}"
|
|
if [ $? -ne 0 ]; then
|
|
log_error "Error: Failed to copy '${src}' to '${final_dest}'"
|
|
exit 1
|
|
fi
|
|
|
|
if [ ! -z "${final_md5}" ]; then
|
|
pushd ${final_dest_dir} > /dev/null
|
|
md5sum "$(basename "${final_dest}")" >> "${final_md5}"
|
|
popd > /dev/null
|
|
fi
|
|
}
|
|
|
|
function generate_boot_cfg {
|
|
local isodir=$1
|
|
|
|
if [ -z "${EFI_MOUNT}" ]; then
|
|
mount_efiboot_img ${isodir}
|
|
fi
|
|
|
|
local PARAM_LIST=
|
|
log "Generating prestage.iso from params: ${PARAMS[*]}"
|
|
# Set/update boot parameters
|
|
if [ ${#PARAMS[@]} -gt 0 ]; then
|
|
for p in "${PARAMS[@]}"; do
|
|
param=${p%%=*}
|
|
value=${p#*=}
|
|
# Pull the boot device out of PARAMS and convert to instdev
|
|
if [ "$param" = "boot_device" ]; then
|
|
log "Setting instdev=$value from boot_device param"
|
|
instdev=$value
|
|
fi
|
|
PARAM_LIST="${PARAM_LIST} ${param}=${value}"
|
|
done
|
|
fi
|
|
|
|
log "Parameters: ${PARAM_LIST}"
|
|
|
|
COMMON_ARGS="initrd=/initrd instdate=@$(date +%s) instw=60 instiso=instboot"
|
|
COMMON_ARGS="${COMMON_ARGS} biosplusefi=1 instnet=0"
|
|
COMMON_ARGS="${COMMON_ARGS} ks=file:///kickstart/kickstart.cfg"
|
|
COMMON_ARGS="${COMMON_ARGS} rdinit=/install instname=debian instbr=starlingx instab=0"
|
|
COMMON_ARGS="${COMMON_ARGS} insturl=file://NOT_SET prestage ip=${BOOT_IP_ARG}"
|
|
COMMON_ARGS="${COMMON_ARGS} BLM=2506 FSZ=32 BSZ=512 RSZ=20480 VSZ=20480 instl=/ostree_repo instdev=${instdev}"
|
|
COMMON_ARGS="${COMMON_ARGS} defaultkernel=vmlinuz*[!t]-amd64"
|
|
|
|
if [[ -n "${FORCE_INSTALL}" ]]; then
|
|
COMMON_ARGS="${COMMON_ARGS} force_install"
|
|
fi
|
|
|
|
# Uncomment for LAT debugging:
|
|
#COMMON_ARGS="${COMMON_ARGS} instsh=2"
|
|
COMMON_ARGS="${COMMON_ARGS} ${PARAM_LIST}"
|
|
log "COMMON_ARGS: $COMMON_ARGS"
|
|
|
|
for f in ${isodir}/isolinux/isolinux.cfg; do
|
|
cat <<EOF > "${f}"
|
|
prompt 0
|
|
timeout ${TIMEOUT}
|
|
allowoptions 1
|
|
serial 0 115200
|
|
|
|
ui vesamenu.c32
|
|
menu background #ff555555
|
|
menu title Debian Local Install : Select kernel options and boot kernel
|
|
menu tabmsg Press [Tab] to edit, [Return] to select
|
|
|
|
DEFAULT ${DEFAULT_SYSLINUX_ENTRY}
|
|
LABEL 0
|
|
menu label Serial Console
|
|
kernel /bzImage-std
|
|
ipappend 2
|
|
append ${COMMON_ARGS} traits=controller console=ttyS0,115200 console=tty0
|
|
|
|
LABEL 1
|
|
menu label Graphical Console
|
|
kernel /bzImage-std
|
|
ipappend 2
|
|
append ${COMMON_ARGS} traits=controller console=tty0
|
|
|
|
EOF
|
|
done
|
|
|
|
for f in ${isodir}/EFI/BOOT/grub.cfg ${EFI_MOUNT}/EFI/BOOT/grub.cfg; do
|
|
cat <<EOF > "${f}"
|
|
default=${DEFAULT_GRUB_ENTRY}
|
|
timeout=${GRUB_TIMEOUT}
|
|
search --no-floppy --set=root -l 'instboot'
|
|
|
|
menuentry 'Serial Console' --id=serial {
|
|
linux /bzImage-std ${COMMON_ARGS} traits=controller console=ttyS0,115200 serial
|
|
initrd /initrd
|
|
}
|
|
|
|
menuentry 'Graphical Console' --id=graphical {
|
|
linux /bzImage-std ${COMMON_ARGS} traits=controller console=tty0
|
|
initrd /initrd
|
|
}
|
|
EOF
|
|
done
|
|
|
|
unmount_efiboot_img
|
|
}
|
|
|
|
# Constants
|
|
DIR_NAME=$(dirname "$0")
|
|
if [[ ! -e "${DIR_NAME}"/stx-iso-utils.sh ]]; then
|
|
log_fatal "${DIR_NAME}/stx-iso-utils.sh does not exist"
|
|
else
|
|
source "${DIR_NAME}"/stx-iso-utils.sh
|
|
fi
|
|
|
|
# Required variables
|
|
declare INPUT_ISO=
|
|
declare OUTPUT_ISO=
|
|
declare -a IMAGES
|
|
declare ORIG_PWD=$PWD
|
|
declare KS_SETUP=
|
|
declare KS_ADDON=
|
|
declare -a PARAMS
|
|
declare -a PATCHES
|
|
declare -a KICKSTART_PATCHES
|
|
declare DEFAULT_LABEL=0
|
|
declare DEFAULT_SYSLINUX_ENTRY=1
|
|
declare DEFAULT_GRUB_ENTRY="graphical"
|
|
declare UPDATE_TIMEOUT="no"
|
|
declare FORCE_INSTALL=
|
|
declare PLATFORM_ROOT="opt/platform-backup"
|
|
declare MD5_FILE="container-image.tar.gz.md5"
|
|
|
|
###############################################################################
|
|
# Get the command line arguments.
|
|
###############################################################################
|
|
|
|
SHORTOPTS=""; LONGOPTS=""
|
|
SHORTOPTS+="i:"; LONGOPTS+="input:,"
|
|
SHORTOPTS+="o:"; LONGOPTS+="output:,"
|
|
SHORTOPTS+="s:"; LONGOPTS+="setup:,"
|
|
SHORTOPTS+="a:"; LONGOPTS+="addon:,"
|
|
SHORTOPTS+="p:"; LONGOPTS+="param:,"
|
|
SHORTOPTS+="P:"; LONGOPTS+="patch:,"
|
|
SHORTOPTS+="K:"; LONGOPTS+="kickstart-patch:,"
|
|
SHORTOPTS+="d:"; LONGOPTS+="default-boot:,"
|
|
SHORTOPTS+="t:"; LONGOPTS+="timeout:,"
|
|
SHORTOPTS+="I:"; LONGOPTS+="images:,"
|
|
SHORTOPTS+="f"; LONGOPTS+="force-install,"
|
|
SHORTOPTS+="h"; LONGOPTS+="help"
|
|
|
|
OPTS=$(getopt -o "${SHORTOPTS}" --long "${LONGOPTS}" --name "$0" -- "$@")
|
|
if [[ "$?" -ne 0 ]]; then
|
|
usage
|
|
log_fatal "Options to $0 not properly parsed"
|
|
fi
|
|
|
|
eval set -- "${OPTS}"
|
|
|
|
if [[ $# == 1 ]]; then
|
|
usage
|
|
log_fatal "No arguments were provided"
|
|
fi
|
|
|
|
while :; do
|
|
case $1 in
|
|
-i | --input)
|
|
INPUT_ISO="$2"
|
|
shift 2
|
|
;;
|
|
-o | --output)
|
|
OUTPUT_ISO=$2
|
|
shift 2
|
|
;;
|
|
-s | --setup)
|
|
KS_SETUP=$2
|
|
shift 2
|
|
;;
|
|
-a | --addon)
|
|
KS_ADDON=$2
|
|
shift 2
|
|
;;
|
|
-p | --param)
|
|
PARAMS+=(${2//,/ })
|
|
shift 2
|
|
;;
|
|
-P | --patch)
|
|
PATCHES+=(${2//,/ })
|
|
shift 2
|
|
;;
|
|
-K | --kickstart-patch)
|
|
KICKSTART_PATCHES+=(${2//,/ })
|
|
shift 2
|
|
;;
|
|
-I | --images)
|
|
IMAGES+=(${2//,/ })
|
|
shift 2
|
|
;;
|
|
-d | --default-boot)
|
|
DEFAULT_LABEL=$2
|
|
case ${DEFAULT_LABEL} in
|
|
0)
|
|
DEFAULT_SYSLINUX_ENTRY=0
|
|
DEFAULT_GRUB_ENTRY="serial"
|
|
;;
|
|
1)
|
|
DEFAULT_SYSLINUX_ENTRY=1
|
|
DEFAULT_GRUB_ENTRY="graphical"
|
|
;;
|
|
*)
|
|
usage
|
|
log_fatal "Invalid default boot menu option: ${DEFAULT_LABEL}"
|
|
;;
|
|
esac
|
|
shift 2
|
|
;;
|
|
-t | --timeout)
|
|
let -i timeout_arg=$2
|
|
if [[ "${timeout_arg}" -gt 0 ]]; then
|
|
let -i "TIMEOUT=${timeout_arg}*10"
|
|
GRUB_TIMEOUT="${timeout_arg}"
|
|
elif [[ "${timeout_arg}" -eq 0 ]]; then
|
|
TIMEOUT=0
|
|
GRUB_TIMEOUT=0.001
|
|
elif [[ "${timeout_arg}" -lt 0 ]]; then
|
|
TIMEOUT=0
|
|
GRUB_TIMEOUT=${FOREVER_GRUB_TIMEOUT}
|
|
fi
|
|
UPDATE_TIMEOUT="yes"
|
|
shift 2
|
|
;;
|
|
-f | --force-install)
|
|
FORCE_INSTALL="true"
|
|
shift
|
|
;;
|
|
--)
|
|
break
|
|
;;
|
|
*)
|
|
shift
|
|
break
|
|
;;
|
|
esac
|
|
done
|
|
|
|
|
|
###############################################################################
|
|
# Generate prestage iso.
|
|
#
|
|
###############################################################################
|
|
check_requirements
|
|
|
|
## Check for mandatory parameters
|
|
check_required_param "--input" "${INPUT_ISO}"
|
|
check_required_param "--output" "${OUTPUT_ISO}"
|
|
|
|
check_files_exist ${INPUT_ISO} ${PATCHES[@]} ${IMAGES[@]} ${KS_SETUP} ${KS_ADDON}
|
|
check_files_size ${INPUT_ISO} ${PATCHES[@]} ${IMAGES[@]} ${KS_SETUP} ${KS_ADDON}
|
|
|
|
if [[ -e "${OUTPUT_ISO}" ]]; then
|
|
log_fatal "${OUTPUT_ISO} exists. Delete before you execute this script."
|
|
fi
|
|
|
|
## Catch Control-C and handle.
|
|
trap cleanup EXIT
|
|
|
|
# Create a temporary build directory.
|
|
BUILDDIR=$(mktemp -d -p $PWD updateiso_build_XXXXXX)
|
|
if [ -z "${BUILDDIR}" -o ! -d ${BUILDDIR} ]; then
|
|
log_fatal "Failed to create builddir. Aborting..."
|
|
fi
|
|
echo ${BUILDDIR}
|
|
mount_iso "${INPUT_ISO}"
|
|
|
|
#
|
|
# Determine release version from ISO
|
|
#
|
|
if [ ! -f ${MNTDIR}/upgrades/version ]; then
|
|
log_error "Version info not found on ${INPUT_ISO}"
|
|
exit 1
|
|
fi
|
|
|
|
ISO_VERSION=$(source ${MNTDIR}/upgrades/version && echo ${VERSION})
|
|
if [ -z "${ISO_VERSION}" ]; then
|
|
log_error "Failed to determine version of installation ISO"
|
|
exit 1
|
|
fi
|
|
|
|
# Copy the contents of the input iso to the build directory.
|
|
# This ensures that the ostree, kernel and the initramfs are all copied over
|
|
# to the prestage iso.
|
|
|
|
rsync -a --exclude "pxeboot" "${MNTDIR}/" "${BUILDDIR}/"
|
|
rc=$?
|
|
if [[ "${rc}" -ne 0 ]]; then
|
|
unmount_iso
|
|
log_fatal "Unable to rsync content from the ISO: Error rc=${rc}"
|
|
fi
|
|
|
|
unmount_iso
|
|
|
|
#
|
|
# Copy ISO, patches, and docker image bundles to /opt on the iso.
|
|
# These will be processed by the prestaged installer kickstart.
|
|
# RPM has no role in the installation of these files.
|
|
#
|
|
PLATFORM_PATH="${PLATFORM_ROOT}/${ISO_VERSION}"
|
|
mkdir_on_iso "${PLATFORM_PATH}"
|
|
|
|
for PATCH in ${PATCHES[@]}; do
|
|
copy_to_iso "${PATCH}" "${PLATFORM_PATH}/"
|
|
done
|
|
|
|
for IMAGE in ${IMAGES[@]}; do
|
|
copy_to_iso "${IMAGE}" "${PLATFORM_PATH}/" "${PLATFORM_PATH}/${MD5_FILE}"
|
|
done
|
|
|
|
# generate the grub and isolinux cmd line parameters
|
|
|
|
generate_boot_cfg "${BUILDDIR}"
|
|
# copy the addon and setup files to the BUILDDIR
|
|
|
|
if [[ -e "${KS_SETUP}" ]]; then
|
|
cp "${KS_SETUP}" "${BUILDDIR}"
|
|
fi
|
|
|
|
if [[ -e "${KS_ADDON}" ]]; then
|
|
cp "${KS_ADDON}" "${BUILDDIR}"
|
|
fi
|
|
|
|
# we are ready to create the prestage iso.
|
|
|
|
mkisofs -o "${OUTPUT_ISO}" \
|
|
-A 'instboot' -V 'instboot' \
|
|
-quiet -U -J -joliet-long -r -iso-level 2 \
|
|
-b isolinux/isolinux.bin -c isolinux/boot.cat -no-emul-boot \
|
|
-boot-load-size 4 -boot-info-table \
|
|
-eltorito-alt-boot \
|
|
-e efi.img \
|
|
-no-emul-boot \
|
|
"${BUILDDIR}"
|
|
|
|
isohybrid --uefi "${OUTPUT_ISO}"
|
|
|