Create utility to generate hybrid bootloader from ISO

The gen-bootloader-iso.sh utility takes a StarlingX installation ISO
as input and generates a small bootloader ISO and www-served directory
structure to provide a hybrid ISO/network installation ISO.

The utility sets up two structures under the specified www-root
directory: shared files and node-specific files

Shared files: These are the rootfs (squashfs.img) and software
package repositories that will be downloaded to the installing node
via http/https, and would be common to all nodes being installed.

Node-specific files: These include:
- a much smaller bootimage.iso that contains the kernel and initrd, as
well as syslinux.cfg and grub.cfg files updated to access rootfs,
repos, and the node-specific kickstart file, via https (rather than
from the ISO).
- a generated kickstart specific for the node which gets the software
to be installed from http/https, and can have a node-specific %post
section.
- symbolic links to shared files, for http/https access.

The utility also provides file cleanup functionality with the --delete
option, which will remove the node-specific files. If all node files
have been removed, it will also remove the shared files.

Story: 2006980
Task: 38727

Change-Id: Ie952ee81d96aeacc04482daed942654da0ec0573
Signed-off-by: Don Penney <don.penney@windriver.com>
This commit is contained in:
Don Penney 2020-02-05 14:36:14 -05:00
parent bf67104914
commit de8f5256c6
3 changed files with 773 additions and 0 deletions

1
.gitignore vendored
View File

@ -1,2 +1,3 @@
.tox
*.egg-info
*.swp

View File

@ -61,6 +61,7 @@ install %{_buildsubdir}/scripts/tc_setup.sh %{buildroot}%{local_bindir}
install %{_buildsubdir}/scripts/remotelogging_tc_setup.sh %{buildroot}%{local_bindir}
install %{_buildsubdir}/scripts/connectivity_test %{buildroot}%{local_bindir}
install -m 555 %{_buildsubdir}/scripts/update-iso.sh %{buildroot}%{local_bindir}
install -m 555 %{_buildsubdir}/scripts/gen-bootloader-iso.sh %{buildroot}%{local_bindir}
install -d %{buildroot}%{local_etc_initd}
install %{_buildsubdir}/scripts/log_functions.sh %{buildroot}%{local_etc_initd}
@ -104,6 +105,7 @@ systemctl enable opt-platform.service
%files controller
%defattr(-,root,root,-)
%{local_bindir}/update-iso.sh
%{local_bindir}/gen-bootloader-iso.sh
%files noncontroller
%defattr(-,root,root,-)

View File

