#!/bin/bash
#
# Iterates over the list of network devices on the node and prompts the user
# to configure each.

. /usr/libexec/ovirt-functions

ME=$(basename "$0")
warn() { printf '%s: %s\n' "$ME" "$*" >&2; }
die() { warn "$*"; exit 1; }

WORKDIR=$(mktemp -d) || exit 1

# Remove $WORKDIR upon interrupt (and HUP, PIPE, TERM) and upon normal
# termination, being careful not to change the exit status.
trap '__st=$?; rm -rf "$WORKDIR"; stop_log; exit $__st' 0
trap 'exit $?' 1 2 13 15

IFCONFIG_FILE_ROOT="/files/etc/sysconfig/network-scripts/ifcfg"
NTPCONF_FILE_ROOT="/files/etc/ntp"
NTP_CONFIG_FILE="/etc/ntp.conf"
NTPSERVERS=""
CONFIGURED_NIC=""
CONFIGURED_NICS=""
VLAN_ID=""
VL_ROOT=""

if [[ -z "$ROOTDRIVE" && "$OVIRT_ISCSI_ENABLED" != "y" ]]; then
# if local storage is not configured, then exit the script
    if [[ ! is_local_storage_configured && "$OVIRT_ISCSI_NETWORKING" != "y" ]] ; then
        printf "Local storage must be configured prior to configuring the management interface.\n\n"
        exit 99
    fi
fi

# $1 - the variable name to set
# $2 - the input prompt
function input_ipv4_address {
    local varname=$1
    local prompt=$2

    eval $varname=\"\"

    while true; do
        read -ep "${prompt}: "

        if [ -z "$REPLY" ]; then return; fi

        if is_valid_ipv4 $REPLY; then
            eval $varname=\"$REPLY\"
            return
        else
            printf "\nThe address $REPLY is not a valid IPv4 address.\n"
        fi
    done
}

# Checks that a network interface was already configured.
function has_configured_interface
{
    local show_message=${1-false}

    if [[ -n "${CONFIGURED_NIC}" ]]; then
        return 0
    else
        if $show_message; then
            printf "\nYou must configure a network interface first.\n\n"
        fi
        return 1
    fi
}

# Configures vlan for the node.
# $1 - the nic
# $2 - the network bridge name
# $3 - the vlan id
# $4 - the VL_ROOT variable
# $5 - the VL_CONFIG variable
# $6 - the IF_ROOT value
# $7 - the vlan config filename variable
# $8 - the NIC config filename
function setup_vlan
{
    local nic=$1
    local bridge=$2
    local vlan_id=$3
    local vlroot=$4
    local vlconfig=$5
    local ifroot=$6
    local vlfilename=$7
    local iffilename=$8

    eval $vlroot="${ifroot}.${vlan_id}"
    eval $vlconfig=\"rm \$${vlroot}\\nset \$${vlroot}/DEVICE ${nic}.${vlan_id}\"
    eval $vlconfig=\"\$${vlconfig}\\nset \$${vlroot}/BRIDGE ${bridge}\"
    eval $vlconfig=\"\$${vlconfig}\\nset \$${vlroot}/VLAN yes\"
    eval $vlfilename="${iffilename}.${vlan_id}"
}

