#!/bin/bash
#
# Copyright 2013 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.

set -e
set -o pipefail

SCRIPT_NAME=$(basename $0)
SCRIPT_HOME=$(dirname $0)

PATH=$PATH:/usr/sbin:/sbin

# Some defaults
ARCH=i386
export IMAGE_NAME=seed
export DIB_IMAGE_SIZE=30
CREATE_IMAGE=yes
ALWAYS_ELEMENTS="vm cloud-init-nocloud local-config boot-stack seed-stack-config remove-serial-console"
DIB_COMMON_ELEMENTS=${DIB_COMMON_ELEMENTS:-''}
SEED_DIB_EXTRA_ARGS=${SEED_DIB_EXTRA_ARGS:-'rabbitmq-server'}
if [ $USE_IRONIC -eq 0 ]; then
    SEED_DIB_EXTRA_ARGS="$SEED_DIB_EXTRA_ARGS nova-baremetal"
else
    SEED_DIB_EXTRA_ARGS="$SEED_DIB_EXTRA_ARGS nova-ironic"
fi
export VM_IP=""

function show_options () {
    echo "Usage: $SCRIPT_NAME [options] <element> [<element> ...]"
    echo
    echo "Create and start a VM by combining the specified elements"
    echo "with common default elements, assuming many things about"
    echo "the local operating environment."
    echo "See ../scripts/devtest.sh"
    echo
    echo "The environment variable TE_DATAFILE must be set, pointing at a test"
    echo "environment JSON file. If seed-ip is present in the JSON then that is"
    echo "used for the VM IP address, otherwise it is discovered by probing the"
    echo "ARP table and then saved back into the JSON file."
    echo
    echo "If host-ip (and possibly ssh-user) is set in the JSON then those details"
    echo "are used to construct a remote libvirt URL and spawn the VM remotely."
    echo "Note that seed-ip *must* be present when doing this. When spawning remotely"
    echo "the image is copied to that host via rsync, and a remote virsh URI is used."
    echo "However SSH access with rsync write access to /var/lib/libvirt/images/,"
    echo "permission to chattr, and the ability to run virsh as the selected user are"
    echo "requirements."
    echo
    echo "Options:"
    echo "      -a i386|amd64     -- set the architecture of the VM (i386)"
    echo "      -o name           -- set the name of the VM and image file"
    echo "                           (seed) - must match that from setup-seed-vm"
    echo "      -s size           -- set the image size (30 GB)"
    echo "      -c                -- use a image cache for seed image"
    echo "      -i                -- image file was built elsewhere, don't"
    echo "                           create"
    echo
    exit $1
}

TEMP=$(getopt -o hcia:o:s: -n $SCRIPT_NAME -- "$@")
if [ $? != 0 ] ; then echo "Terminating..." >&2 ; exit 1 ; fi

# Note the quotes around `$TEMP': they are essential!
eval set -- "$TEMP"

while true ; do
    case "$1" in
        -a) export ARCH=$2; shift 2 ;;
        -o) export IMAGE_NAME=$2; shift 2 ;;
        -s) export DIB_IMAGE_SIZE=$2; shift 2 ;;
        -h) show_options 0;;
        -c) export IMAGE_CACHE_USE=1; shift ;;
        -i) export CREATE_IMAGE=; shift ;;
        --) shift ; break ;;
        *) echo "Error: unsupported option $1." ; exit 1 ;;
    esac
done

for arg do SEED_DIB_EXTRA_ARGS="$SEED_DIB_EXTRA_ARGS $arg" ; done

SEED_ARCH=

case $ARCH in
    i386) SEED_ARCH='i686'; ;;
    amd64) SEED_ARCH='x86_64'; ;;
    *) echo "Unsupported arch $ARCH!" ; exit 1 ;;
esac

if [ -z "$TE_DATAFILE" ]; then
    echo "Error: TE_DATAFILE not set."
    show_options 1
fi

HOST_IP=$(OS_CONFIG_FILES=$TE_DATAFILE os-apply-config --key host-ip --type netaddress --key-default '')
REMOTE_OPERATIONS=$(OS_CONFIG_FILES=$TE_DATAFILE os-apply-config --key remote-operations --type raw --key-default '')
if [ -n "$HOST_IP" ]; then
  SSH_USER=$(OS_CONFIG_FILES=$TE_DATAFILE os-apply-config --key ssh-user --type raw --key-default '')
  if [ -n "$SSH_USER" ]; then
    SSH_USER="${SSH_USER}@"
  fi
  VM_HOST=${SSH_USER}${HOST_IP}
  echo $VM_HOST
fi

ENV_NUM=$(OS_CONFIG_FILES=$TE_DATAFILE os-apply-config --key env-num --type int --key-default 0)