@ -0,0 +1,770 @@
#!/bin/bash
#
# Copyright (c) 2020 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
#
#
declare LOG_TAG=$(basename $0)
function log_error {
logger -i -s -t ${LOG_TAG} -- "$@"
}
declare ADDON=
declare BASE_URL=
declare BOOT_GATEWAY=
declare BOOT_HOSTNAME=
declare BOOT_INTERFACE=
declare BOOT_IP=
declare BOOT_NETMASK=
declare DEFAULT_GRUB_ENTRY=
declare DEFAULT_LABEL=
declare DEFAULT_SYSLINUX_ENTRY=
declare DELETE="no"
declare EFIBOOT_IMG_LOOP=
declare EFI_MOUNT=
declare GRUB_TIMEOUT=-1
declare INPUT_ISO=
declare KS_NODETYPE=
declare NODE_ID=
declare ORIG_PWD=$PWD
declare OUTPUT_ISO=
declare -a PARAMS
declare -i TIMEOUT=0
declare UPDATE_TIMEOUT="no"
declare WWW_ROOT_DIR=
declare MNTDIR=
declare BUILDDIR=
declare WORKDIR=
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
--baseurl <url>: Specify URL for www-root dir
--id <node id>: Specify ID for target node
--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
--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
}
#
# Parse cmdline arguments
#
LONGOPTS="input:,addon:,param:,default-boot:,timeout:"
LONGOPTS="${LONGOPTS},base-url:,www-root:,id:,delete"
LONGOPTS="${LONGOPTS},boot-gateway:,boot-hostname:,boot-interface:,boot-ip:,boot-netmask:"
LONGOPTS="${LONGOPTS},help"
OPTS=$(getopt -o h --long "${LONGOPTS}" --name "$0" -- "$@")
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)
DEFAULT_LABEL=$2
shift 2
case ${DEFAULT_LABEL} 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: ${DEFAULT_LABEL}"
usage
exit 1
;;
esac
;;
--timeout)
let -i timeout_arg=$2
shift 2
if [ ${timeout_arg} -gt 0 ]; then
let -i 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
;;
--delete)
DELETE="yes"
shift
;;
--)
shift
break
;;
*)
usage
exit 1
;;
esac
done
#
# Functions
#
function check_requirements {
local -a required_utils=(
rsync
mkisofs
isohybrid
implantisomd5
)
if [ $UID -ne 0 ]; then
# If running as non-root user, additional utils are required
required_utils+=(
guestmount
guestunmount
udisksctl
)
fi
local -i missing=0
for req in ${required_utils[@]}; do
which ${req} >&/dev/null
if [ $? -ne 0 ]; then
log_error "Unable to find required utility: ${req}"
let -i missing++
fi
done
if [ ${missing} -gt 0 ]; then
log_error "One or more required utilities are missing. Aborting..."
exit 1
fi
}
function mount_iso {
if [ $UID -eq 0 ]; then
# Mount the ISO
mount -o loop ${INPUT_ISO} ${MNTDIR}
if [ $? -ne 0 ]; then
log_error "Failed to mount ${INPUT_ISO}"
exit 1
fi
else
# As non-root user, mount the ISO using guestmount
guestmount -a ${INPUT_ISO} -m /dev/sda1 --ro ${MNTDIR}
rc=$?
if [ $rc -ne 0 ]; then
# Add a retry
log_error "Call to guestmount failed with rc=$rc. Retrying once..."
guestmount -a ${INPUT_ISO} -m /dev/sda1 --ro ${MNTDIR}
rc=$?
if [ $rc -ne 0 ]; then
log_error "Call to guestmount failed with rc=$rc. Aborting..."
exit $rc
fi
fi
fi
}
function unmount_iso {
if [ $UID -eq 0 ]; then
umount ${MNTDIR}
else
guestunmount ${MNTDIR}
fi
rmdir ${MNTDIR}
}
function mount_efiboot_img {
local isodir=$1
local efiboot_img=${isodir}/images/efiboot.img
local loop_setup_output=
if [ $UID -eq 0 ]; then
# As root, setup a writeable loop device for the
# efiboot.img file and mount it
loop_setup_output=$(losetup --show -f ${efiboot_img})
if [ $? -ne 0 ]; then
log_error "Failed losetup"
exit 1
fi
EFIBOOT_IMG_LOOP=${loop_setup_output}
EFI_MOUNT=$(mktemp -d -p /mnt -t EFI-noudev.XXXXXX)
mount ${EFIBOOT_IMG_LOOP} ${EFI_MOUNT}
if [ $? -ne 0 ]; then
log_error "Failed to mount loop device ${EFIBOOT_IMG_LOOP}"
exit 1
fi
else
# As non-root user, we can use the udisksctl to setup a loop device
# and mount the efiboot.img, with read/write access.
loop_setup_output=$(udisksctl loop-setup -f ${efiboot_img} --no-user-interaction)
if [ $? -ne 0 ]; then
log_error "Failed udisksctl loop-setup"
exit 1
fi
EFIBOOT_IMG_LOOP=$(echo ${loop_setup_output} | awk '{print $5;}' | sed -e 's/\.$//g')
if [ -z "${EFIBOOT_IMG_LOOP}" ]; then
log_error "Failed to determine loop device from command output: ${loop_setup_output}"
exit 1
fi
udisksctl mount -b ${EFIBOOT_IMG_LOOP}
if [ $? -ne 0 ]; then
log_error "Failed udisksctl mount"
exit 1
fi
EFI_MOUNT=$(udisksctl info -b ${EFIBOOT_IMG_LOOP} | grep MountPoints | awk '{print $2;}')
if [ -z "${EFI_MOUNT}" ]; then
log_error "Failed to determine mount point from udisksctl info command"
exit 1
fi
fi
}
function unmount_efiboot_img {
if [ $UID -eq 0 ]; then
if [ -n "${EFI_MOUNT}" ]; then
mountpoint -q ${EFI_MOUNT} && umount ${EFI_MOUNT}
rmdir ${EFI_MOUNT}
EFI_MOUNT=
fi
if [ -n "${EFIBOOT_IMG_LOOP}" ]; then
losetup -d ${EFIBOOT_IMG_LOOP}
EFIBOOT_IMG_LOOP=
fi
else
if [ -n "${EFIBOOT_IMG_LOOP}" ]; then
udisksctl unmount -b ${EFIBOOT_IMG_LOOP}
udisksctl loop-delete -b ${EFIBOOT_IMG_LOOP}
EFI_MOUNT=
EFIBOOT_IMG_LOOP=
fi
fi
}
function generate_boot_cfg {
if [ -z "${EFI_MOUNT}" ]; then
mount_efiboot_img ${BUILDDIR}
fi
KS_URL="${NODE_URL}/miniboot_${KS_NODETYPE}.cfg"
BOOT_IP_ARG="${BOOT_IP}::${BOOT_GATEWAY}:${BOOT_NETMASK}:${BOOT_HOSTNAME}:${BOOT_INTERFACE}:none"
COMMON_ARGS="inst.text inst.gpt boot_device=sda rootfs_device=sda"
COMMON_ARGS="${COMMON_ARGS} biosdevname=0 usbcore.autosuspend=-1"
COMMON_ARGS="${COMMON_ARGS} security_profile=standard user_namespace.enable=1"
COMMON_ARGS="${COMMON_ARGS} inst.repo=${NODE_URL} inst.stage2=${NODE_URL} inst.ks=${KS_URL}"
COMMON_ARGS="${COMMON_ARGS} ip=${BOOT_IP_ARG}"
for f in ${BUILDDIR}/isolinux.cfg ${BUILDDIR}/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 ${COMMON_ARGS} console=ttyS0,115200 serial
# Graphical Console submenu
label 1
menu label Graphical Console
kernel vmlinuz
initrd initrd.img
append rootwait ${COMMON_ARGS} console=tty0
menu end
EOF
done
for f in ${BUILDDIR}/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 "${NODE_ID}" {
echo " "
}
menuentry 'Serial Console' --id=serial {
linuxefi /vmlinuz ${COMMON_ARGS} console=ttyS0,115200 serial
initrdefi /initrd.img
}
menuentry 'Graphical Console' --id=graphical {
linuxefi /vmlinuz ${COMMON_ARGS} console=tty0
initrdefi /initrd.img
}
EOF
done
}
function update_parameter {
local isodir=$1
local param=$2
local value=$3
if [ -z "${EFI_MOUNT}" ]; then
mount_efiboot_img ${isodir}
fi
for f in ${isodir}/isolinux.cfg ${isodir}/syslinux.cfg; do
grep -q "^[[:space:]]*append\>.*[[:space:]]${param}=" ${f}
if [ $? -eq 0 ]; then
# Parameter already exists. Update the value
sed -i -e "s#^\([[:space:]]*append\>.*${param}\)=[^[:space:]]*#\1=${value}#" ${f}
if [ $? -ne 0 ]; then
log_error "Failed to update parameter ($param)"
exit 1
fi
else
# Parameter doesn't exist. Add it to the cmdline
sed -i -e "s|^\([[:space:]]*append\>.*\)|\1 ${param}=${value}|" ${f}
if [ $? -ne 0 ]; then
log_error "Failed to add parameter ($param)"
exit 1
fi
fi
done
for f in ${isodir}/EFI/BOOT/grub.cfg ${EFI_MOUNT}/EFI/BOOT/grub.cfg; do
grep -q "^[[:space:]]*linuxefi\>.*[[:space:]]${param}=" ${f}
if [ $? -eq 0 ]; then
# Parameter already exists. Update the value
sed -i -e "s#^\([[:space:]]*linuxefi\>.*${param}\)=[^[:space:]]*#\1=${value}#" ${f}
if [ $? -ne 0 ]; then
log_error "Failed to update parameter ($param)"
exit 1
fi
else
# Parameter doesn't exist. Add it to the cmdline
sed -i -e "s|^\([[:space:]]*linuxefi\>.*\)|\1 ${param}=${value}|" ${f}
if [ $? -ne 0 ]; then
log_error "Failed to add parameter ($param)"
exit 1
fi
fi
done
}
function cleanup {
unmount_efiboot_img
if [ -n "$MNTDIR" -a -d "$MNTDIR" ]; then
unmount_iso
fi
if [ -n "$BUILDDIR" -a -d "$BUILDDIR" ]; then
\rm -rf $BUILDDIR
fi
if [ -n "$WORKDIR" -a -d "$WORKDIR" ]; then
\rm -rf $WORKDIR
fi
}
function check_required_param {
local param="${1}"
local value="${2}"
if [ -z "${value}" ]; then
log_error "Required parameter ${param} is not set"
exit 1
fi
}
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
if [ -z "$(ls -A ${NODE_DIR_BASE})" ]; then
rmdir ${NODE_DIR_BASE}
rm -rf ${SHARED_DIR}
fi
}
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}
if [ $? -ne 0 ]; then
log_error "Failed to create directory: ${SHARED_DIR}"
exit 1
fi
# Check ISO content
if [ ! -f ${MNTDIR}/LiveOS/squashfs.img ]; then
log_error "squashfs.img not found on ${INPUT_ISO}"
exit 1
fi
rsync -a ${MNTDIR}/LiveOS/ ${SHARED_DIR}/LiveOS/
if [ $? -ne 0 ]; then
log_error "Failed to copy rootfs from ${INPUT_ISO}"
exit 1
fi
rsync ${MNTDIR}/isolinux.cfg ${SHARED_DIR}/
if [ $? -ne 0 ]; then
log_error "Failed to copy isolinux.cfg from ${INPUT_ISO}"
exit 1
fi
rsync -a ${MNTDIR}/Packages/ ${SHARED_DIR}/Packages/
if [ $? -ne 0 ]; then
log_error "Failed to copy base packages from ${INPUT_ISO}"
exit 1
fi
rsync -a ${MNTDIR}/repodata/ ${SHARED_DIR}/repodata/
if [ $? -ne 0 ]; then
log_error "Failed to copy base repodata from ${INPUT_ISO}"
exit 1
fi
if [ -d ${MNTDIR}/patches ]; then
rsync -a ${MNTDIR}/patches/ ${SHARED_DIR}/patches/
if [ $? -ne 0 ]; then
log_error "Failed to copy patches repo from ${INPUT_ISO}"
exit 1
fi
fi
}
function extract_node_files {
# Copy files for mini ISO build
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}/
rc=$?
if [ $rc -ne 0 ]; then
log_error "Call to rsync ISO content. Aborting..."
exit $rc
fi
# Setup syslinux and grub cfg files
generate_boot_cfg
# 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}
if [ $? -ne 0 ]; then
log_error "Failed to create ${NODE_DIR}"
exit 1
fi
# 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
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}
isohybrid --uefi ${OUTPUT_ISO}
implantisomd5 ${OUTPUT_ISO}
# Setup the kickstart
cp ${MNTDIR}/pxeboot/pxeboot_${KS_NODETYPE}.cfg ${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
}
#
# Main
#
# Run cleanup on any exit
trap cleanup EXIT
# Check script dependencies
check_requirements
# Validate parameters
# Check mandatory parameters
check_required_param "--id" "${NODE_ID}"
check_required_param "--www-root" "${WWW_ROOT_DIR}"
declare NODE_DIR_BASE="${WWW_ROOT_DIR}/nodes"
declare NODE_DIR="${NODE_DIR_BASE}/${NODE_ID}"
declare SHARED_DIR="${WWW_ROOT_DIR}/shared"
if [ ! -d "${WWW_ROOT_DIR}" ]; then
log_error "Root directory ${WWW_ROOT_DIR} does not exist"
exit 1
fi
# 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}"
declare NODE_URL="${BASE_URL%\/}/nodes/${NODE_ID}"
if [ ! -f ${INPUT_ISO} ]; then
log_error "Input file does not exist: ${INPUT_ISO}"
exit 1
fi
if [ -d ${NODE_DIR} ]; then
log_error "Output dir already exists: ${NODE_DIR}"
exit 1
fi
MNTDIR=$(mktemp -d -p $PWD updateiso_mnt_XXXXXX)
if [ -z "${MNTDIR}" -o ! -d ${MNTDIR} ]; then
log_error "Failed to create mntdir. Aborting..."
exit 1
fi
BUILDDIR=$(mktemp -d -p $PWD updateiso_build_XXXXXX)
if [ -z "${BUILDDIR}" -o ! -d ${BUILDDIR} ]; then
log_error "Failed to create builddir. Aborting..."
exit 1
fi
mount_iso
# Copy the common files from the ISO, if needed
extract_shared_files
# Extract/generate the node-specific files
extract_node_files
unmount_iso
exit 0