842 lines
26 KiB
Bash
Executable File
842 lines
26 KiB
Bash
Executable File
#!/bin/bash
|
|
# vim: filetype=sh shiftwidth=4 expandtab
|
|
#
|
|
# Copyright (c) 2020-2023 Wind River Systems, Inc.
|
|
#
|
|
# SPDX-License-Identifier: Apache-2.0
|
|
#
|
|
# Utility for setting up a mini ISO and boot structure to support a
|
|
# hybrid boot that combines an ISO and network boot, where:
|
|
# - mini ISO contains kernel and initrd, with boot parameters
|
|
# configured to access everything else from network
|
|
# - setup rootfs (squashfs.img), kickstart, and software repositories
|
|
# under an http/https served directory
|
|
#
|
|
#
|
|
readonly SCRIPTDIR=$(readlink -m "$(dirname "$0")")
|
|
readonly SCRIPTNAME=$(basename "$0")
|
|
|
|
# Source shared utility functions
|
|
# shellcheck disable=SC1090 # ignore source warning
|
|
source "$SCRIPTDIR"/stx-iso-utils-centos.sh
|
|
|
|
ADDON=
|
|
BASE_URL=
|
|
BOOT_ARGS_COMMON=
|
|
BOOT_GATEWAY=
|
|
BOOT_HOSTNAME=
|
|
BOOT_INTERFACE=
|
|
BOOT_IP=
|
|
BOOT_NETMASK=
|
|
CLEAN_NODE_DIR="no"
|
|
CLEAN_SHARED_DIR="no"
|
|
DEFAULT_GRUB_ENTRY=
|
|
DEFAULT_SYSLINUX_ENTRY=
|
|
DELETE="no"
|
|
GRUB_TIMEOUT=-1
|
|
INPUT_ISO=
|
|
INSTALL_TYPE=
|
|
ISO_VERSION=
|
|
KS_NODETYPE=
|
|
LOCK_FILE=/var/run/.gen-bootloader-iso.lock
|
|
LOCK_TMOUT=600 # Wait up to 10 minutes, by default
|
|
LOG_TAG=$SCRIPTNAME
|
|
NODE_ID=
|
|
NODE_URL=
|
|
OUTPUT_ISO=
|
|
PATCHES_FROM_HOST="yes"
|
|
SCRATCH_DIR=${SCRATCH_DIR:-/scratch}
|
|
TIMEOUT=0
|
|
UPDATE_TIMEOUT="no"
|
|
VERBOSE=${VERBOSE:-}
|
|
VERBOSE_LOG_DIR=/var/log/dcmanager/miniboot
|
|
VERBOSE_OVERRIDE_FILE=/tmp/gen-bootloader-verbose # turn on verbose if this file is present
|
|
WWW_ROOT_DIR=
|
|
|
|
declare -a PARAMS
|
|
|
|
# Initialized via initialize_and_lock:
|
|
BUILDDIR=
|
|
NODE_DIR=
|
|
NODE_DIR_BASE=
|
|
VERBOSE_RSYNC=
|
|
WORKDIR=
|
|
|
|
# Initialized by stx-iso-utils-centos.sh:mount_efiboot_img
|
|
EFI_MOUNT=
|
|
|
|
# Set this to a directory path containing kickstart *.cfg script(s) for testing:
|
|
KICKSTART_OVERRIDE_DIR=${KICKSTART_OVERRIDE_DIR:-/var/miniboot/kickstart-override-centos}
|
|
|
|
function log_verbose {
|
|
if [ -n "$VERBOSE" ]; then
|
|
echo "$@"
|
|
fi
|
|
}
|
|
|
|
function log_info {
|
|
echo "$@"
|
|
}
|
|
|
|
function log_error {
|
|
logger -i -s -t "${LOG_TAG}" -- "ERROR: $*"
|
|
}
|
|
|
|
function log_warn {
|
|
logger -i -s -t "${LOG_TAG}" -- "WARN: $*"
|
|
}
|
|
|
|
function fatal_error {
|
|
logger -i -s -t "${LOG_TAG}" -- "FATAL: $*"
|
|
exit 1
|
|
}
|
|
|
|
function check_rc_exit {
|
|
local rc=$1
|
|
shift
|
|
if [ "$rc" -ne 0 ]; then
|
|
logger -i -s -t "${LOG_TAG}" -- "FATAL: $* [exit: $rc]"
|
|
exit "$rc"
|
|
fi
|
|
}
|
|
|
|
function get_os {
|
|
local os
|
|
os=$(awk -F '=' '/^ID=/ { print $2; }' /etc/os-release)
|
|
case "$os" in
|
|
*debian*)
|
|
echo debian
|
|
;;
|
|
*centos*)
|
|
echo centos
|
|
;;
|
|
*)
|
|
echo "$os"
|
|
;;
|
|
esac
|
|
}
|
|
|
|
function get_path_size {
|
|
local path=$1
|
|
du -hs "$path" | awk '{print $1}'
|
|
}
|
|
|
|
function log_path_size {
|
|
local path=$1
|
|
local msg=$2
|
|
log_info "$msg: $(get_path_size "$path")"
|
|
}
|
|
|
|
function usage {
|
|
cat <<ENDUSAGE
|
|
Description: Sets up a mini bootimage.iso that includes the minimum required to
|
|
retrieve the rootfs and software packages needed for installation via http or
|
|
https, generated for a specific node.
|
|
|
|
Mandatory parameters for setup:
|
|
--input <file>: Specify input ISO file
|
|
--www-root <dir>: Specify www-serviced directory (for target mini bootimage.iso)
|
|
--base-url <url>: Specify URL for www-root dir
|
|
--id <node id>: Specify ID for target node (typically subcloud name)
|
|
--boot-interface <intf>: Specify target node boot interface
|
|
--boot-ip <ip address>: Specify address for boot interface
|
|
--default-boot <0-5>: Specify install type:
|
|
0 - Standard Controller, Serial Console
|
|
1 - Standard Controller, Graphical Console
|
|
2 - AIO, Serial Console
|
|
3 - AIO, Graphical Console
|
|
4 - AIO Low-latency, Serial Console
|
|
5 - AIO Low-latency, Graphical Console
|
|
|
|
Optional parameters for setup:
|
|
--addon <file>: Specify custom kickstart %post addon, for
|
|
post-install interface config
|
|
--boot-hostname <host>: Specify temporary hostname for target node
|
|
--boot-netmask <mask>: Specify netmask for boot interface
|
|
--boot-gateway <addr>: Specify gateway for boot interface
|
|
--timeout <seconds>: Specify boot menu timeout, in seconds
|
|
--lock-timeout <secs>: Specify time to wait for mutex lock before aborting
|
|
--patches-from-iso: Use patches from the ISO, if any, rather than host
|
|
--param <p=v>: Specify boot parameter customization
|
|
Examples:
|
|
--param rootfs_device=nvme0n1 --param 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
|
|
|
|
Generated ISO will be: <www-root>/nodes/<node-id>/bootimage.iso
|
|
|
|
Mandatory parameters for cleanup:
|
|
--www-root <dir>: Specify www-serviced directory
|
|
--id <node id>: Specify ID for target node
|
|
--delete: Request file deletion
|
|
|
|
Example kickstart addon, to define a VLAN on initial OAM interface setup:
|
|
#### start custom kickstart
|
|
OAM_DEV=enp0s3
|
|
OAM_VLAN=1234
|
|
|
|
cat << EOF > /etc/sysconfig/network-scripts/ifcfg-\$OAM_DEV
|
|
DEVICE=\$OAM_DEV
|
|
BOOTPROTO=none
|
|
ONBOOT=yes
|
|
LINKDELAY=20
|
|
EOF
|
|
|
|
cat << EOF > /etc/sysconfig/network-scripts/ifcfg-\$OAM_DEV.\$OAM_VLAN
|
|
DEVICE=\$OAM_DEV.\$OAM_VLAN
|
|
BOOTPROTO=dhcp
|
|
ONBOOT=yes
|
|
VLAN=yes
|
|
LINKDELAY=20
|
|
EOF
|
|
#### end custom kickstart
|
|
|
|
ENDUSAGE
|
|
}
|
|
|
|
#
|
|
# Functions
|
|
#
|
|
|
|
function parse_arguments {
|
|
# Parse cmdline arguments
|
|
local longopts opts
|
|
longopts="input:,addon:,param:,default-boot:,timeout:,lock-timeout:,patches-from-iso"
|
|
longopts="${longopts},base-url:,www-root:,id:,delete"
|
|
longopts="${longopts},boot-gateway:,boot-hostname:,boot-interface:,boot-ip:,boot-netmask:"
|
|
longopts="${longopts},verbose,help"
|
|
|
|
opts=$(getopt -o h --long "${longopts}" --name "$0" -- "$@")
|
|
# shellcheck disable=SC2181 # prefer to check exit code:
|
|
if [ $? -ne 0 ]; then
|
|
usage
|
|
exit 1
|
|
fi
|
|
|
|
eval set -- "${opts}"
|
|
|
|
while :; do
|
|
case "$1" in
|
|
--input)
|
|
INPUT_ISO=$2
|
|
shift 2
|
|
;;
|
|
--addon)
|
|
ADDON=$2
|
|
shift 2
|
|
;;
|
|
--boot-gateway)
|
|
BOOT_GATEWAY=$2
|
|
shift 2
|
|
;;
|
|
--boot-hostname)
|
|
BOOT_HOSTNAME=$2
|
|
shift 2
|
|
;;
|
|
--boot-interface)
|
|
BOOT_INTERFACE=$2
|
|
shift 2
|
|
;;
|
|
--boot-ip)
|
|
BOOT_IP=$2
|
|
shift 2
|
|
;;
|
|
--boot-netmask)
|
|
BOOT_NETMASK=$2
|
|
shift 2
|
|
;;
|
|
--param)
|
|
PARAMS+=($2)
|
|
shift 2
|
|
;;
|
|
--default-boot)
|
|
INSTALL_TYPE=$2
|
|
shift 2
|
|
case ${INSTALL_TYPE} in
|
|
0)
|
|
DEFAULT_SYSLINUX_ENTRY=0
|
|
DEFAULT_GRUB_ENTRY=serial
|
|
KS_NODETYPE='controller'
|
|
;;
|
|
1)
|
|
DEFAULT_SYSLINUX_ENTRY=1
|
|
DEFAULT_GRUB_ENTRY=graphical
|
|
KS_NODETYPE='controller'
|
|
;;
|
|
2)
|
|
DEFAULT_SYSLINUX_ENTRY=0
|
|
DEFAULT_GRUB_ENTRY=serial
|
|
KS_NODETYPE='smallsystem'
|
|
;;
|
|
3)
|
|
DEFAULT_SYSLINUX_ENTRY=1
|
|
DEFAULT_GRUB_ENTRY=graphical
|
|
KS_NODETYPE='smallsystem'
|
|
;;
|
|
4)
|
|
DEFAULT_SYSLINUX_ENTRY=0
|
|
DEFAULT_GRUB_ENTRY=serial
|
|
KS_NODETYPE='smallsystem_lowlatency'
|
|
;;
|
|
5)
|
|
DEFAULT_SYSLINUX_ENTRY=1
|
|
DEFAULT_GRUB_ENTRY=graphical
|
|
KS_NODETYPE='smallsystem_lowlatency'
|
|
;;
|
|
*)
|
|
log_error "Invalid default boot menu option: ${INSTALL_TYPE}"
|
|
usage
|
|
exit 1
|
|
;;
|
|
esac
|
|
;;
|
|
--timeout)
|
|
local -i timeout_arg=$2
|
|
shift 2
|
|
if [ ${timeout_arg} -gt 0 ]; then
|
|
TIMEOUT=$(( timeout_arg * 10 ))
|
|
GRUB_TIMEOUT=${timeout_arg}
|
|
elif [ ${timeout_arg} -eq 0 ]; then
|
|
GRUB_TIMEOUT=0.001
|
|
fi
|
|
UPDATE_TIMEOUT="yes"
|
|
;;
|
|
--www-root)
|
|
WWW_ROOT_DIR=$2
|
|
shift 2
|
|
;;
|
|
--base-url)
|
|
BASE_URL=$2
|
|
shift 2
|
|
;;
|
|
--id)
|
|
NODE_ID=$2
|
|
shift 2
|
|
;;
|
|
--lock-timeout)
|
|
local -i LOCK_TMOUT=$2
|
|
shift 2
|
|
if [ "${LOCK_TMOUT}" -le 0 ]; then
|
|
echo "Lock timeout must be greater than 0" >&2
|
|
exit 1
|
|
fi
|
|
;;
|
|
--delete)
|
|
DELETE="yes"
|
|
shift
|
|
;;
|
|
--patches-from-iso)
|
|
PATCHES_FROM_HOST="no"
|
|
shift
|
|
;;
|
|
--verbose)
|
|
VERBOSE=1
|
|
shift
|
|
;;
|
|
--)
|
|
shift
|
|
break
|
|
;;
|
|
*)
|
|
usage
|
|
exit 1
|
|
;;
|
|
esac
|
|
done
|
|
}
|
|
|
|
function get_lock {
|
|
# Grab the lock, to protect against simultaneous execution
|
|
# Open $LOCK_FILE for reading, with assigned file handle 200
|
|
exec 200>${LOCK_FILE}
|
|
flock -w "${LOCK_TMOUT}" 200
|
|
check_rc_exit $? "Failed waiting for lock: ${LOCK_FILE}"
|
|
}
|
|
|
|
function initialize_and_lock {
|
|
check_requirements
|
|
|
|
# Check mandatory parameters
|
|
check_required_param "--id" "${NODE_ID}"
|
|
check_required_param "--www-root" "${WWW_ROOT_DIR}"
|
|
[ -d "${WWW_ROOT_DIR}" ] || fatal_error "Root directory ${WWW_ROOT_DIR} does not exist"
|
|
|
|
[ -f "${VERBOSE_OVERRIDE_FILE}" ] && VERBOSE=1
|
|
if [ -n "${VERBOSE}" ]; then
|
|
VERBOSE_RSYNC="--verbose"
|
|
|
|
# log all output to file
|
|
if [ ! -d "$(dirname "${VERBOSE_LOG_DIR}")" ]; then
|
|
# For testing: the base directory does not exist - use /tmp instead
|
|
VERBOSE_LOG_DIR=/tmp/miniboot
|
|
fi
|
|
[ -d "${VERBOSE_LOG_DIR}" ] || mkdir -p "${VERBOSE_LOG_DIR}"
|
|
local logfile="${VERBOSE_LOG_DIR}/gen-bootloader-iso-centos-${NODE_ID}.log"
|
|
[ -f "${logfile}" ] && rm -f "${logfile}"
|
|
touch "${logfile}"
|
|
echo "Verbose: logging output to ${logfile}"
|
|
echo "$(date) Starting $0"
|
|
printenv >> "${logfile}"
|
|
exec > >(tee --append "${logfile}") 2>&1
|
|
fi
|
|
|
|
# Initialize dynamic variables
|
|
NODE_DIR_BASE="${WWW_ROOT_DIR}/nodes"
|
|
NODE_DIR="${NODE_DIR_BASE}/${NODE_ID}"
|
|
SHARED_DIR="${WWW_ROOT_DIR}/shared"
|
|
|
|
if [ ! -d "$SCRATCH_DIR" ]; then
|
|
log_warn "SCRATCH_DIR does not exist, using /tmp"
|
|
SCRATCH_DIR=/tmp
|
|
fi
|
|
|
|
get_lock
|
|
|
|
# Check for deletion
|
|
if [ ${DELETE} = "yes" ]; then
|
|
handle_delete
|
|
exit 0
|
|
fi
|
|
|
|
# Handle extraction and setup
|
|
check_required_param "--input" "${INPUT_ISO}"
|
|
check_required_param "--default-boot" "${DEFAULT_GRUB_ENTRY}"
|
|
check_required_param "--base-url" "${BASE_URL}"
|
|
check_required_param "--boot-ip" "${BOOT_IP}"
|
|
check_required_param "--boot-interface" "${BOOT_INTERFACE}"
|
|
|
|
NODE_URL="${BASE_URL%\/}/nodes/${NODE_ID}"
|
|
|
|
if [ ! -f "${INPUT_ISO}" ]; then
|
|
fatal_error "Input file does not exist: ${INPUT_ISO}"
|
|
fi
|
|
if [ -d "${NODE_DIR}" ]; then
|
|
fatal_error "Output dir already exists: ${NODE_DIR}"
|
|
fi
|
|
|
|
# Run cleanup on any exit
|
|
trap cleanup_on_exit EXIT
|
|
|
|
BUILDDIR=$(mktemp -d -p "${SCRATCH_DIR}" gen_bootloader_build_XXXXXX)
|
|
if [ -z "${BUILDDIR}" ] || [ ! -d "${BUILDDIR}" ]; then
|
|
fatal_error "Failed to create builddir: ${BUILDDIR}"
|
|
fi
|
|
|
|
WORKDIR=$(mktemp -d -p "${SCRATCH_DIR}" gen_bootloader_workdir_XXXXXX)
|
|
if [ -z "${WORKDIR}" ] || [ ! -d "${WORKDIR}" ]; then
|
|
fatal_error "Failed to create WORKDIR directory: $WORKDIR"
|
|
fi
|
|
}
|
|
|
|
function generate_boot_cfg {
|
|
local isodir=$1
|
|
|
|
if [ -z "${EFI_MOUNT}" ]; then
|
|
mount_efiboot_img ${isodir}
|
|
fi
|
|
|
|
local KS_URL="${NODE_URL}/miniboot_${KS_NODETYPE}.cfg"
|
|
local BOOT_IP_ARG="${BOOT_IP}::${BOOT_GATEWAY}:${BOOT_NETMASK}:${BOOT_HOSTNAME}:${BOOT_INTERFACE}:none"
|
|
|
|
BOOT_ARGS_COMMON="inst.text inst.gpt boot_device=sda rootfs_device=sda"
|
|
BOOT_ARGS_COMMON="${BOOT_ARGS_COMMON} biosdevname=0 usbcore.autosuspend=-1"
|
|
BOOT_ARGS_COMMON="${BOOT_ARGS_COMMON} security_profile=standard user_namespace.enable=1"
|
|
BOOT_ARGS_COMMON="${BOOT_ARGS_COMMON} inst.repo=${NODE_URL} inst.stage2=${NODE_URL} inst.ks=${KS_URL}"
|
|
BOOT_ARGS_COMMON="${BOOT_ARGS_COMMON} ip=${BOOT_IP_ARG}"
|
|
|
|
log_info "Using boot parameters: ${BOOT_ARGS_COMMON}"
|
|
log_verbose "Generating isolinux.cfg/syslinux.cfg, default: $DEFAULT_SYSLINUX_ENTRY, timeout: $TIMEOUT"
|
|
for f in "${isodir}/isolinux.cfg" "${isodir}/syslinux.cfg"; do
|
|
cat <<EOF > "${f}"
|
|
display splash.cfg
|
|
timeout ${TIMEOUT}
|
|
F1 help.txt
|
|
F2 devices.txt
|
|
F3 splash.cfg
|
|
serial 0 115200
|
|
ui vesamenu.c32
|
|
menu background #ff555555
|
|
|
|
default ${DEFAULT_SYSLINUX_ENTRY}
|
|
|
|
menu begin
|
|
menu title ${NODE_ID}
|
|
|
|
# Serial Console submenu
|
|
label 0
|
|
menu label Serial Console
|
|
kernel vmlinuz
|
|
initrd initrd.img
|
|
append rootwait ${BOOT_ARGS_COMMON} console=ttyS0,115200 serial
|
|
|
|
# Graphical Console submenu
|
|
label 1
|
|
menu label Graphical Console
|
|
kernel vmlinuz
|
|
initrd initrd.img
|
|
append rootwait ${BOOT_ARGS_COMMON} console=tty0
|
|
menu end
|
|
|
|
EOF
|
|
done
|
|
|
|
log_verbose "Generating grub.cfg, install_type: ${INSTALL_TYPE}, default: ${DEFAULT_GRUB_ENTRY}, timeout: ${GRUB_TIMEOUT}"
|
|
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 'oe_iso_boot'
|
|
|
|
menuentry "CentOS Miniboot Install ${NODE_ID}" {
|
|
echo " "
|
|
}
|
|
|
|
menuentry 'Serial Console' --id=serial {
|
|
linuxefi /vmlinuz ${BOOT_ARGS_COMMON} console=ttyS0,115200 serial
|
|
initrdefi /initrd.img
|
|
}
|
|
|
|
menuentry 'Graphical Console' --id=graphical {
|
|
linuxefi /vmlinuz ${BOOT_ARGS_COMMON} console=tty0
|
|
initrdefi /initrd.img
|
|
}
|
|
EOF
|
|
done
|
|
}
|
|
|
|
function cleanup_on_exit {
|
|
# This is invoked from the trap handler.
|
|
# The last exit code is used to determine if we are exiting
|
|
# in failed state (non-zero exit), in which case we do the
|
|
# full cleanup. Disable the warning here since we are
|
|
# invoked as a trap handler
|
|
# shellcheck disable=SC2181 # Check exit code directly...
|
|
if [ $? -ne 0 ]; then
|
|
log_info "Cleanup on failure"
|
|
handle_delete
|
|
fi
|
|
common_cleanup
|
|
}
|
|
|
|
function check_requirements {
|
|
common_check_requirements
|
|
}
|
|
|
|
function handle_delete {
|
|
# Remove node-specific files
|
|
if [ -d "${NODE_DIR}" ]; then
|
|
rm -rf "${NODE_DIR}"
|
|
fi
|
|
|
|
# If there are no more nodes, cleanup everything else
|
|
# shellcheck disable=SC2012
|
|
if [ "$(ls -A "${NODE_DIR_BASE}" 2>/dev/null | wc -l)" = 0 ]; then
|
|
if [ -d "${NODE_DIR_BASE}" ]; then
|
|
rmdir "${NODE_DIR_BASE}"
|
|
fi
|
|
|
|
if [ -d "${SHARED_DIR}" ]; then
|
|
rm -rf "${SHARED_DIR}"
|
|
fi
|
|
fi
|
|
|
|
# TODO(kmacleod): do we need this?
|
|
# Mark the DNF cache expired
|
|
dnf clean expire-cache
|
|
}
|
|
|
|
function get_patches_from_host {
|
|
local host_patch_repo=/var/www/pages/updates/rel-${ISO_VERSION}
|
|
|
|
if [ ! -d "${host_patch_repo}" ]; then
|
|
log_error "Patch repo not found: ${host_patch_repo}"
|
|
# Don't fail, as there could be scenarios where there's nothing on
|
|
# the host related to the release on the ISO
|
|
return
|
|
fi
|
|
|
|
mkdir -p "${SHARED_DIR}/patches"
|
|
check_rc_exit $? "Failed to create directory: ${SHARED_DIR}/patches"
|
|
|
|
rsync -a "${host_patch_repo}/repodata" "${SHARED_DIR}/patches/"
|
|
check_rc_exit $? "Failed to copy ${host_patch_repo}/repodata"
|
|
|
|
if [ -d "${host_patch_repo}/Packages" ]; then
|
|
rsync -a "${host_patch_repo}/Packages" "${SHARED_DIR}/patches/"
|
|
check_rc_exit $? "Failed to copy ${host_patch_repo}/Packages"
|
|
elif [ ! -d "${SHARED_DIR}/patches/Packages" ]; then
|
|
# Create an empty Packages dir
|
|
mkdir "${SHARED_DIR}/patches/Packages"
|
|
check_rc_exit $? "Failed to create ${SHARED_DIR}/patches/Packages"
|
|
fi
|
|
|
|
mkdir -p \
|
|
"${SHARED_DIR}/patches/metadata/available" \
|
|
"${SHARED_DIR}/patches/metadata/applied" \
|
|
"${SHARED_DIR}/patches/metadata/committed"
|
|
check_rc_exit $? "Failed to create director(ies): ${SHARED_DIR}/patches/metadata/..."
|
|
|
|
local metadata_to_copy=
|
|
for state in applied committed; do
|
|
if [ ! -d /opt/patching/metadata/${state} ]; then
|
|
continue
|
|
fi
|
|
|
|
metadata_to_copy=$(find /opt/patching/metadata/${state} -type f -exec grep -q "<sw_version>${ISO_VERSION}</sw_version>" {} \; -print)
|
|
if [ -n "${metadata_to_copy}" ]; then
|
|
rsync -a "${metadata_to_copy}" "${SHARED_DIR}/patches/metadata/${state}/"
|
|
check_rc_exit $? "Failed to copy ${state} patch metadata"
|
|
fi
|
|
done
|
|
}
|
|
|
|
function query_patched_pkg {
|
|
local pkg=$1
|
|
local pkg_location=
|
|
local shared_patch_repo=${SHARED_DIR}/patches
|
|
|
|
pkg_location=$(dnf repoquery --disablerepo=* --repofrompath local,file:///${shared_patch_repo} --latest-limit=1 --location -q ${pkg})
|
|
if [ $? -eq 0 -a -n "${pkg_location}" ]; then
|
|
echo ${pkg_location/file:\/\/\//}
|
|
fi
|
|
}
|
|
|
|
function extract_pkg_to_workdir {
|
|
local pkg=$1
|
|
local pkgfile=
|
|
|
|
pkgfile=$(query_patched_pkg ${pkg})
|
|
if [ -z "${pkgfile}" ]; then
|
|
# Nothing to do
|
|
return
|
|
fi
|
|
|
|
[ -f "${pkgfile}" ] || fatal_error "File doesn't exist, unable to extract: ${pkgfile}"
|
|
|
|
pushd "${WORKDIR}" >/dev/null
|
|
log_info "Extracting files from ${pkgfile}"
|
|
rpm2cpio ${pkgfile} | cpio -idmv
|
|
check_rc_exit $? "Failed to extract files from ${pkgfile}"
|
|
popd >/dev/null
|
|
}
|
|
|
|
function extract_shared_files {
|
|
if [ -d "${SHARED_DIR}" ]; then
|
|
# If the shared dir already exists, assume we don't need to re-extract
|
|
return
|
|
fi
|
|
|
|
mkdir -p "${SHARED_DIR}"
|
|
check_rc_exit $? "Failed to create directory: ${SHARED_DIR}"
|
|
|
|
# Check ISO content
|
|
[ -f "${MNTDIR}/LiveOS/squashfs.img" ] || fatal_error "squashfs.img not found on ${INPUT_ISO}"
|
|
|
|
# Setup shared patch data
|
|
if [ "${PATCHES_FROM_HOST}" = "yes" ]; then
|
|
get_patches_from_host
|
|
else
|
|
if [ -d "${MNTDIR}/patches" ]; then
|
|
rsync -a "${MNTDIR}/patches/" "${SHARED_DIR}/patches/"
|
|
check_rc_exit $? "Failed to copy patches repo from ${INPUT_ISO}"
|
|
fi
|
|
fi
|
|
|
|
# Mark the DNF cache expired, in case there was previous ad-hoc repo data
|
|
dnf clean expire-cache
|
|
|
|
local squashfs_img_file=${MNTDIR}/LiveOS/squashfs.img
|
|
if [ "${PATCHES_FROM_HOST}" = "yes" ]; then
|
|
extract_pkg_to_workdir 'pxe-network-installer'
|
|
|
|
local patched_squashfs_img_file=${WORKDIR}/var/www/pages/feed/rel-${ISO_VERSION}/LiveOS/squashfs.img
|
|
if [ -f "${patched_squashfs_img_file}" ]; then
|
|
# Use the patched squashfs.img
|
|
squashfs_img_file=${patched_squashfs_img_file}
|
|
fi
|
|
fi
|
|
|
|
mkdir "${SHARED_DIR}/LiveOS"
|
|
rsync -a "${squashfs_img_file}" "${SHARED_DIR}/LiveOS/"
|
|
check_rc_exit $? "Failed to copy rootfs: ${patched_squashfs_img_file}"
|
|
|
|
# The CentOS kickstart files are on the system controller in their own directory.
|
|
# Copy them into miniboot ISO.
|
|
[ -f /etc/build.info ] || fatal_error "File /etc/build.info does not exist. Cannot determine software version."
|
|
source /etc/build.info
|
|
[ -n "$SW_VERSION" ] || fatal_error "SW_VERSION is not in /etc/build.info. Cannot determine software version."
|
|
local kickstart_files_dir=/var/www/pages/feed/rel-${SW_VERSION}/kickstart/centos
|
|
|
|
mkdir "${SHARED_DIR}/kickstart" || fatal_error "mkdir ${SHARED_DIR}/kickstart failed"
|
|
rsync -a "${kickstart_files_dir}"/miniboot_*.cfg "${SHARED_DIR}"/kickstart
|
|
check_rc_exit $? "Failed to copy kickstart files from ${kickstart_files_dir}"
|
|
|
|
# Any files in $KICKSTART_OVERRIDE_DIR are used in place of the files from above:
|
|
if [ -d "${KICKSTART_OVERRIDE_DIR}" ] && \
|
|
[ "$(echo "${KICKSTART_OVERRIDE_DIR}"/miniboot_*.cfg)" != \
|
|
"${KICKSTART_OVERRIDE_DIR}/miniboot_*.cfg" ]; then
|
|
log_info "Copying override cfg files from ${KICKSTART_OVERRIDE_DIR}"
|
|
cp "${KICKSTART_OVERRIDE_DIR}"/miniboot_*.cfg "${SHARED_DIR}"/kickstart
|
|
check_rc_exit $? "Failed to copy override kickstart files from ${KICKSTART_OVERRIDE_DIR}"
|
|
fi
|
|
|
|
rsync -a "${MNTDIR}/isolinux.cfg" "${SHARED_DIR}/"
|
|
check_rc_exit $? "Failed to copy isolinux.cfg from ${INPUT_ISO}"
|
|
|
|
rsync -a "${MNTDIR}/Packages/" "${SHARED_DIR}/Packages/"
|
|
check_rc_exit $? "Failed to copy base packages from ${INPUT_ISO}"
|
|
|
|
rsync -a "${MNTDIR}/repodata/" "${SHARED_DIR}/repodata/"
|
|
check_rc_exit $? "Failed to copy base repodata from ${INPUT_ISO}"
|
|
}
|
|
|
|
function extract_node_files {
|
|
# Copy files for mini ISO build
|
|
rsync ${VERBOSE_RSYNC} -a \
|
|
--exclude LiveOS/ \
|
|
--exclude Packages/ \
|
|
--exclude repodata/ \
|
|
--exclude patches/ \
|
|
--exclude pxeboot/ \
|
|
--exclude pxeboot_setup.sh \
|
|
--exclude upgrades/ \
|
|
--exclude '*_ks.cfg' \
|
|
--exclude ks.cfg \
|
|
--exclude ks \
|
|
"${MNTDIR}/" "${BUILDDIR}/"
|
|
check_rc_exit $? "Failed to rsync ISO content from ${MNTDIR} to ${BUILDDIR}"
|
|
|
|
if [ "${PATCHES_FROM_HOST}" = "yes" ]; then
|
|
local patched_initrd_file=${WORKDIR}/pxeboot/rel-${ISO_VERSION}/installer-intel-x86-64-initrd_1.0
|
|
local patched_vmlinuz_file=${WORKDIR}/pxeboot/rel-${ISO_VERSION}/installer-bzImage_1.0
|
|
|
|
# First, check to see if pxe-network-installer is already extracted.
|
|
# If this is the first setup for this ISO, it will have been extracted
|
|
# during the shared setup, and we don't need to do it again.
|
|
if [ ! -f "${patched_initrd_file}" ]; then
|
|
extract_pkg_to_workdir 'pxe-network-installer'
|
|
fi
|
|
|
|
# Copy patched files, as appropriate
|
|
if [ -f "${patched_initrd_file}" ]; then
|
|
rsync -a "${patched_initrd_file}" "${BUILDDIR}"/initrd.img
|
|
check_rc_exit $? "Failed to copy ${patched_initrd_file}"
|
|
fi
|
|
|
|
if [ -f "${patched_vmlinuz_file}" ]; then
|
|
rsync -a "${patched_vmlinuz_file}" "${BUILDDIR}"/vmlinuz
|
|
check_rc_exit $? "Failed to copy ${patched_vmlinuz_file}"
|
|
fi
|
|
fi
|
|
|
|
# Setup syslinux and grub cfg files
|
|
generate_boot_cfg "${BUILDDIR}"
|
|
|
|
# Set/update boot parameters
|
|
if [ ${#PARAMS[@]} -gt 0 ]; then
|
|
for p in ${PARAMS[@]}; do
|
|
param=${p%%=*} # Strip from the first '=' on
|
|
value=${p#*=} # Strip to the first '='
|
|
|
|
update_parameter "${BUILDDIR}" "${param}" "${value}"
|
|
done
|
|
fi
|
|
|
|
unmount_efiboot_img
|
|
|
|
mkdir -p "${NODE_DIR}" || fatal_error "Failed to create ${NODE_DIR}"
|
|
|
|
# Setup symlinks to the shared content, which lighttpd can serve
|
|
pushd ${NODE_DIR} >/dev/null
|
|
ln -s ../../shared/* .
|
|
popd >/dev/null
|
|
|
|
# Rebuild the ISO
|
|
OUTPUT_ISO=${NODE_DIR}/bootimage.iso
|
|
log_info "Creating ${OUTPUT_ISO} from BUILDDIR: ${BUILDDIR}"
|
|
mkisofs -o "${OUTPUT_ISO}" \
|
|
-R -D -A 'oe_iso_boot' -V 'oe_iso_boot' \
|
|
-quiet \
|
|
-b isolinux.bin -c boot.cat -no-emul-boot \
|
|
-boot-load-size 4 -boot-info-table \
|
|
-eltorito-alt-boot \
|
|
-e images/efiboot.img \
|
|
-no-emul-boot \
|
|
"${BUILDDIR}"
|
|
check_rc_exit $? "mkisofs failed"
|
|
|
|
isohybrid --uefi "${OUTPUT_ISO}"
|
|
check_rc_exit $? "isohybrid failed"
|
|
implantisomd5 "${OUTPUT_ISO}"
|
|
check_rc_exit $? "implantisomd5 failed"
|
|
log_path_size "$OUTPUT_ISO" "Size of bootimage.iso"
|
|
# Setup the kickstart
|
|
local ksfile=${SHARED_DIR}/kickstart/miniboot_${KS_NODETYPE}_ks.cfg
|
|
|
|
cp "${ksfile}" "${NODE_DIR}/miniboot_${KS_NODETYPE}.cfg"
|
|
check_rc_exit $? "Failed to copy ${ksfile} to ${NODE_DIR}/miniboot_${KS_NODETYPE}.cfg"
|
|
|
|
# Number of dirs in the NODE_URL: Count the / characters, subtracting 2 for http:// or https://
|
|
DIRS=$(($(grep -o "/" <<< "$NODE_URL" | wc -l) - 2))
|
|
|
|
# Escape the / chars for use in sed
|
|
NODE_URL_SED="${NODE_URL//\//\\/}"
|
|
|
|
sed -i "s#xxxHTTP_URLxxx#${NODE_URL_SED}#g;
|
|
s#xxxHTTP_URL_PATCHESxxx#${NODE_URL_SED}/patches#g;
|
|
s#NUM_DIRS#${DIRS}#g" \
|
|
"${NODE_DIR}/miniboot_${KS_NODETYPE}.cfg"
|
|
|
|
# Append the custom addon
|
|
if [ -n "${ADDON}" ]; then
|
|
cat <<EOF >> "${NODE_DIR}/miniboot_${KS_NODETYPE}.cfg"
|
|
|
|
%post --erroronfail
|
|
|
|
# Source common functions
|
|
. /tmp/ks-functions.sh
|
|
|
|
$(cat "${ADDON}")
|
|
|
|
%end
|
|
EOF
|
|
fi
|
|
}
|
|
|
|
function create_miniboot_iso {
|
|
# Determine release version from ISO
|
|
[ -f ${MNTDIR}/upgrades/version ] || fatal_error "Version info not found on input ISO: ${INPUT_ISO}"
|
|
ISO_VERSION=$(source ${MNTDIR}/upgrades/version && echo ${VERSION})
|
|
if [ -z "${ISO_VERSION}" ]; then
|
|
fatal_error "Failed to determine version of installation ISO from ${MNTDIR}/upgrades/version"
|
|
fi
|
|
|
|
# Copy the common files from the ISO, if needed
|
|
extract_shared_files
|
|
|
|
# Extract/generate the node-specific files
|
|
extract_node_files
|
|
}
|
|
|
|
#
|
|
# Main
|
|
#
|
|
function main {
|
|
# if [ "$(get_os)" != centos ]; then
|
|
# fatal_error "This script must be invoked on CentOS only"
|
|
# fi
|
|
parse_arguments "$@"
|
|
initialize_and_lock
|
|
mount_iso "${INPUT_ISO}" "${SCRATCH_DIR}"
|
|
create_miniboot_iso
|
|
unmount_iso
|
|
exit 0
|
|
}
|
|
|
|
# Execute main if script is executed directly (not sourced):
|
|
# This allows for shunit2 testing
|
|
if [[ "${BASH_SOURCE[0]}" = "$0" ]]; then
|
|
main "$@"
|
|
fi
|