if [ $CREATE_IMAGE  ]; then

    ELEMENTS_PATH=${ELEMENTS_PATH:-$SCRIPT_HOME/../../tripleo-image-elements/elements}
    export ELEMENTS_PATH

    DIB_PATH=${DIB_PATH:-$SCRIPT_HOME/../../diskimage-builder}
    DIB=$(which disk-image-create || echo $DIB_PATH/bin/disk-image-create)

    if [ ! -e $DIB ]; then
        echo "Error: unable to locate disk-image-create"
        exit 1
    fi

fi

# Shutdown any running VM - writing to the image file of a running VM is a
# great way to get a corrupt image file.
if [ -n "$REMOTE_OPERATIONS" ]; then
    ssh -o StrictHostKeyChecking=no -o PasswordAuthentication=no ${VM_HOST} virsh destroy ${IMAGE_NAME}_$ENV_NUM || true
    # nova-baremetal doesn't kill these, they need to be destroyed to avoid conflicting with new CI runs,
    # this can be removed when we're exclusively using ironic.
    if [ "${USE_IRONIC:-0}" = "0" ] ; then
        for NUM in $(seq 0 3) ; do
            ssh -o StrictHostKeyChecking=no -o PasswordAuthentication=no ${VM_HOST} virsh destroy baremetalbrbm${ENV_NUM}_${NUM} || true
        done
    fi
else
    virsh destroy $IMAGE_NAME || true
    if [ "${USE_IRONIC:-0}" = "0" ] ; then
        for NUM in $(seq 0 3) ; do
            virsh destroy baremetal_${NUM} || true
        done
    fi
fi

if [ $CREATE_IMAGE  ]; then

    IMAGE_CACHE_FILE=$TRIPLEO_ROOT/seed

    # Create the image if it doesn't exist or we're not using image cache
    if [ ! -e "$IMAGE_CACHE_FILE.qcow2" -o -z "$IMAGE_CACHE_USE" ] ; then
        $DIB -x -u -a $ARCH $ALWAYS_ELEMENTS $DIB_COMMON_ELEMENTS $SEED_DIB_EXTRA_ARGS -o $IMAGE_CACHE_FILE 2>&1 | tee $IMAGE_CACHE_FILE.log
    else
        echo "Using cached seed image : $IMAGE_CACHE_FILE.qcow2"
    fi

    if [ -n "$REMOTE_OPERATIONS" ]; then
      # rsync could be used here which may have been more efficient but using a
      # custom command "copyseed" should be easier to restrict. Also we can
      # take multiple steps on the server in this single command meaning we
      # don't have to open up the ssh access even further.
      dd if=$IMAGE_CACHE_FILE.qcow2 | ssh -o StrictHostKeyChecking=no -o PasswordAuthentication=no ${VM_HOST} copyseed $ENV_NUM
    else
      sudo cp $IMAGE_CACHE_FILE.qcow2 /var/lib/libvirt/images/$IMAGE_NAME.qcow2
      sudo chattr +C /var/lib/libvirt/images/$IMAGE_NAME.qcow2 || true
    fi
fi

function poll_vm {
  if [ -z "$VM_IP" ]; then
    MAC=$(sudo virsh dumpxml $IMAGE_NAME | grep "mac address" | head -1 | awk -F "'" '{print $2}')
    VM_IP=$(arp -n | grep $MAC | awk '{print $1}')
  fi
  [ -z $VM_IP ] && return 1
  ping -c 1 $VM_IP || return 1
  return 0
}
export -f poll_vm

if [ -n "$REMOTE_OPERATIONS" ]; then
  ssh -o StrictHostKeyChecking=no -o PasswordAuthentication=no ${VM_HOST} virsh start ${IMAGE_NAME}_$ENV_NUM
  VM_IP=$(OS_CONFIG_FILES=$TE_DATAFILE os-apply-config --key seed-ip --type netaddress --key-default '')
else
  sudo virsh start $IMAGE_NAME
fi

echo "Waiting for $IMAGE_NAME VM to boot."
wait_for 100 1 poll_vm
poll_vm

echo
echo "Booted. Found IP: $VM_IP. Waiting for ssh service to start."
until echo '' | nc $VM_IP 22; do
    echo -n '.'
    sleep 1
done
echo

# hostkeys are generated by cloud-init as part of the boot sequence - can
# take a few seconds.
echo "Waiting for SSH hostkey."
SSH_ARGS="-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null"
until ssh $SSH_ARGS root@$VM_IP uptime 2>&1 > /dev/null; do
    echo -n '.'
    sleep 1
done
echo

# Remove the 192.0.2.1 hostkey, new instance == new key.
ssh-keygen -R 192.0.2.1 || true

echo "element(s): $ALWAYS_ELEMENTS $DIB_COMMON_ELEMENTS $SEED_DIB_EXTRA_ARGS booted and ready."
echo "SEED_IP=$VM_IP"
echo
echo "to login:   ssh root@$VM_IP"
NEW_JSON=$(jq '.["seed-ip"]="'${VM_IP}'"' $TE_DATAFILE)
echo $NEW_JSON > $TE_DATAFILE