function configure_interface
{
    local NIC=$1
    local AUTO=$2
    if [[   "$AUTO" == "AUTO"    &&
         -n "$OVIRT_IP_ADDRESS" ]]; then
        IPADDR=$OVIRT_IP_ADDRESS
        NETMASK=$OVIRT_IP_NETMASK
        GATEWAY=$OVIRT_IP_GATEWAY
        PREFIX=$OVIRT_IP_PREFIX
    fi

    if has_configured_interface; then
        printf "This will delete the current configuration for ${CONFIGURED_NIC}.\n"
        if ask_yes_or_no "Do you wish to continue ([Y]es/[N]o)?"; then
            printf "\nDeleting existing network configuration...\n"
            cp -a  /etc/sysconfig/network-scripts/ifcfg-lo /etc/sysconfig/network-scripts/backup.lo
            remove_config /etc/sysconfig/network-scripts/ifcfg-*
            rm -rf /etc/sysconfig/network-scripts/ifcfg-*
            cp -a  /etc/sysconfig/network-scripts/backup.lo /etc/sysconfig/network-scripts/ifcfg-lo
        else
            printf "\nAborting...\n"
            return
        fi
    fi

    rm -rf $WORKDIR/*
    CONFIGURED_NIC=$NIC

    local BRIDGE=br$NIC
    local IF_FILENAME="$WORKDIR/augtool-$NIC"
    local BR_FILENAME="$WORKDIR/augtool-$BRIDGE"

    printf "\nConfigure $BRIDGE for use by $NIC..\n\n"

    local IF_ROOT="$IFCONFIG_FILE_ROOT-$NIC"
    local IF_CONFIG="rm $IF_ROOT\nset $IF_ROOT/DEVICE $NIC"

    local BR_ROOT="$IFCONFIG_FILE_ROOT-$BRIDGE"
    local BR_CONFIG="rm $BR_ROOT\nset $BR_ROOT/DEVICE $BRIDGE"

    BR_CONFIG="$BR_CONFIG\nset $BR_ROOT/TYPE Bridge"
    BR_CONFIG="$BR_CONFIG\nset $BR_ROOT/PEERNTP yes"
    BR_CONFIG="$BR_CONFIG\nset $BR_ROOT/DELAY 0"
    local BR_CONFIG_BASE=$BR_CONFIG

    if [ -z "$AUTO" ]; then
        while true; do
            local VL_CONFIG=""

            printf "\n"
            LINK=`ethtool $NIC| grep "Link detected"`:u
            [ -z "$LINK" ] && return
            if echo $LINK | grep -q "Link detected: yes" ; then
                NICSTATUS="ACTIVE"
            else
                NICSTATUS="INACTIVE"
            fi
            echo "NIC is: $NICSTATUS"

            if ask_yes_or_no "Help identify ${NIC} by blinking lights for 10 seconds ([Y]es/[N]o)?"; then
                ethtool --identify $NIC 10
            fi

            ask_yes_or_no "Include VLAN support ([Y]es/[N]o/[A]bort)? " true true
            case $? in
                0)
                    while true; do
                        read -ep "What is the VLAN ID for this device (a=abort) "
                        case $REPLY in
                            A|a) CONFIGURED_NIC=""; return;;
                            *)
                                if [[ -n "$REPLY" ]] && [[ "$REPLY" =~ ^[0-9]{1,}$ ]]; then
				    VLAN_ID=$REPLY
				    setup_vlan $NIC $BRIDGE $VLAN_ID VL_ROOT VL_CONFIG $IF_ROOT VL_FILENAME $IF_FILENAME
                                    break
                                fi
                                ;;
                        esac
                    done
                    ;;
                1)  IF_CONFIG="${IF_CONFIG}\nset ${IF_ROOT}/BRIDGE ${BRIDGE}" ;;
                2)
                    CONFIGURED_NIC=""
                    VLAN_ID=""
                    return;;
            esac

            while true; do
                read -ep "Enable IPv4 support ([S]tatic IP, [D]HCP, [N]o or [A]bort)? "
                case $REPLY in
                    D|d)
                        BR_CONFIG="$BR_CONFIG\nset $BR_ROOT/BOOTPROTO dhcp"
                        break
                        ;;
                    S|s)
                        printf "\n"
                        input_ipv4_address IPADDR  "IP Address"
                        input_ipv4_address NETMASK "   Netmask"
                        input_ipv4_address GATEWAY "   Gateway"

                        BR_CONFIG="$BR_CONFIG\nset $BR_ROOT/BOOTPROTO none"
                        BR_CONFIG="$BR_CONFIG\nset $BR_ROOT/IPADDR $IPADDR"
                        BR_CONFIG="$BR_CONFIG\nset $BR_ROOT/NETMASK $NETMASK"
                        if [ -n "${GATEWAY}" ]; then
                            BR_CONFIG="$BR_CONFIG\nset $BR_ROOT/GATEWAY $GATEWAY"
                        fi
                        break
                        ;;
                    A|a)
                        CONFIGURED_NIC=""
                        VLAN_ID=""
                        return
                        ;;
                    N|n)
                        break
                        ;;

                esac
            done

            printf "\n"

            while true; do
                read -ep "Enable IPv6 support ([S]tatic, [D]HCPv6, A[u]to, [N]o or [A]bort)? "
                case $REPLY in
                    S|s)
                        read -ep "IPv6 Address: "; IPADDR=$REPLY
                        BR_CONFIG="$BR_CONFIG\nset $BR_ROOT/IPV6INIT yes"
                        BR_CONFIG="$BR_CONFIG\nset $BR_ROOT/IPV6ADDR $IPADDR"
                        break
                        ;;
                    D|d)
                        BR_CONFIG="$BR_CONFIG\nset $BR_ROOT/IPV6INIT yes"
                        BR_CONFIG="$BR_CONFIG\nset $BR_ROOT/IPV6AUTCONF no"
                        BR_CONFIG="$BR_CONFIG\nset $BR_ROOT/IPV6FORWARDING no"
                        BR_CONFIG="$BR_CONFIG\nset $BR_ROOT/DHCPV6C yes"
                        break
                        ;;
                    U|u)
                        BR_CONFIG="$BR_CONFIG\nset $BR_ROOT/IPV6INIT yes"
                        BR_CONFIG="$BR_CONFIG\nset $BR_ROOT/IPV6FORWARDING no"
                        BR_CONFIG="$BR_CONFIG\nset $BR_ROOT/IPV6AUTOCONF yes"
                        break
                        ;;
                    A|a)
                        CONFIGURED_NIC=""
                        VLAN_ID=""
                        return
                        ;;
                    N|n)
                        break
                        ;;

                esac
            done

            printf "\n"
            ask_yes_or_no "Is this correct ([Y]es/[N]o/[A]bort)?" true true
            case $? in
                0)
                    IF_CONFIG="$IF_CONFIG\nset $IF_ROOT/ONBOOT yes"
                    BR_CONFIG="$BR_CONFIG\nset $BR_ROOT/ONBOOT yes"
                    if [[ -n "$VLAN_ID" ]]; then
                        VL_CONFIG="$VL_CONFIG\nset $VL_ROOT/ONBOOT yes"
                    fi
                    printf "$IF_CONFIG\n" > $IF_FILENAME
                    printf "$BR_CONFIG\n" > $BR_FILENAME
                    if [[ -n "$VLAN_ID" ]]; then
                        printf "$VL_CONFIG\n" > $VL_FILENAME
                    fi
                    break
                    ;;
                1)
                    BR_CONFIG=$BR_CONFIG_BASE
                    ;;
                2)
                    CONFIGURED_NIC=""
                    VLAN_ID=""
                    return
                    ;;
            esac
        done
    else
        if [ -n "$OVIRT_IPV6" ]; then
            case "$OVIRT_IPV6" in
                "auto")
                    BR_CONFIG="$BR_CONFIG\nset $BR_ROOT/IPV6INIT yes"
                    BR_CONFIG="$BR_CONFIG\nset $BR_ROOT/IPV6FORWARDING no"
                    BR_CONFIG="$BR_CONFIG\nset $BR_ROOT/IPV6_AUTOCONF yes"
                    ;;
                "dhcp")
                    BR_CONFIG="$BR_CONFIG\nset $BR_ROOT/IPV6INIT yes"
                    BR_CONFIG="$BR_CONFIG\nset $BR_ROOT/IPV6_AUTOCONF no"
                    BR_CONFIG="$BR_CONFIG\nset $BR_ROOT/IPV6FORWARDING no"
                    BR_CONFIG="$BR_CONFIG\nset $BR_ROOT/DHCPV6C yes"
                    ;;
                *)
                    BR_CONFIG="$BR_CONFIG\nset $BR_ROOT/IPV6INIT yes"
                    BR_CONFIG="$BR_CONFIG\nset $BR_ROOT/IPV6ADDR $OVIRT_IPV6"
                    BR_CONFIG="$BR_CONFIG\nset $BR_ROOT/IPV6_AUTOCONF no"
                    BR_CONFIG="$BR_CONFIG\nset $BR_ROOT/IPV6FORWARDING no"
            esac
        fi

        if [ -n "$OVIRT_VLAN" ]; then
	    VLAN_ID=$OVIRT_VLAN
	    setup_vlan $NIC $BRIDGE $VLAN_ID VL_ROOT VL_CONFIG $IF_ROOT VL_FILENAME $IF_FILENAME
        fi

        if [ -z "$OVIRT_IP_ADDRESS" ]; then
	    if [ -z "$VL_CONFIG" ]; then
		IF_CONFIG="${IF_CONFIG}\nset ${IF_ROOT}/BRIDGE ${BRIDGE}"
	    fi
	    BR_CONFIG="$BR_CONFIG\nset $BR_ROOT/BOOTPROTO dhcp"
        else
            if [ "$OVIRT_IP_ADDRESS" != "off" ]; then
		if [ -z "$VL_CONFIG" ]; then
                    IF_CONFIG="${IF_CONFIG}\nset ${IF_ROOT}/BRIDGE ${BRIDGE}"
		fi
                BR_CONFIG="${BR_CONFIG}\nset ${BR_ROOT}/BOOTPROTO static"
                BR_CONFIG="$BR_CONFIG\nset $BR_ROOT/IPADDR $OVIRT_IP_ADDRESS"
                if [ -n "$OVIRT_IP_NETMASK" ]; then
                    BR_CONFIG="$BR_CONFIG\nset $BR_ROOT/NETMASK $OVIRT_IP_NETMASK"
                fi
                if [ -n "$OVIRT_IP_GATEWAY" ]; then
                    BR_CONFIG="$BR_CONFIG\nset $BR_ROOT/GATEWAY $OVIRT_IP_GATEWAY"
                fi
            fi
        fi

        IF_CONFIG="$IF_CONFIG\nset $IF_ROOT/ONBOOT yes"
        BR_CONFIG="$BR_CONFIG\nset $BR_ROOT/ONBOOT yes"
	if [ -n "${VL_CONFIG}" ]; then
	    VL_CONFIG="$VL_CONFIG\nset $VL_ROOT/ONBOOT yes"
	fi

        printf "$IF_CONFIG\n" > $IF_FILENAME
        printf "$BR_CONFIG\n" > $BR_FILENAME
	if [ -n "$VL_CONFIG" ]; then
	    printf "$VL_CONFIG\n" > $VL_FILENAME
	fi
    fi
}

function configure_dns
{
    local DNS=$1
    local AUTO=$2
    if [[   "$AUTO" == "AUTO"    &&
         -n "$OVIRT_DNS" ]]; then
        DNS=$OVIRT_DNS
    fi

    local IF_FILENAME="$WORKDIR/augtool-br${CONFIGURED_NIC}"
    local IF_ROOT="$IFCONFIG_FILE_ROOT-br${CONFIGURED_NIC}"
    local IF_CONFIG=

    if [ -z "$AUTO" ]; then
        if has_configured_interface true; then
            while true; do
                for dns in first second; do
                    while true; do
                        printf "\n"
                        read -erp "Please enter the ${dns} DNS server (or ENTER to exit): "
                        local ADDRESS=$(trim_whitespace $REPLY)
                        if [[ -z "${ADDRESS}" ]]; then
                            if [[ -z "${DNS}" ]]; then
                                printf "\nAborted...\n"
                                return
                            else
                                break
                            fi
                        fi
                        if is_valid_ipv4 $ADDRESS; then
                            if [[ -z "${DNS}" ]]; then
                                DNS="${ADDRESS}"
                                break
                            elif [[ -n "${ADDRESS}" ]]; then
                                if [[ ! $DNS =~ "${ADDRESS}" ]]; then
                                    DNS="${DNS}:${ADDRESS}"
                                    break
                                else
                                    printf "${ADDRESS} is already defined as a DNS server.\n"
                                fi
                            fi
                        else
                            printf "${ADDRESS} is an invalid address.\n"
                        fi
                    done
                done

                printf "\n"
                ask_yes_or_no "Is this correct ([Y]es/[N]o/[A]bort)?" true true
                case $? in
                    0) break ;;
                    1) DNS="";;
                    2) return ;;
                esac
            done
        fi
    fi

    if [ -n "$DNS" ]; then
        DNS1=$(echo "$DNS" | awk -F\: '{print $1}')
        DNS2=$(echo "$DNS" | awk -F\: '{print $2}')

        test -n "$DNS1" && printf "set $IF_ROOT/DNS1 $DNS1\n" >> $IF_FILENAME
        test -n "$DNS2" && printf "set $IF_ROOT/DNS2 $DNS2\n" >> $IF_FILENAME
    fi
}

function configure_ntp
{
    local AUTO=$2
    if [[ "$AUTO" == "AUTO" && -n "$OVIRT_NTP" ]]; then
        NTPSERVERS=$OVIRT_NTP
    else
        NTPSERVERS=""
    fi

    if [ -z "$AUTO" ]; then
        if has_configured_interface true; then
            while true; do
                read -rp "Enter an NTP server (hit return when finished): "
                local ADDRESS=$(trim_whitespace $REPLY)

                if [ -z "${ADDRESS}" ]; then break; fi
                if is_valid_ipv4 $ADDRESS; then
                    if [[ ! $NTPSERVERS =~ $ADDRESS ]]; then
                        NTPSERVERS="${NTPSERVERS}:${ADDRESS}"
                    else
                        printf "${ADDRESS} is already defined as an NTP server.\n"
                    fi
                else
                    printf "${ADDRESS} is an invalid address.\n"
                fi
            done
        fi
    fi
}

function save_ntp_configuration
{
    local ntpconf="$WORKDIR/augtool-ntp"
    local ntproot="/files/etc/ntp.conf"

    printf "\
rm ${ntproot}\n\
set ${ntproot}/driftfile /var/lib/ntp/drift\n\
set ${ntproot}/includefile /etc/ntp/crypto/pw\n\
set ${ntproot}/keys /etc/ntp/keys\n\
" > $ntpconf

    if [ -n "$NTPSERVERS" ]; then
        offset=1
        SERVERS=$(echo $NTPSERVERS | awk 'BEGIN{FS=":"}{for (i=1; i<=NF; i++) print $i}')
        for server in $SERVERS; do
            printf "set /files/etc/ntp.conf/server[${offset}] ${server}\n" >> $ntpconf
            offset=$(echo "$offset+1" | bc)
        done
    fi
}

function setup_menu
{
    NICS=""
    nics=$(ls -bd /sys/class/net/*)
    for nic in $nics; do
        nic=$(basename $nic)
        address=$(systool -c net -d $nic -A address | awk '/address\ += "(.*)"/ { print $3; }')
        if [[ ! "$address" =~ '00:00:00:00' ]]; then
            NICS="$NICS $nic"
        fi
    done

    # Add virtio NICs that were possibly not detected by other means
    NICS="$(ifconfig -a | awk '/Ethernet/ {print $1}'|xargs)"
    NICS=$(echo $NICS | tr ' ' '\n' | sort -u | xargs)

    PS3="Please select an interface or configuration option: "
}

if [ "$1" == "AUTO" ]; then
    if [ -n "$OVIRT_BOOTIF" ]; then
	configure_interface "$OVIRT_BOOTIF" AUTO
	configure_dns "$OVIRT_DNS" AUTO
	configure_ntp "$OVIRT_NTP" AUTO
    else
	printf "No network interface specified. Unable to configure networking."
	exit 0
    fi
else
    setup_menu

    while true; do
        printf "\n\nManagement Network Configuration\n\n" >&2
        printf "***** WARNING *****\n"
        printf "Configuring the network will destroy any existing networking\n"
        printf "configuration on this system.\n"
        printf "***** WARNING *****\n"
        printf "\nPhysical Networking Devices (*=PXE boot interface)\n"
        printf " %-10s %-12s %-18s\n" "Name" "Driver" "MAC"
        PHY_NICS=""
        for nic in $NICS; do
            driver=$(basename $(readlink /sys/class/net/$nic/device/driver) 2>/dev/null)
            mac=$(cat /sys/class/net/$nic/address)
            if [ "$nic" = "$OVIRT_BOOTIF" ]; then
                pxe="*"
            else
                pxe=" "
            fi
            if [ -n "$driver" ]; then
                PHY_NICS="$PHY_NICS $nic"
                printf "%s%-10s %-12s %-18s\n" "$pxe" "$nic" "$driver" "$mac"
            fi
        done

        DNS="DNS"
        NTP="NTP"
        Abort="Abort"
        Save="Save And Return To Menu"
        select NIC in $PHY_NICS "$DNS" "$NTP" "$Abort" "$Save"
        do
            printf "\n"
            case "$NIC" in
                "$DNS") configure_dns "$OVIRT_DNS"; break ;;
                "$NTP") configure_ntp "$OVIRT_NTP"; break ;;
                "$Abort") rm -f "${WORKDIR}"/augtool-*; exit 99;;
                "$Save")
                    save_ntp_configuration
                    break 2;;
                *)
                    if [[ -n "${NIC}" ]] && [[ "${NICS}" =~ "${NIC}" ]]; then
                        configure_interface $NIC $IFACE_NUMBER
                    else
                        printf "\nInvalid option selected.\n"
                    fi
                    break
                    ;;
            esac
        done
    done
fi

start_log
net_configured=0
if ls "$WORKDIR"/augtool-* > /dev/null 2>&1 ; then
    log "Configuring network"

    # delete existing scripts
    for vlan in /proc/net/vlan/*; do
        if [[ ! "${vlan}" =~ config ]]; then
            vlan=$(echo $vlan|awk -F "/" {'print $5'})
            vconfig rem $vlan
        fi
    done

    for script in /etc/sysconfig/network-scripts/ifcfg-*; do
        if [[ ! "${script}" =~ ifcfg-lo ]]; then
            ovirt_safe_delete_config $script
        fi
    done

    config="$WORKDIR"/config-augtool
    cat "$WORKDIR"/augtool-* > $config
    echo "set /files/etc/sysconfig/network/NETWORKING yes" >> $config
    CONFIGURED_NICS="$CONFIGURED_NIC br$CONFIGURED_NIC"
    if [ -n "${VLAN_ID}" ]; then
        CONFIGURED_NICS="$CONFIGURED_NICS $CONFIGURED_NIC.$VLAN_ID"
    fi
    # preserve current MAC mappings for *all physical* network interfaces
    # to avoid dev$RANDOM rhbz#489927
    for nic in $(ls -d /sys/class/net/*/device|cut -d/ -f5) ; do
        mac=$(cat /sys/class/net/$nic/address)
        if [ -n "$nic" -a -n "$mac" ]; then
            if_root="$IFCONFIG_FILE_ROOT-$nic"
            printf "\nset $if_root/DEVICE $nic" >> $config
            # store the hwaddr field in both, physical and vlan ifcfg if vlan is used
            if [ -n "${VLAN_ID}" -a "$nic" = "${CONFIGURED_NIC}" ]; then
                printf "\nset $if_root/HWADDR $mac" >> $config
                printf "\n" >> $config
                if_root="${VL_ROOT}"
            fi
            if [ ${CONFIGURED_NIC} != $nic ]; then
                printf "\nset $if_root/ONBOOT no" >> $config
                CONFIGURED_NICS="$CONFIGURED_NICS $nic"
            fi
            printf "\nset $if_root/HWADDR $mac" >> $config
            printf "\n" >> $config
        fi
    done
    cp $config /tmp/augconfig
    augtool $config
    if [ $? = 0 ]; then
        log "Network configured successfully"
        net_configured=1
        for nic in $CONFIGURED_NICS; do
            ovirt_store_config /etc/sysconfig/network-scripts/ifcfg-$nic
        done
        ovirt_store_config $NTP_CONFIG_FILE
    else
        log "Error configuring network, see $OVIRT_LOGFILE"
        stop_log
        exit 1
    fi
fi
stop_log

if [ "$net_configured" = 1 ]; then
    service network stop > /dev/null 2>&1
    # XXX eth assumed in breth
    for i in `brctl show | grep breth | awk '{print $1}'`
    do
      ifconfig $i down
      brctl delbr $i
    done
    service network start 2> /dev/null
    if [ $NTPSERVERS ]; then
        log "Testing NTP Configuration"
        test_ntp_configuration
    fi
fi
