Blame test/image-tests/qemu.sh

Packit 63bb0d
#!/bin/bash
Packit 63bb0d
set -euo pipefail
Packit 63bb0d
Packit 63bb0d
# Get OS data.
Packit 63bb0d
source /etc/os-release
Packit 63bb0d
ARCH=$(uname -m)
Packit 63bb0d
Packit 63bb0d
# Take the image type passed to the script or use qcow2 by default if nothing
Packit 63bb0d
# was passed.
Packit 63bb0d
IMAGE_TYPE=${1:-qcow2}
Packit 63bb0d
Packit 63bb0d
# Select the file extension based on the image that we are building.
Packit 63bb0d
IMAGE_EXTENSION=$IMAGE_TYPE
Packit 63bb0d
if [[ $IMAGE_TYPE == 'openstack' ]]; then
Packit 63bb0d
    IMAGE_EXTENSION=qcow2
Packit 63bb0d
fi
Packit 63bb0d
Packit 63bb0d
# RHEL 8 cannot boot a VMDK using libvirt. See BZ 999789.
Packit 63bb0d
if [[ $IMAGE_TYPE == vmdk ]]; then
Packit 63bb0d
    echo "๐Ÿคท libvirt cannot boot stream-optimized VMDK."
Packit 63bb0d
    exit 0
Packit 63bb0d
fi
Packit 63bb0d
Packit 63bb0d
# Apply lorax patch to work around pytoml issues in RHEL 8.x.
Packit 63bb0d
# See BZ 1843704 or https://github.com/weldr/lorax/pull/1030 for more details.
Packit 63bb0d
if [[ $ID == rhel ]]; then
Packit 63bb0d
    sudo sed -r -i 's#toml.load\(args\[3\]\)#toml.load(open(args[3]))#' \
Packit 63bb0d
        /usr/lib/python3.6/site-packages/composer/cli/compose.py
Packit 63bb0d
    sudo rm -f /usr/lib/python3.6/site-packages/composer/cli/compose.pyc
