# -*-Shell-script-*-
#
# functions     This file contains functions used panini script
#
#
# Panini function check
#
# Copyright (c) 2015-2017 by Cisco Systems, Inc.
# All rights reserved.
#

# source PD-function library
if [ -f /etc/init.d/spirit_pd.sh -a "$spirit_pd_sourced" != "true" ]; then
   . /etc/init.d/spirit_pd.sh
   spirit_pd_sourced=true
fi


# source Libvirt Conf File
. /etc/libvirt/libvirtd.conf

# source logging library
. /etc/init.d/spirit_log.sh

get_board_type

if [[ $PD_BOARD_INST_CONF = "" ]]; then
    readonly PD_BOARD_INST_CONF="/etc/init.d/PD_board_install.cfg"
fi

if [[ $PD_BOOTSTRAP = "" ]]; then
    readonly PD_BOOTSTRAP="/etc/init.d/calvados_bootstrap.cfg"
fi

if [[ $INSTALL_PARAMS_OVERRIDE = "" ]]; then
    readonly INSTALL_PARAMS_OVERRIDE="/etc/init.d/install_params.sh"
fi

SYSADMIN_PATH_FILE="/var/run/sysadmin_path"
XR_PATH_FILE="/var/run/xr_path"
STANDARD_LIBVIRT_PORT=16509
DOCKER_PORT=2376

# Do not launch vm from installer image
# Do not launch vm if image is running from ramdisk
function calvados_launch_check {
  get_board_type
  # Skip launch check for Fretta membooted cards
  if [ "${PLATFORM}" == "fretta" ]; then
    if [ "${BOARDTYPE}" == "XC" -o "${BOARDTYPE}" == "FC" ]; then
      return 0
    fi
  fi
  # Skip start of vm for install iso
  CMDCHEK=`cat /proc/cmdline | grep install=`
  if [ -z "$CMDCHEK" ]; then
    # Do not start VM if we are running from /dev/ram
    RAMCHK=`cat /proc/cmdline | grep "/dev/ram"`
    if [ -z "$RAMCHK" ]; then
      # Ok to start vm only if we running from disk
      ROOTDEV=`cat /proc/cmdline | grep root=`
      if [ -n "$ROOTDEV" ]; then
        return 0
      fi
    fi
  fi
  return 1
}

function launch_check {
  calvados_launch_check 
}

#
# Print a shell backtrace. Useful to see how your script function was sourced
# and called.
#
function backtrace () {
    local deptn=${#FUNCNAME[@]}

    for ((i=1; i<$deptn; i++)); do
        local func="${FUNCNAME[$i]}"
        local line="${BASH_LINENO[$((i-1))]}"
        local src="${BASH_SOURCE[$((i-1))]}"
        printf '%*s at: %s(), %s, line %s\n' $i '' $func $src $line
    done
}


#
# set_rootfs_var
#
function set_rootfs_var ()
{
    if [[ $# -ne 2 ]]; then
        return;
    fi

    local key=$1
    local value=$2

    case "$key" in
    SYSADMIN_PATH)
        echo "SYSADMIN_PATH=$value" > $SYSADMIN_PATH_FILE
        ;;

    XR_PATH)
        echo "XR_PATH=$value" > $XR_PATH_FILE
        ;;

    *)
        declare -F platform_log &>/dev/null && \
            platform_log "set_rootfs_var: Unknown key $key"
        ;;
    esac

    #
    # Invoke the PD counterpart to override or add functionality, if required.
    #
    declare -F pd_set_rootfs_var &>/dev/null && \
                        pd_set_rootfs_var "$key" "$value"

}

#
# populate_rootfs_vars
#
function populate_rootfs_vars ()
{
    if [[ -f $SYSADMIN_PATH_FILE ]]; then
        . $SYSADMIN_PATH_FILE
    fi
    if [[ -f $XR_PATH_FILE ]]; then
        . $XR_PATH_FILE
    fi

    #
    # Invoke the PD counterpart to override or add functionality, if required.
    #
    declare -F pd_populate_rootfs_vars &>/dev/null && pd_populate_rootfs_vars

}


