diskimage-builder/lib/common-functions

297 lines
9.1 KiB
Bash

# vim: syntax=sh ts=4 sts=4 sw=2:et
# Copyright 2012 Hewlett-Packard Development Company, L.P.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
function tmpfs_check() {
[ "$DIB_NO_TMPFS" == "0" ] || return 1
[ -r /proc/meminfo ] || return 1
total_kB=$(awk '/^MemTotal/ { print $2 }' /proc/meminfo)
# tmpfs uses by default 50% of the available RAM, so the RAM should be at least
# the double of the minimum tmpfs size required
RAM_NEEDED=$[ $DIB_MIN_TMPFS * 2 ]
[ $total_kB -lt $(($RAM_NEEDED*1024*1024)) ] || return 0
echo "Not enough RAM to use tmpfs for build. ($total_kB < ${RAM_NEEDED}G)"
return 1
}
function mk_build_dir () {
TMP_BUILD_DIR=$(mktemp -t -d --tmpdir=${TMP_DIR:-/tmp} image.XXXXXXXX)
TMP_IMAGE_DIR=$(mktemp -t -d --tmpdir=${TMP_DIR:-/tmp} image.XXXXXXXX)
[ $? -eq 0 ] || die "Failed to create tmp directory"
export TMP_BUILD_DIR
if tmpfs_check ; then
sudo mount -t tmpfs tmpfs $TMP_BUILD_DIR
sudo mount -t tmpfs tmpfs $TMP_IMAGE_DIR
fi
sudo chown $(id -u):$(id -g) $TMP_BUILD_DIR $TMP_IMAGE_DIR
trap cleanup EXIT
echo Building in $TMP_BUILD_DIR
export TMP_IMAGE_PATH=$TMP_IMAGE_DIR/image.raw
export TMP_HOOKS_PATH=$TMP_BUILD_DIR/hooks
}
function finish_image () {
mv $TMP_IMAGE_PATH $1
cleanup_dirs
# All done!
trap EXIT
echo "Image file $1 created..."
}
function save_image () {
# TODO: this really should rename the old file
if [ -f $1 ] ; then
echo "Old Image file Found REMOVING"
rm -f $1
fi
cp $TMP_IMAGE_PATH $1
cleanup_dirs
# All done!
trap EXIT
echo "Image file $1 created..."
}
function element_pre_check() {
local element_dir=$1
[ -d $element_dir ] || return 1
if [ -d $element_dir/first-boot.d ] ; then
first_boot_deprecated_message $element_dir
fi
}
_DISPLAYED_FIRST_BOOT_DEPRECATED=0
function first_boot_deprecated_message() {
local element_dir=$1
echo "WARNING: first-boot.d found in $element_dir"
if [ $_DISPLAYED_FIRST_BOOT_DEPRECATED -eq 1 ] ; then
return
fi
echo "***********************************************************"
echo "* *"
echo "* *"
echo "* *"
echo "* *"
echo "* WARNING: first-boot.d is DEPRECATED, it will be removed *"
echo "* from diskimage-builder in a future release. *"
echo "* *"
echo "* *"
echo "* *"
echo "* *"
echo "***********************************************************"
echo Pausing 10 seconds....
sleep 10
_DISPLAYED_FIRST_BOOT_DEPRECATED=1
}
function generate_hooks () {
mkdir -p $TMP_HOOKS_PATH
for _ELEMENT in $IMAGE_ELEMENT ; do
for dir in ${ELEMENTS_PATH//:/ } ; do
element_pre_check $dir/$_ELEMENT || continue
cp -t $TMP_HOOKS_PATH -a $dir/$_ELEMENT/* ;
break
done
done
}
# Call the supplied break-in routine if the named point is listed in the break
# list.
# $1 the break point.
# $2.. what to call if a break is needed
function check_break () {
if echo "${break:-}" | egrep -e "(,|^)$1(,|$)" -q; then
echo "Starting debug shell. Exit to resume building." >&2
echo At stage $1 >&2
shift
"$@"
echo "Resuming" >&2
fi
}
# Check that a real element has been chosen (prevents foot-guns)
function check_element () {
[ -d $TMP_HOOKS_PATH ] || generate_hooks
}
# Run a hook, looking for a regex in its stdout, and eval the matched lines.
# $1 is the hook to run
# $2 is the regex to look for
function eval_run_d () {
local TEMP=`run_d $1`
echo "$TEMP"
if [ `echo "$TEMP" | grep -s "$2"` ]; then
TEMP=`echo "$TEMP" | grep "$2"`
eval "$TEMP"
fi
}
# Usage: map_nbd $image
# Returns nbd device path
function map_nbd {
(lsmod | grep '^nbd ' >/dev/null) || sudo modprobe nbd max_part=16
if [[ $(qemu-nbd --help | grep cache) == *writeback* ]] ; then
CACHE="--cache=writeback"
else
echo "Warning: qemu-nbd without --cache=writeback is /slow/."
CACHE=""
fi
NBD_DEV=
for i in `seq 0 15`; do
if [ ! -e /sys/block/nbd$i/pid ]; then
NBD_DEV=/dev/nbd$i
# Connect to nbd and wait till it is ready
sudo qemu-nbd -c $NBD_DEV $CACHE $1
if ! timeout 60 sh -c "while ! [ -e /sys/block/nbd$i/pid ]; do sleep 1; done"; then
echo "Couldn't connect $NBD_DEV"
exit 1
fi
break
fi
done
if [ -z "$NBD_DEV" ]; then
echo "No free NBD slots"
exit 1
fi
}
# Delete and unmount the working dir used in extracting kernel/initrd
function unmount_qcow_image () {
sudo umount $WORK_DIR || true
sudo qemu-nbd -d $NBD_DEV >/dev/null || true
sudo rm -rf $WORK_DIR
trap - SIGHUP SIGINT SIGTERM EXIT
}
function mount_qcow_image() {
trap unmount_qcow_image SIGHUP SIGINT SIGTERM EXIT
WORK_DIR=$(mktemp -d)
map_nbd $1
if [ -e "${NBD_DEV}p1" ]; then
sudo mount ${NBD_DEV}p1 $WORK_DIR
else
sudo mount ${NBD_DEV} $WORK_DIR
fi
}
function cleanup_dirs () {
sudo rm -rf $TMP_BUILD_DIR/built
sudo rm -rf $TMP_BUILD_DIR/mnt
sudo umount -f $TMP_BUILD_DIR $TMP_IMAGE_DIR || true
rm -rf $TMP_BUILD_DIR $TMP_IMAGE_DIR
}
# Run a directory of hooks outside the target.
function run_d() {
check_element
check_break before-$1 bash
if [ -d ${TMP_HOOKS_PATH}/$1.d ] ; then
${SCRIPT_HOME}/dib-run-parts ${TMP_HOOKS_PATH}/$1.d
fi
check_break after-$1 bash
}
function detach_loopback() {
local loopdev=$1
# Remove the map if it exists
# If setup on a rhel or derivative the map was created with kpartx not losetup
# and subsequently needs to be removed.
loopdev_name=$(echo $loopdev | sed 's/\/dev\///g')
mapper_name=$(sudo dmsetup ls | grep $loopdev_name | awk '{ print $1 }')
if [ "$mapper_name" ]; then
sudo dmsetup remove $mapper_name
fi
# loopback dev may be tied up a bit by udev events triggered by partition events
for try in $(seq 10 -1 1) ; do
if sudo losetup -d $loopdev ; then
return 0
fi
echo $loopdev may be busy, sleeping up to $try more seconds...
sleep 1
done
echo Gave up trying to detach $loopdev
return 1
}
function arg_to_elements() {
for arg do IMAGE_ELEMENT="$IMAGE_ELEMENT $arg" ; done
if [ "$SKIP_BASE" != "1" ]; then
IMAGE_ELEMENT="base $IMAGE_ELEMENT"
fi
if [ "$IS_RAMDISK" == "1" ]; then
IMAGE_ELEMENT="ramdisk $IMAGE_ELEMENT"
fi
echo "Building elements: $IMAGE_ELEMENT"
export IMAGE_ELEMENT=$($SCRIPT_HOME/element-info --expand-dependencies $IMAGE_ELEMENT)
echo "Expanded element dependencies to: $IMAGE_ELEMENT"
}
function create_base () {
mkdir $TMP_BUILD_DIR/mnt
export TMP_MOUNT_PATH=$TMP_BUILD_DIR/mnt
# Copy data in to the root.
TARGET_ROOT=$TMP_MOUNT_PATH run_d root
if [ -z "$(ls $TMP_MOUNT_PATH | grep -v '^lost+found\|tmp$')" ] ; then
# No root element copied in. Note the test above allows
# root.d elements to put things in /tmp
echo "Please include at least one distribution root element."
exit 1
fi
# Configure Image
# Setup resolv.conf so we can chroot to install some packages
if [ -L $TMP_MOUNT_PATH/etc/resolv.conf ] || [ -f $TMP_MOUNT_PATH/etc/resolv.conf ] ; then
sudo mv $TMP_MOUNT_PATH/etc/resolv.conf $TMP_MOUNT_PATH/etc/resolv.conf.ORIG
fi
# Recreate resolv.conf
sudo touch $TMP_MOUNT_PATH/etc/resolv.conf
sudo chmod 777 $TMP_MOUNT_PATH/etc/resolv.conf
# use system configured resolv.conf if available to support internal proxy resolving
if [ -e /etc/resolv.conf ]
then
cat /etc/resolv.conf > $TMP_MOUNT_PATH/etc/resolv.conf
else
echo nameserver 8.8.8.8 > $TMP_MOUNT_PATH/etc/resolv.conf
fi
mount_proc_dev_sys
}
function mount_proc_dev_sys () {
# supporting kernel file systems
sudo mount -t proc none $TMP_MOUNT_PATH/proc
sudo mount --bind /dev $TMP_MOUNT_PATH/dev
sudo mount --bind /dev/pts $TMP_MOUNT_PATH/dev/pts
sudo mount -t sysfs none $TMP_MOUNT_PATH/sys
}
function unmount_dir () {
local pattern="$1" mnts=""
if [ -n "$pattern" ]; then
mnts=`awk '{print $2}' < /proc/mounts | grep "^$pattern" | sort -r`
fi
if [ -n "$mnts" ]; then
sudo umount -fl $mnts || true
fi
}