#!/bin/bash
#
# Script that spawns an instance
# Has 2 operational modes:
#   - workload: spawn VM with floating ip (default)
#   - sanity:   spanw VM with floating ip assert it's ssh'able,
#               delete VM and associated resources
#

if [[ -n ${1} ]]; then
    MODE="${1}"
else
    MODE="workload"
fi

function sanity_teardown {
    # remove auxiliary resources

    local timeout_seconds=180
    local elapsed_seconds=0

    echo "Remove ${INSTANCE_FIP} from ${INSTANCE_NAME}"
    openstack server remove floating ip ${INSTANCE_NAME} ${INSTANCE_FIP}

    echo "Delete floating ip ${INSTANCE_FIP}"
    openstack floating ip delete ${INSTANCE_FIP}

    echo "Remove VM ${INSTANCE_NAME}"
    openstack server delete ${INSTANCE_NAME}
    while true; do
        openstack server list -f value -c Name | grep -q ${INSTANCE_NAME}
        if [[ ${?} -eq 1 ]]; then
            echo "Instance ${INSTANCE_NAME} successfully removed"
            break
        fi
        elapsed_seconds=$(( ${elapsed_seconds} + 5 ))
        if [ $elapsed_seconds -ge $timeout_seconds ]; then
            echo "FAILURE: Failed to remove instance ${INSTANCE_NAME}"
            exit 1
        fi
    done

    echo "Remove cinder volume ${CINDER_VOL_ID}"
    openstack volume delete ${CINDER_VOL_ID}

    echo "Clear default gateway from ${TENANT_NET_NAME}_router"
    openstack router unset --external-gateway ${TENANT_NET_NAME}_router

    echo "Remove subnet ${TENANT_NET_NAME}_subnet from router ${TENANT_NET_NAME}_router"
    openstack router remove subnet ${TENANT_NET_NAME}_router ${TENANT_NET_NAME}_subnet

    echo "Remove router ${TENANT_NET_NAME}_router"
    openstack router delete ${TENANT_NET_NAME}_router

    echo "Remove subnet ${TENANT_NET_NAME}_subnet"
    openstack subnet delete ${TENANT_NET_NAME}_subnet

    echo "Remove network ${TENANT_NET_NAME}"
    openstack network delete ${TENANT_NET_NAME}

    echo "Remove security group ${SECGROUP_NAME}"
    openstack security group delete ${SECGROUP_NAME}

    echo "Remove keypair ${KEYPAIR_NAME}"
    openstack keypair delete ${KEYPAIR_NAME}

    echo "Remove image ${IMAGE_NAME}"
    openstack image delete ${IMAGE_NAME}

    echo "Remove file ${IMAGE_FILE}"
    rm -Rf $(dirname ${IMAGE_FILE})

}

SUFFIX=$(openssl rand -hex 5)

OVERCLOUD_RC='{{ overcloud_rc }}'
IMAGE_URL='{{ workload_image_url }}'
IMAGE_NAME="upgrade_workload_${SUFFIX}"
IMAGE_FILE="$(mktemp -d )/upgrade_workload_image.qcow2"
INSTANCE_NAME="instance_${SUFFIX}"
INSTANCE_USER='{{ workload_user }}'
KEYPAIR_NAME="userkey_${SUFFIX}"
FLAVOR_NAME="v1-1G-{{ workload_disk}}G-${SUFFIX}"
SECGROUP_NAME="allow-icmp-ssh-${SUFFIX}"
TENANT_NET_NAME="internal_net_${SUFFIX}"
EXTERNAL_NET_NAME='{{ external_network_name }}'

source ${OVERCLOUD_RC}

## create image
openstack image list | grep ${IMAGE_NAME}

if [ $? -ne 0 ]; then
    echo "Downloading image ${IMAGE_URL}"
    curl --silent --retry 3 -L -4 -o ${IMAGE_FILE} ${IMAGE_URL}

    if [ $? -ne 0 ]; then
        echo "Failed to download ${IMAGE_URL}"
        exit 1
    fi

    echo "Uploading ${IMAGE_NAME} to Glance"
    openstack image create \
        --file ${IMAGE_FILE} \
        --disk-format qcow2 \
        --container-format bare \
        ${IMAGE_NAME}
fi

## create user key
openstack keypair list | grep ${KEYPAIR_NAME}
if [ $? -ne 0 ]; then
    echo "Creating keypair ${KEYPAIR_NAME}"
    openstack keypair create --public-key ~/.ssh/id_rsa.pub ${KEYPAIR_NAME}
fi

## create flavor
openstack flavor list | grep ${FLAVOR_NAME}
if [ $? -ne 0 ]; then
    echo "Creating flavor ${FLAVOR_NAME}"
    openstack flavor create --vcpus {{ workload_vcpu }} \
        --ram {{ workload_memory }} \
        --disk {{ workload_disk }} \
        --swap {{ workload_swap }} \
        $FLAVOR_NAME
fi