function dump_board_install_parameters_to_file 
{
    outfile=$1

    # clear out any old instances of this file
    if [ -f $outfile ]; then
       rm -f $outfile
    fi

    # We don't want this to be executable script
    # echo "#!/bin/bash" >> $outfile

    echo "MAIN_DISK="$MAIN_DISK >> $outfile
    echo "SECONDARY_DISK="$SECONDARY_DISK >> $outfile
    echo "DISK_HOST_BOOT_PART_SIZE="$DISK_HOST_BOOT_PART_SIZE >> $outfile
    echo "DISK_HOST_DATA_PART_SIZE="$DISK_HOST_DATA_PART_SIZE >> $outfile
    echo "DISK_HOST_EFI_PART_SIZE="$DISK_HOST_EFI_PART_SIZE >> $outfile
    echo "DISK_HOST_BOOT_SIZE="$DISK_HOST_BOOT_SIZE >> $outfile
    echo "DISK_HOST_CONFIG_SIZE="$DISK_HOST_CONFIG_SIZE >> $outfile
    echo "DISK_HOST_LOG_SIZE="$DISK_HOST_LOG_SIZE >> $outfile
    echo "DISK_HOST_SCRATCH_SIZE="$DISK_HOST_SCRATCH_SIZE >> $outfile

    echo "DISK_CALVADOS_BOOT_SIZE="$DISK_CALVADOS_BOOT_SIZE >> $outfile
    echo "DISK_CALVADOS_DATA_SIZE="$DISK_CALVADOS_DATA_SIZE >> $outfile
    echo "DISK_CALVADOS_REPO_SIZE="$DISK_CALVADOS_REPO_SIZE >> $outfile
    echo "DISK_CALVADOS_CONFIG_SIZE="$DISK_CALVADOS_CONFIG_SIZE >> $outfile

    echo "DISK_CALVADOS_LOG_SIZE="$DISK_CALVADOS_LOG_SIZE >> $outfile
    echo "DISK_CALVADOS_SCRATCH_SIZE="$DISK_CALVADOS_SCRATCH_SIZE >> $outfile

    echo "DISK_XR_BOOT_SIZE="$DISK_XR_BOOT_SIZE >> $outfile
    echo "DISK_XR_DATA_SIZE="$DISK_XR_DATA_SIZE >> $outfile
    echo "DISK_XR_CONFIG_SIZE="$DISK_XR_CONFIG_SIZE >> $outfile
    echo "DISK_XR_LOG_SIZE="$DISK_XR_LOG_SIZE >> $outfile
    echo "DISK_XR_SCRATCH_SIZE="$DISK_XR_SCRATCH_SIZE >> $outfile

    echo "DISK_SSD_HOST_SIZE="$DISK_SSD_HOST_SIZE >> $outfile
    echo "DISK_SSD_CALVADOS_SIZE="$DISK_SSD_CALVADOS_SIZE >> $outfile
    echo "DISK_SSD_XR_SIZE="$DISK_SSD_XR_SIZE >> $outfile

    if [[ "$NUM_EXTRA_PARTS" -gt 0 ]]; then
        echo "NUM_EXTRA_PARTS="$NUM_EXTRA_PARTS >> $outfile
        for (( i=1; i<=$NUM_EXTRA_PARTS; i++ )); do
            PART_SIZE="EXTRA_PART_SIZE${i}"
            PART_NAME="EXTRA_PART_NAME${i}"
            FS_VOL_LBL="EXTRA_PART_VOL_LBL${i}"

            echo "EXTRA_PART_SIZE${i}="${!PART_SIZE} >> $outfile
            echo "EXTRA_PART_NAME${i}="${!PART_NAME} >> $outfile
            echo "EXTRA_PART_VOL_LBL${i}="${!FS_VOL_LBL} >> $outfile
        done
   fi

}