Packit 63bb0d
fi
Packit 63bb0d
Packit 63bb0d
# Colorful output.
Packit 63bb0d
function greenprint {
Packit 63bb0d
    echo -e "\033[1;32m${1}\033[0m"
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
# Install required packages.
Packit 63bb0d
greenprint "๐Ÿ“ฆ Installing required packages"
Packit 63bb0d
sudo dnf -y install jq libvirt-client libvirt-daemon \
Packit 63bb0d
    libvirt-daemon-config-network libvirt-daemon-config-nwfilter \
Packit 63bb0d
    libvirt-daemon-driver-interface libvirt-daemon-driver-network \
Packit 63bb0d
    libvirt-daemon-driver-nodedev libvirt-daemon-driver-nwfilter \
Packit 63bb0d
    libvirt-daemon-driver-qemu libvirt-daemon-driver-secret \
Packit 63bb0d
    libvirt-daemon-driver-storage libvirt-daemon-driver-storage-disk \
Packit 63bb0d
    libvirt-daemon-kvm qemu-img qemu-kvm virt-install
Packit 63bb0d
Packit 63bb0d
# Start libvirtd and test it.
Packit 63bb0d
greenprint "๐Ÿš€ Starting libvirt daemon"
Packit 63bb0d
sudo systemctl start libvirtd
Packit 63bb0d
sudo virsh list --all > /dev/null
Packit 63bb0d
Packit 63bb0d
# Set a customized dnsmasq configuration for libvirt so we always get the
Packit 63bb0d
# same address on bootup.
Packit 63bb0d
sudo tee /tmp/integration.xml > /dev/null << EOF
Packit 63bb0d
<network>
Packit 63bb0d
  <name>integration</name>
Packit 63bb0d
  <uuid>1c8fe98c-b53a-4ca4-bbdb-deb0f26b3579</uuid>
Packit 63bb0d
  <forward mode='nat'>
Packit 63bb0d
    <nat>
Packit 63bb0d
      <port start='1024' end='65535'/>
Packit 63bb0d
    </nat>
Packit 63bb0d
  </forward>
Packit 63bb0d
  <bridge name='integration' stp='on' delay='0'/>
Packit 63bb0d
  <mac address='52:54:00:36:46:ef'/>
Packit 63bb0d
  <ip address='192.168.100.1' netmask='255.255.255.0'>
Packit 63bb0d
    <dhcp>
Packit 63bb0d
      <range start='192.168.100.2' end='192.168.100.254'/>
Packit 63bb0d
      <host mac='34:49:22:B0:83:30' name='vm' ip='192.168.100.50'/>
Packit 63bb0d
    </dhcp>
Packit 63bb0d
  </ip>
Packit 63bb0d
</network>
Packit 63bb0d
EOF
Packit 63bb0d
if ! sudo virsh net-info integration > /dev/null 2>&1; then
Packit 63bb0d
    sudo virsh net-define /tmp/integration.xml
Packit 63bb0d
    sudo virsh net-start integration
Packit 63bb0d
fi
Packit 63bb0d
Packit 63bb0d
# Allow anyone in the wheel group to talk to libvirt.
Packit 63bb0d
greenprint "๐Ÿšช Allowing users in wheel group to talk to libvirt"
Packit 63bb0d
WHEEL_GROUP=wheel
Packit 63bb0d
if [[ $ID == rhel ]]; then
Packit 63bb0d
    WHEEL_GROUP=adm
Packit 63bb0d
fi
Packit 63bb0d
sudo tee /etc/polkit-1/rules.d/50-libvirt.rules > /dev/null << EOF
Packit 63bb0d
polkit.addRule(function(action, subject) {
Packit 63bb0d
    if (action.id == "org.libvirt.unix.manage" &&
Packit 63bb0d
        subject.isInGroup("${WHEEL_GROUP}")) {
Packit 63bb0d
            return polkit.Result.YES;
Packit 63bb0d
    }
Packit 63bb0d
});
Packit 63bb0d
EOF
Packit 63bb0d
Packit 63bb0d
# Set up variables.
Packit 63bb0d
TEST_UUID=$(uuidgen)
Packit 63bb0d
IMAGE_KEY=osbuild-composer-aws-test-${TEST_UUID}
Packit 63bb0d
INSTANCE_ADDRESS=192.168.100.50
Packit 63bb0d
Packit 63bb0d
# Set up temporary files.
Packit 63bb0d
TEMPDIR=$(mktemp -d)
Packit 63bb0d
BLUEPRINT_FILE=${TEMPDIR}/blueprint.toml
Packit 63bb0d
COMPOSE_START=${TEMPDIR}/compose-start-${IMAGE_KEY}.json
Packit 63bb0d
COMPOSE_INFO=${TEMPDIR}/compose-info-${IMAGE_KEY}.json
Packit 63bb0d
Packit 63bb0d
# Check for the smoke test file on the AWS instance that we start.
Packit 63bb0d
smoke_test_check () {
Packit 63bb0d
    # Ensure the ssh key has restricted permissions.
Packit 63bb0d
    SSH_KEY=${WORKSPACE}/test/keyring/id_rsa
Packit 63bb0d
    chmod 0600 $SSH_KEY
Packit 63bb0d
Packit 63bb0d
    SSH_OPTIONS="-o StrictHostKeyChecking=no -o ConnectTimeout=5"
Packit 63bb0d
    SMOKE_TEST=$(ssh $SSH_OPTIONS -i ${SSH_KEY} redhat@${1} 'cat /etc/smoke-test.txt')
Packit 63bb0d
    if [[ $SMOKE_TEST == smoke-test ]]; then
Packit 63bb0d
        echo 1
Packit 63bb0d
    else
Packit 63bb0d
        echo 0
Packit 63bb0d
    fi
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
# Get the compose log.
Packit 63bb0d
get_compose_log () {
Packit 63bb0d
    COMPOSE_ID=$1
Packit 63bb0d
    LOG_FILE=${WORKSPACE}/osbuild-${ID}-${VERSION_ID}-${IMAGE_TYPE}.log
Packit 63bb0d
Packit 63bb0d
    # Download the logs.
Packit 63bb0d
    sudo composer-cli compose log $COMPOSE_ID | tee $LOG_FILE > /dev/null
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
# Get the compose metadata.
Packit 63bb0d
get_compose_metadata () {
Packit 63bb0d
    COMPOSE_ID=$1
Packit 63bb0d
    METADATA_FILE=${WORKSPACE}/osbuild-${ID}-${VERSION_ID}-${IMAGE_TYPE}.json
Packit 63bb0d
Packit 63bb0d
    # Download the metadata.
Packit 63bb0d
    sudo composer-cli compose metadata $COMPOSE_ID > /dev/null
Packit 63bb0d
Packit 63bb0d
    # Find the tarball and extract it.
Packit 63bb0d
    TARBALL=$(basename $(find . -maxdepth 1 -type f -name "*-metadata.tar"))
Packit 63bb0d
    tar -xf $TARBALL
Packit 63bb0d
    rm -f $TARBALL
Packit 63bb0d
Packit 63bb0d
    # Move the JSON file into place.
Packit 63bb0d
    cat ${COMPOSE_ID}.json | jq -M '.' | tee $METADATA_FILE > /dev/null
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
# Write a basic blueprint for our image.
Packit 63bb0d
# NOTE(mhayden): The service customization will always be required for QCOW2
Packit 63bb0d
# but it is needed for OpenStack due to issue #698 in osbuild-composer. ๐Ÿ˜ญ
Packit 63bb0d
# NOTE(mhayden): The cloud-init package isn't included in VHD/Azure images
Packit 63bb0d
# by default and it must be added here.
Packit 63bb0d
tee $BLUEPRINT_FILE > /dev/null << EOF
Packit 63bb0d
name = "bash"
Packit 63bb0d
description = "A base system with bash"
Packit 63bb0d
version = "0.0.1"
Packit 63bb0d
Packit 63bb0d
[[packages]]
Packit 63bb0d
name = "bash"
Packit 63bb0d
Packit 63bb0d
[[packages]]
Packit 63bb0d
name = "cloud-init"
Packit 63bb0d
Packit 63bb0d
[customizations.services]
Packit 63bb0d
enabled = ["sshd", "cloud-init", "cloud-init-local", "cloud-config", "cloud-final"]
Packit 63bb0d
Packit 63bb0d
[customizations.kernel]
Packit 63bb0d
append = "LANG=en_US.UTF-8 net.ifnames=0 biosdevname=0"
Packit 63bb0d
EOF
Packit 63bb0d
Packit 63bb0d
# Prepare the blueprint for the compose.
Packit 63bb0d
greenprint "๐Ÿ“‹ Preparing blueprint"
Packit 63bb0d
sudo composer-cli blueprints push $BLUEPRINT_FILE
Packit 63bb0d
sudo composer-cli blueprints depsolve bash
Packit 63bb0d
Packit 63bb0d
# Get worker unit file so we can watch the journal.
Packit 63bb0d
WORKER_UNIT=$(sudo systemctl list-units | egrep -o "osbuild.*worker.*\.service")
Packit 63bb0d
sudo journalctl -af -n 1 -u ${WORKER_UNIT} &
Packit 63bb0d
WORKER_JOURNAL_PID=$!
Packit 63bb0d
Packit 63bb0d
# Start the compose
Packit 63bb0d
greenprint "๐Ÿš€ Starting compose"
Packit 63bb0d
sudo composer-cli --json compose start bash $IMAGE_TYPE | tee $COMPOSE_START
Packit 63bb0d
COMPOSE_ID=$(jq -r '.build_id' $COMPOSE_START)
Packit 63bb0d
Packit 63bb0d
# Wait for the compose to finish.
Packit 63bb0d
greenprint "โฑ Waiting for compose to finish: ${COMPOSE_ID}"
Packit 63bb0d
while true; do
Packit 63bb0d
    sudo composer-cli --json compose info ${COMPOSE_ID} | tee $COMPOSE_INFO > /dev/null
Packit 63bb0d
    COMPOSE_STATUS=$(jq -r '.queue_status' $COMPOSE_INFO)
Packit 63bb0d
Packit 63bb0d
    # Is the compose finished?
Packit 63bb0d
    if [[ $COMPOSE_STATUS != RUNNING ]] && [[ $COMPOSE_STATUS != WAITING ]]; then
Packit 63bb0d
        break
Packit 63bb0d
    fi
Packit 63bb0d
Packit 63bb0d
    # Wait 30 seconds and try again.
Packit 63bb0d
    sleep 5
Packit 63bb0d
done
Packit 63bb0d
Packit 63bb0d
# Capture the compose logs from osbuild.
Packit 63bb0d
greenprint "๐Ÿ’ฌ Getting compose log and metadata"
Packit 63bb0d
get_compose_log $COMPOSE_ID
Packit 63bb0d
get_compose_metadata $COMPOSE_ID
Packit 63bb0d
Packit 63bb0d
# Did the compose finish with success?
Packit 63bb0d
if [[ $COMPOSE_STATUS != FINISHED ]]; then
Packit 63bb0d
    echo "Something went wrong with the compose. ๐Ÿ˜ข"
Packit 63bb0d
    exit 1
Packit 63bb0d
fi
Packit 63bb0d
Packit 63bb0d
# Stop watching the worker journal.
Packit 63bb0d
sudo kill ${WORKER_JOURNAL_PID}
Packit 63bb0d
Packit 63bb0d
# Download the image.
Packit 63bb0d
greenprint "๐Ÿ“ฅ Downloading the image"
Packit 63bb0d
sudo composer-cli compose image ${COMPOSE_ID} > /dev/null
Packit 63bb0d
IMAGE_FILENAME=$(basename $(find . -maxdepth 1 -type f -name "*.${IMAGE_EXTENSION}"))
Packit 63bb0d
LIBVIRT_IMAGE_PATH=/var/lib/libvirt/images/${IMAGE_KEY}.${IMAGE_EXTENSION}
Packit 63bb0d
sudo mv $IMAGE_FILENAME $LIBVIRT_IMAGE_PATH
Packit 63bb0d
Packit 63bb0d
# Prepare cloud-init data.
Packit 63bb0d
CLOUD_INIT_DIR=$(mktemp -d)
Packit 63bb0d
cp ${WORKSPACE}/test/cloud-init/{meta,user}-data ${CLOUD_INIT_DIR}/
Packit 63bb0d
cp ${WORKSPACE}/test/cloud-init/network-config ${CLOUD_INIT_DIR}/
Packit 63bb0d
Packit 63bb0d
# Set up a cloud-init ISO.
Packit 63bb0d
greenprint "๐Ÿ’ฟ Creating a cloud-init ISO"
Packit 63bb0d
CLOUD_INIT_PATH=/var/lib/libvirt/images/seed.iso
Packit 63bb0d
rm -f $CLOUD_INIT_PATH
Packit 63bb0d
pushd $CLOUD_INIT_DIR
Packit 63bb0d
    sudo genisoimage -o $CLOUD_INIT_PATH -V cidata \
Packit 63bb0d
        -r -J user-data meta-data network-config 2>&1 > /dev/null
Packit 63bb0d
popd
Packit 63bb0d
Packit 63bb0d
# Ensure SELinux is happy with our new images.
Packit 63bb0d
greenprint "๐Ÿ‘ฟ Running restorecon on image directory"
Packit 63bb0d
sudo restorecon -Rv /var/lib/libvirt/images/
Packit 63bb0d
Packit 63bb0d
# Run virt-install to import the QCOW and boot it.
Packit 63bb0d
greenprint "๐Ÿš€ Booting the image with libvirt"
Packit 63bb0d
if [[ $ARCH == 'ppc64le' ]]; then
Packit 63bb0d
    # ppc64le has some machine quirks that must be worked around.
Packit 63bb0d
    sudo virt-install \
Packit 63bb0d
        --name $IMAGE_KEY \
Packit 63bb0d
        --memory 2048 \
Packit 63bb0d
        --vcpus 2 \
Packit 63bb0d
        --disk path=${LIBVIRT_IMAGE_PATH} \
Packit 63bb0d
        --disk path=${CLOUD_INIT_PATH},device=cdrom \
Packit 63bb0d
        --import \
Packit 63bb0d
        --os-variant rhel8-unknown \
Packit 63bb0d
        --noautoconsole \
Packit 63bb0d
        --network network=integration,mac=34:49:22:B0:83:30 \
Packit 63bb0d
        --qemu-commandline="-machine pseries,cap-cfpc=broken,cap-sbbc=broken,cap-ibs=broken,cap-ccf-assist=off,cap-large-decr=off"
Packit 63bb0d
elif [[ $ARCH == 's390x' ]]; then
Packit 63bb0d
    # Our s390x machines are highly constrained on resources.
Packit 63bb0d
    sudo virt-install \
Packit 63bb0d
        --name $IMAGE_KEY \
Packit 63bb0d
        --memory 512 \
Packit 63bb0d
        --vcpus 1 \
Packit 63bb0d
        --disk path=${LIBVIRT_IMAGE_PATH} \
Packit 63bb0d
        --disk path=${CLOUD_INIT_PATH},device=cdrom \
Packit 63bb0d
        --import \
Packit 63bb0d
        --os-variant rhel8-unknown \
Packit 63bb0d
        --noautoconsole \
Packit 63bb0d
        --network network=integration,mac=34:49:22:B0:83:30
Packit 63bb0d
else
Packit 63bb0d
    sudo virt-install \
Packit 63bb0d
        --name $IMAGE_KEY \
Packit 63bb0d
        --memory 1024 \
Packit 63bb0d
        --vcpus 2 \
Packit 63bb0d
        --disk path=${LIBVIRT_IMAGE_PATH} \
Packit 63bb0d
        --disk path=${CLOUD_INIT_PATH},device=cdrom \
Packit 63bb0d
        --import \
Packit 63bb0d
        --os-variant rhel8-unknown \
Packit 63bb0d
        --noautoconsole \
Packit 63bb0d
        --network network=integration,mac=34:49:22:B0:83:30
Packit 63bb0d
fi
Packit 63bb0d
Packit 63bb0d
# Set a number of maximum loops to check for our smoke test file via ssh.
Packit 63bb0d
case $ARCH in
Packit 63bb0d
    s390x)
Packit 63bb0d
        # s390x needs more time to boot its VM.
Packit 63bb0d
        MAX_LOOPS=60
Packit 63bb0d
        ;;
Packit 63bb0d
    *)
Packit 63bb0d
        MAX_LOOPS=30
Packit 63bb0d
        ;;
Packit 63bb0d
esac
Packit 63bb0d
Packit 63bb0d
# Check for our smoke test file.
Packit 63bb0d
greenprint "๐Ÿ›ƒ Checking for smoke test file in VM"
Packit 63bb0d
for LOOP_COUNTER in `seq 0 ${MAX_LOOPS}`; do
Packit 63bb0d
    RESULTS="$(smoke_test_check $INSTANCE_ADDRESS)"
Packit 63bb0d
    if [[ $RESULTS == 1 ]]; then
Packit 63bb0d
        echo "Smoke test passed! ๐Ÿฅณ"
Packit 63bb0d
        break
Packit 63bb0d
    fi
Packit 63bb0d
    sleep 10
Packit 63bb0d
done
Packit 63bb0d
Packit 63bb0d
# Clean up our mess.
Packit 63bb0d
greenprint "๐Ÿงผ Cleaning up"
Packit 63bb0d
sudo virsh destroy ${IMAGE_KEY}
Packit 63bb0d
if [[ $ARCH == aarch64 ]]; then
Packit 63bb0d
    sudo virsh undefine ${IMAGE_KEY} --nvram
Packit 63bb0d
else
Packit 63bb0d
    sudo virsh undefine ${IMAGE_KEY}
Packit 63bb0d
fi
Packit 63bb0d
sudo rm -f $LIBVIRT_IMAGE_PATH $CLOUD_INIT_PATH
Packit 63bb0d
Packit 63bb0d
# Use the return code of the smoke test to determine if we passed or failed.
Packit 63bb0d
if [[ $RESULTS == 1 ]]; then
Packit 63bb0d
  greenprint "๐Ÿ’š Success"
Packit 63bb0d
else
Packit 63bb0d
  greenprint "โŒ Failed"
Packit 63bb0d
  exit 1
Packit 63bb0d
fi
Packit 63bb0d
Packit 63bb0d
exit 0