## create networking
openstack network list | grep ${TENANT_NET_NAME}
if [ $? -ne 0 ]; then
    NAMESERVER=$(grep -Eo '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}' /etc/resolv.conf  | head -1)
    echo "Creating router ${TENANT_NET_NAME}_router"
    openstack router create ${TENANT_NET_NAME}_router

    echo "Creating network ${TENANT_NET_NAME}"
    openstack network create ${TENANT_NET_NAME}

    echo "Creating subnet ${TENANT_NET_NAME}_subnet"
    openstack subnet create \
        --subnet-range 192.168.0.0/24 \
        --allocation-pool start=192.168.0.10,end=192.168.0.100 \
        --gateway 192.168.0.254 \
        --dns-nameserver ${NAMESERVER} \
        --network ${TENANT_NET_NAME} \
        ${TENANT_NET_NAME}_subnet

    echo "Add subnet ${TENANT_NET_NAME}_subnet to router ${TENANT_NET_NAME}_router"
    openstack router add subnet  ${TENANT_NET_NAME}_router ${TENANT_NET_NAME}_subnet

    echo "Set external-gateway for ${TENANT_NET_NAME}_router"
    openstack router set --external-gateway ${EXTERNAL_NET_NAME} ${TENANT_NET_NAME}_router
fi

## create security group
openstack security group list | grep ${SECGROUP_NAME}
if [ $? -ne 0 ]; then
    echo "Creating security group ${SECGROUP_NAME}"
    openstack security group create ${SECGROUP_NAME}

    echo "Creating rules for ports 22,80,443 in security group ${SECGROUP_NAME}"
    openstack security group rule create --proto icmp ${SECGROUP_NAME}
    openstack security group rule create --proto tcp --dst-port 22 ${SECGROUP_NAME}
    openstack security group rule create --proto tcp --dst-port 80 ${SECGROUP_NAME}
    openstack security group rule create --proto tcp --dst-port 443 ${SECGROUP_NAME}
fi

## create instance
TENANT_NET_ID=$( openstack network show -f json ${TENANT_NET_NAME} |  jq -r -c '.id' )

echo "Creating overcloud instance ${INSTANCE_NAME}"
openstack server create  \
    --image ${IMAGE_NAME} \
    --flavor ${FLAVOR_NAME} \
    --security-group ${SECGROUP_NAME} \
    --key-name  ${KEYPAIR_NAME} \
    --nic net-id=${TENANT_NET_ID} \
    ${INSTANCE_NAME}

timeout_seconds=120
elapsed_seconds=0
while true; do
    INSTANCE_STATUS=$(openstack server show ${INSTANCE_NAME} -f json | jq -r '.status')
    case "${INSTANCE_STATUS}" in
        "ACTIVE")
            echo "${INSTANCE_NAME} reached 'ACTIVE' status"
            break
            ;;
        "ERROR")
            echo "${INSTANCE_NAME} failed"
            exit 1
    esac
    sleep 3
    elapsed_seconds=$(expr $elapsed_seconds + 3)
    if [ $elapsed_seconds -ge $timeout_seconds ]; then
        echo "FAILURE: Instance failed to boot within ${elapsed_seconds} seconds"
        openstack server show ${INSTANCE_NAME} -f json 2>&1
        exit 1
    fi
done

## assign floating ip
INSTANCE_FIP=$(openstack floating ip create ${EXTERNAL_NET_NAME} -f json | jq -r -c '.floating_ip_address' )

echo "Assign FIP[${INSTANCE_FIP}] to server ${INSTANCE_NAME}"
openstack server add floating ip ${INSTANCE_NAME} ${INSTANCE_FIP}

## create and attach a volume
CINDER_VOL_ID=$(openstack volume create --size 1 vol_${SUFFIX} -f json | jq -r .id)
echo "Attach volume vol_${SUFFIX} to instance ${INSTANCE_NAME}"
openstack server add volume ${INSTANCE_NAME} ${CINDER_VOL_ID}

## SSH to VM through it's floating ip
if [[ "${MODE}" == "sanity" ]]; then
    timeout_seconds=180
    elapsed_seconds=0

    while true; do
        # assert instance is reachable via FIP
        echo "  [$(date)] Trying to ssh to ${INSTANCE_FIP}"
        ssh -q -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \
            ${INSTANCE_USER}@${INSTANCE_FIP} 'whoami'

        if [[ ${?} -eq 0 ]]; then
            echo "Instance ${INSTANCE_NAME} is reachable via ${INSTANCE_FIP}"
            break
        fi
        sleep 3
        elapsed_seconds=$(expr $elapsed_seconds + 3)
        if [ $elapsed_seconds -ge $timeout_seconds ]; then
            echo "FAILURE: Instance failed to boot."
            sanity_teardown
            exit 1
        fi
    done
fi

if [[ "${MODE}" == "workload" ]]; then
    echo "floating-ip: ${INSTANCE_FIP}" > ~/${INSTANCE_NAME}
fi

if [[ "${MODE}" == "sanity" ]]; then
    sanity_teardown
fi
