#!/bin/bash

xe_min()
{
    local cmd="$1"
    shift
    xe "$cmd" --minimal "$@"
}

function die_with_error {
    local err_msg

    err_msg="$1"

    echo "$err_msg" >&2
    exit 1
}

function get_local_sr {
    xe pool-list params=default-SR minimal=true
}

function find_ip_by_name {
    local guest_name="$1"
    local interface="$2"

    local period=10
    local max_tries=10
    local i=0

    while true; do
        if [ $i -ge $max_tries ]; then
            echo "Timeout: ip address for interface $interface of $guest_name"
            exit 11
        fi

        ipaddress=$(xe vm-list --minimal \
                    name-label=$guest_name \
                    params=networks | sed -ne "s,^.*${interface}/ip: \([0-9.]*\).*\$,\1,p")

        if [ -z "$ipaddress" ]; then
            sleep $period
            i=$((i+1))
        else
            echo $ipaddress
            break
        fi
    done
}

function _vm_uuid {
    local vm_name_label

    vm_name_label="$1"

    xe vm-list name-label="$vm_name_label" --minimal
}

function _create_new_network {
    local name_label
    name_label=$1

    uuid=$(xe network-create name-label="$name_label")
    xe network-param-add uuid=$uuid param-name=other-config assume_network_is_shared='true'
}

function _multiple_networks_with_name {
    local name_label
    name_label=$1

    # A comma indicates multiple matches
    xe network-list name-label="$name_label" --minimal | grep -q ","
}

function _network_exists {
    local name_label
    name_label=$1

    ! [ -z "$(xe network-list name-label="$name_label" --minimal)" ]
}

function _bridge_exists {
    local bridge
    bridge=$1

    ! [ -z "$(xe network-list bridge="$bridge" --minimal)" ]
}

function _network_uuid {
    local bridge_or_net_name
    bridge_or_net_name=$1

    if _bridge_exists "$bridge_or_net_name"; then
        xe network-list bridge="$bridge_or_net_name" --minimal
    else
        xe network-list name-label="$bridge_or_net_name" --minimal
    fi
}

function add_interface {
    local vm_name_label
    local bridge_or_network_name

    vm_name_label="$1"
    bridge_or_network_name="$2"
    device_number="$3"

    local vm
    local net

    vm=$(_vm_uuid "$vm_name_label")
    net=$(_network_uuid "$bridge_or_network_name")
    xe vif-create network-uuid=$net vm-uuid=$vm device=$device_number
}

function setup_network {
    local bridge_or_net_name
    bridge_or_net_name=$1

    if ! _bridge_exists "$bridge_or_net_name"; then
        if _network_exists "$bridge_or_net_name"; then
            if _multiple_networks_with_name "$bridge_or_net_name"; then
                cat >&2 << EOF
ERROR: Multiple networks found matching name-label to "$bridge_or_net_name"
please review your XenServer network configuration / localrc file.
EOF
                exit 1
            fi
        else
            _create_new_network "$bridge_or_net_name"
        fi
    fi
}

function bridge_for {
    local bridge_or_net_name
    bridge_or_net_name=$1

    if _bridge_exists "$bridge_or_net_name"; then
        echo "$bridge_or_net_name"
    else
        xe network-list name-label="$bridge_or_net_name" params=bridge --minimal
    fi
}

function xenapi_ip_on {
    local bridge_or_net_name
    bridge_or_net_name=$1

    ip -4 addr show $(bridge_for "$bridge_or_net_name") |\
    awk '/inet/{split($2, ip, "/"); print ip[1];}'
}

function xenapi_is_listening_on {
    local bridge_or_net_name
    bridge_or_net_name=$1

    ! [ -z $(xenapi_ip_on "$bridge_or_net_name") ]
}

function parameter_is_specified {
    local parameter_name
    parameter_name=$1

    compgen -v | grep "$parameter_name"
}

function append_kernel_cmdline {
    local vm_name_label
    local kernel_args

    vm_name_label="$1"
    kernel_args="$2"

    local vm
    local pv_args

    vm=$(_vm_uuid "$vm_name_label")
    pv_args=$(xe vm-param-get param-name=PV-args uuid=$vm)
    xe vm-param-set PV-args="$pv_args $kernel_args" uuid=$vm
}

function destroy_all_vifs_of {
    local vm_name_label

    vm_name_label="$1"

    local vm

    vm=$(_vm_uuid "$vm_name_label")
    IFS=,
    for vif in $(xe vif-list vm-uuid=$vm --minimal); do
        xe vif-destroy uuid="$vif"
    done
    unset IFS
}

function have_multiple_hosts {
    xe host-list --minimal | grep -q ","
}

function get_current_host_uuid {
    source /etc/xensource-inventory;
    echo $INSTALLATION_UUID
}

function get_current_dom0_uuid {
    source /etc/xensource-inventory;
    echo $CONTROL_DOMAIN_UUID
}

function attach_network {
    local bridge_or_net_name

    bridge_or_net_name="$1"

    local net
    local host

    net=$(_network_uuid "$bridge_or_net_name")
    host=$(get_current_host_uuid)

    xe network-attach uuid=$net host-uuid=$host
}

function set_vm_memory {
    local vm_name_label
    local memory

    vm_name_label="$1"
    memory="$2"

    local vm

    vm=$(_vm_uuid "$vm_name_label")

    xe vm-memory-limits-set \
        static-min=${memory}MiB \
        static-max=${memory}MiB \
        dynamic-min=${memory}MiB \
        dynamic-max=${memory}MiB \
        uuid=$vm
}