# Convert hex base ip address to dot-decimal notation
# $1: hex ip address, eg. 0A000210
# return: dot-decimal ip address, eg. 10.0.2.16
function convert_ip() {
    local HEX_IP=$1
    # Parse hex host ip address to dot-decimal format
    local IP_P1=$((16#${HEX_IP:0:2}))
    local IP_P2=$((16#${HEX_IP:2:2}))
    local IP_P3=$((16#${HEX_IP:4:2}))
    local IP_P4=$((16#${HEX_IP:6:2}))

    local DEC_IP=${IP_P1}.${IP_P2}.${IP_P3}.${IP_P4}

    echo $DEC_IP
}

# run cmd if it is defined as bash function
# $1: cmd to be run
function check_and_run() {
    local cmd=$1

    type -t $cmd  >& /dev/null

    if [ $? -eq 0 ] ; then
        $cmd
    fi
}

function set_proc_sys_net {
    # send first TCP keepalive  after 30 sec 
    echo 30 > /proc/sys/net/ipv4/tcp_keepalive_time
    # and then every 10 sec 
    echo 10 > /proc/sys/net/ipv4/tcp_keepalive_intvl
    # send four probes.
    # This should result into 30+10*4 = 70sec before disconnect
    echo 4 > /proc/sys/net/ipv4/tcp_keepalive_probes
}
#
# Function to read TCP Port from Libvirtd.conf file 
# to see whether standard port is overridden 
#
function get_tcp_port {
if test ${tcp_port+defined}; then 
        echo $tcp_port  
else 
        echo $STANDARD_LIBVIRT_PORT  
fi

}

#
# hostos_get_lxc_card_type
#
# On the hostos, given the VM name, we are able to query the XML 
# configuration of the VM (assuming it is an LXC) and determine 
# the boardtype.
#
function hostos_get_lxc_card_type()
{
    local vm_name=$1

    if [[ "$vm_name" = "" ]]; then
        return 
    fi

    if [[ "$VIRT_METHOD" != "lxc" ]]; then
        return 
    fi

    VMTYPE=`cat /proc/cmdline | sed 's/^.*vmtype=//' | cut -d" " -f1`
    if [[ "$VMTYPE" != "hostos" ]]; then
        return 
    fi

    local mnt_file="/var/run/libvirt/lxc/$vm_name.xml"
    if [[ ! -f "$mnt_file" ]]; then
        return
    fi

    local vm_root=`cat "$mnt_file" | grep "[\"']/lxc_rootfs" | cut -d"'" -f2`

    local card_file="${vm_root}/root/card_instances.txt"
    if [[ ! -f ${card_file} ]]; then
        return
    fi

    cat $card_file | cut -d" " -f1
}

#
# Is app hosting enabled on this platform?
# Is app hosting supported on this vm ? 
#
function app_host_is_enabled()
{
    # vm_name is an optional argument 
    local vm_name=$1

    get_board_type
    if [[ "$BOARDTYPE" == "RP" ]]; then
        if [[ -z "$APP_HOST_ENABLE" ]]; then
           source $PD_BOOTSTRAP
        fi

        case "$vm_name" in
        "")
            # VM name was not specified, break
            ;;
        default-sdr--*)
            # When Sunstone runs in centralized mode,  RP and LC are both
            # run as LXCs, default-sdr--1 (RP) and default-sdr--2 ( LC )
            # App hosting is supported in the RP LXC but not the LC LXC.
            if [[ "$PLATFORM" == "xrv9k" ]]; then
                declare -F is_vm_lcp &>/dev/null && is_vm_lcp $vm_name ""
                if [[ $? -eq 0 ]]; then
                    false
                    return
                fi
            fi
            ;;
        *)
            # Unrecognized/invalid VM name
            false
            return
            ;;
        esac

        if [[ "$APP_HOST_ENABLE" == "TRUE" ]]; then
            true
            return
        fi
    fi
        
    false
    return
}

#
# Is netstack enabled? 
#
# It is on all EXR platforms by default unlike app hosting.
#
function netstack_is_enabled()
{
    local NETSTACK_ENABLE=TRUE
    local vm_name=$1
    local card_type=

    get_board_type
    if [[ "$BOARDTYPE" == "RP" ]]; then
        case "$vm_name" in
        "")
            # VM name was not specified, break
            ;;
        default-sdr--*)
            card_type=$(hostos_get_lxc_card_type $vm_name 2>/dev/null)
            if [[ "$card_type" != "RP" ]]; then
                false
                return
            fi

            # When Sunstone runs in centralized mode,  RP and LC are both
            # run as LXCs, default-sdr--1 (RP) and default-sdr--2 ( LC )
            # App hosting is supported in the RP LXC but not the LC LXC.
            if [[ "$PLATFORM" == "xrv9k" ]]; then
                declare -F is_vm_lcp &>/dev/null && is_vm_lcp $vm_name ""
                if [[ $? -eq 0 ]]; then
                    false
                    return
                fi
            fi
            ;;
        *)
            # Unrecognized/invalid VM name
            false
            return
            ;;
        esac

        if [[ "$NETSTACK_ENABLE" == "TRUE" ]]; then
            true
            return
        fi
    fi
        
    false
    return
}

function app_host_with_separate_pv()
{
    declare -F pd_create_separate_pv >& /dev/null
    if [ $? -eq 0 ]; then
        pd_create_separate_pv
    else
        app_host_is_enabled
    fi
}

# Function to decide whether to enable Port-forwarding on LXC based Platforms
# Currently equivalent to 'is app hosting enabled?'
# This could be overridden by PD Function
function do_port_forward {
    declare -F app_host_is_enabled &>/dev/null && app_host_is_enabled
}

#
# Add a field to the cmdline starting with __ only if it is not present already
#
function cmdline_add_uniq()
{
    local cmdline=$1
    local toadd=$2
    local target=$3

    echo $cmdline | grep -q "\<$toadd\>" -
    if [ $? -ne 0 ]; then
        sed -i "s;\(platform=\);$toadd \1;g" $target
    fi
}

#
# Give a chance to debug on error conditions
#
function drop_to_console_for_debug()
{
    local debug=
    read -t 5 -p "Enter 'y' within 5 seconds to drop to bash for debug: " debug
    if [[ "$debug" = "y" ]]; then
        echo "Entering bash shell:"
        /bin/bash
    else
        echo "Continuing"
    fi
}

#
# Run a critical command and complain if it fails, dropping to bash for
# debugging
#
function run_and_check()
{
    local cmd="$*"
    local last_cmd_log=/tmp/log.cmd.$$
    local PREFIX="`date -u`: -- "

    declare -F platform_log &>/dev/null && platform_log "RUN: $cmd"

    ($cmd 2>&1) | sed "s/^/${PREFIX}/g" >$last_cmd_log

    local RET=${PIPESTATUS[0]}
    if [[ $RET -ne 0 ]]; then
        step_log_console "ERROR: \"$cmd\" failed:"

        cat $last_cmd_log

        drop_to_console_for_debug
    fi

    cat $last_cmd_log >> /tmp/log
    /bin/rm -f $last_cmd_log

    return $RET
}

#
# For several system executables (virsh, service) we need to wrap some
# XR-specific behavior around them. This wrapper is packaged as 'foo.alias'
# to avoid inadvertently overwriting the system executable.
#
# fixup_alias() does the following for one such wrapper:
# 1) Move /path/to/foo       to /path/to/foo.real
# 2) Copy /path/to/foo to    /path/to/foo.alias
#    NOTE: we do a copy rather than moving foo.alias to foo to avoid
#    issues with the 'install verify' command failing due to a 'missing'
#    file, as seen in CSCvb27488.
#
# We need to handle the following cases:
#
# Case                                  x.alias       x           x.real
# -----------------------------------------------------------------------
# First boot                  (before)  alias         real        none
#                             (after)   alias         alias       real
#
# Reboot                      (before)  alias         alias       real
#                             (after)   alias         alias       real
#
# Boot after x update         (before)  alias         new real    old real
#                             (after)   alias         alias       new real
#
# Boot after x.alias update   (before)  new alias     old alias   real
#                             (after)   new alias     new alias   real
#
# Boot after update of both   (before)  new alias     new real    old real
#                             (after)   new alias     new alias   new real
#
# Note that x.real should never change spontaneously - only x.alias and/or x.
#
function fixup_alias()
{
    local prog=$1
    if [[ ! -f ${prog}.alias ]]; then
        return
    fi

    local alias_change="yes"
    local prog_change="yes"

    if [[ -f ${prog}.alias.md5 ]]; then
        /usr/bin/md5sum --check ${prog}.alias.md5 --status && alias_change="no"
    fi
    if [[ -f ${prog}.md5 ]]; then
        /usr/bin/md5sum --check ${prog}.md5 --status && prog_change="no"
    fi

    if [[ $prog_change == "yes" ]]; then
        $PLATFORM_LOG_EXEC mv -f "$prog" "${prog}.real"
    fi

    if [[ $prog_change == "yes" || $alias_change == "yes" ]]; then
        $PLATFORM_LOG_EXEC cp -f "${prog}.alias" "$prog"

        /usr/bin/md5sum ${prog}.alias > ${prog}.alias.md5
        /usr/bin/md5sum ${prog} > ${prog}.md5
    fi
}