function set_vm_disk {
    local vm_name_label
    local vm_disk_size

    vm_name_label="$1"
    vm_disk_size="$2"

    local vm_uuid
    local vm_vbd
    local vm_vdi

    vm_uuid=$(xe vm-list name-label=$vm_name_label --minimal)
    vm_vbd=$(xe vbd-list vm-uuid=$vm_uuid device=xvda --minimal)
    vm_vdi=$(xe vdi-list vbd-uuids=$vm_vbd --minimal)

    xe vdi-resize uuid=$vm_vdi disk-size=$((vm_disk_size * 1024 * 1024 * 1024))
}


function max_vcpus {
    local vm_name_label

    vm_name_label="$1"

    local vm
    local host
    local cpu_count

    host=$(get_current_host_uuid)

    vm=$(_vm_uuid "$vm_name_label")

    cpu_count=$(xe host-param-get \
        param-name=cpu_info \
        uuid=$host |
        sed -e 's/^.*cpu_count: \([0-9]*\);.*$/\1/g')

    if [ -z "$cpu_count" ]; then
        # get dom0's vcpu count
        cpu_count=$(cat /proc/cpuinfo | grep processor | wc -l)
    fi

    # Assert cpu_count is not empty
    [ -n "$cpu_count" ]

    # Assert ithas a numeric nonzero value
    expr "$cpu_count" + 0

    # 8 VCPUs should be enough for devstack VM; avoid using too
    # many VCPUs:
    # 1. too many VCPUs may trigger a kernel bug which result VM
    #    not able to boot:
    #    https://kernel.googlesource.com/pub/scm/linux/kernel/git/wsa/linux/+/e2e004acc7cbe3c531e752a270a74e95cde3ea48
    # 2. The remaining CPUs can be used for other purpose:
    #    e.g. boot test VMs.
    MAX_VCPUS=8
    if [ $cpu_count -ge $MAX_VCPUS ]; then
        cpu_count=$MAX_VCPUS
    fi

    xe vm-param-set uuid=$vm VCPUs-max=$cpu_count
    xe vm-param-set uuid=$vm VCPUs-at-startup=$cpu_count
}

function get_template {
    local tmp_name="$1"
    local host="$2"
    tmp=$(xe template-list name-label="$tmp_name" --minimal)
    if [[ $tmp == *","* ]]; then
        tmp_group=tmp
        tmp=$(xe template-list name-label="$tmp_name" \
        possible-hosts=$host --minimal)
        # Current host have no template
        if [[ -z $tmp ]]; then
            tmp=${tmp_group##*,}
        fi
    fi
    echo $tmp
}

function clean_template_other_conf {
    local tmp_name="$1"
    tmp=$(xe template-list name-label="$tmp_name" --minimal)
    if [ -n "\$tmp" ]; then
        echo "  $tmp clean other configure"
        IFS=','
        for i in "\${tmp[@]}"; do
            xe template-param-clear param-name=other-config uuid=$i > /dev/null
        done
    fi
}

function uninstall_template {
    local tmp_name="$1"
    tmp=$(xe template-list name-label="$tmp_name" --minimal)
    if [ -n "\$tmp" ]; then
        echo "  $tmp already exist, uninstalling"
        IFS=','
        for i in "\${tmp[@]}"; do
            xe template-uninstall template-uuid="\$i" force=true > /dev/null
        done
    fi
}

function get_domid {
    local vm_name_label

    vm_name_label="$1"

    xe vm-list name-label="$vm_name_label" params=dom-id minimal=true
}

function install_conntrack_tools {
    local xs_host
    local xs_ver_major
    local centos_ver
    local conntrack_conf
    xs_host=$(get_current_host_uuid)
    xs_ver_major=$(xe host-param-get uuid=$xs_host param-name=software-version param-key=product_version_text_short | cut -d'.' -f 1)
    if [ $xs_ver_major -gt 6 ]; then
        # Only support conntrack-tools in Dom0 with XS7.0 and above
        if [ ! -f /usr/sbin/conntrackd ]; then
            sed -i s/#baseurl=/baseurl=/g /etc/yum.repos.d/CentOS-Base.repo
            centos_ver=$(yum version nogroups |grep Installed | cut -d' ' -f 2 | cut -d'/' -f 1 | cut -d'-' -f 1)
            yum install -y --enablerepo=base --releasever=$centos_ver conntrack-tools
            # Backup conntrackd.conf after install conntrack-tools, use the one with statistic mode
            mv /etc/conntrackd/conntrackd.conf /etc/conntrackd/conntrackd.conf.back
            conntrack_conf=$(find /usr/share/doc -name conntrackd.conf |grep stats)
            cp $conntrack_conf /etc/conntrackd/conntrackd.conf
        fi
        service conntrackd restart
    fi
}

#
# Wait for VM to halt
#
function wait_for_VM_to_halt {
    #mgmt_ip="$1"
    GUEST_VM_NAME="$1"
    set +x
    echo "Waiting for the VM to halt.  Progress in-VM can be checked with XenCenter or xl console:"
    domid=$(get_domid "$GUEST_VM_NAME")
    echo "ssh root@host \"xl console $domid\""
    while true; do
        state=$(xe_min vm-list name-label="$GUEST_VM_NAME" power-state=halted)
        if [ -n "$state" ]; then
            break
        else
            echo -n "."
            sleep 20
        fi
    done
    set -x
}
