# Default behavior if install succeed
ONSUCCESS="halt"
ONFAILURE="console"
IP="all:dhcp"

set +e
set -x

exec 3>&1
exec 4>&2

exec >> /log 2>&1

tail -f /log > /dev/console &
tpid=$!

eval $(cat /proc/cmdline | tr ' ' "\n" | egrep "^ONSUCCESS=")
eval $(cat /proc/cmdline | tr ' ' "\n" | egrep "^ONFAILURE=")
eval $(cat /proc/cmdline | tr ' ' "\n" | egrep "^IP=")
eval $(cat /proc/cmdline | tr ' ' "\n" | egrep "^discoverd_callback_url=")
eval $(cat /proc/cmdline | tr ' ' "\n" | egrep "^BOOTIF=")
eval $(cat /proc/cmdline | tr ' ' "\n" | egrep "^RUNBENCH=")

do_reboot() {
    umount -a
    reboot -f
}

do_halt() {
    echo "Node is now discovered! Halting..."
    # Give user a chance of seeing the output
    sleep 5
    poweroff -f
}

do_console() {
    troubleshoot
}

step() {
    echo "################################################################"
    echo "$@"
    echo "################################################################"
}

log() {
    echo "$@"
}

log_n() {
    echo -n "$@"
}

give_up() {
    log "$@"

    case "$ONFAILURE" in
      "halt")
        log "Automatic poweroff as required by ONFAILURE"
        do_halt
        ;;
      "console")
        log "ONFAILURE=console, launching an interactive shell"
        do_console
        ;;
      *)
        log "Unsupported ONFAILURE=$ONFAILURE value"
        do_console
        ;;
    esac
}

get_pci_modules() {
    for d in $(cut -f2 /proc/bus/pci/devices ); do
        echo $d| sed 's/\(....\)/\1 /'|while read vendor device; do
            egrep -i "pci:v0000${vendor}d(\*|0000${device})" /lib/modules/$(uname -r)/modules.alias|while read a n module; do
                echo $module
            done
        done
    done
}

get_virtio_modules() {
    for id in $(cut -f2 -d: /sys/bus/virtio/devices/*/modalias | sed 's/v.*/v/' ); do
        egrep -i "virtio:${id}\*" /lib/modules/$(uname -r)/modules.alias|while read a n module; do
            echo $module
        done
     done
}

probe_virtio_devices() {
    for loop in $(seq 10); do
        if [ -d /sys/bus/virtio/devices ]; then
            for module in $(get_virtio_modules|sort -u); do
                log_n "Loading $module "
                modprobe $module && log "done" || log "error"
            done
            break
        fi
    done
}

probe_pci_devices() {
    step "Probing PCI devices"
    BLACK_LIST="snd_hda_intel mgag200"
    for module in $(get_pci_modules|sort -u); do
        echo "$BLACK_LIST" | grep -qw "$module" && log "Skipping $module" && continue
        log_n "Loading $module "
        modprobe $module && log "done" || log "error"
        if [ $module = virtio_pci ]; then
            probe_virtio_devices
        fi
    done

    # Some kernel drivers doesn't have hardware dependencies but are required to make the device work
    # Let's probe them before starting the network stuff
    step "Probing Additional modules"
    # Show all Mellanox cards (15b3 is hexa vendor ID)
    if [ "$(lspci -d 15b3: -n|awk '{print $2}'|grep -q '0280';echo $?)" -eq 0 ]; then
      additional_modules='mlx4_en ib_sa ib_cm ib_umad ib_addr ib_uverbs ib_ipoib ib_ipath mlx4_ib'
      for module in $additional_modules; do
        log_n "Loading additional module $module "
        modprobe $module && log "done" || log "error"
      done
    fi

    # Add weird sleep to get all drivers probed
    sleep 5
}


probe_kernel_modules() {
    step "Probing complementary kernel modules"
    # Some kernel modules could be needed to perfom some operations

    # Some linux distribution have dm-mod in module, some other in static.
    # Let's try to probe it for the 1st case
    modprobe "dm-mod" || true
}

enable_network_links() {
    nolink=$1
    retry_count=$2
    DEVICE_LIST=
    for current_pass in `seq 1 $retry_count`; do
        log "Enabling Ethernet Links ($current_pass/$retry_count)"
        pushd /sys/class/net >/dev/null
        for device in *; do
            if [ "$device" = "lo" ]; then
                continue
            fi
            ip link set dev $device up
            DEVICE_LIST="$DEVICE_LIST $device"
        done
        popd > /dev/null

        if [ -n "$DEVICE_LIST" ]; then
            log "Ethernet devices detection completed"
            break;
        else
            log "No Ethernet devices found"
            if [ "$current_pass" != "$retry_count" ]; then
                log "Waiting a little bit before retrying"
                sleep 2
            fi
        fi
    done

    # If we have only match "lo" it means no physical interface got detected !
    if [ -z "$DEVICE_LIST" ]; then
        show_kernel_modules
        show_network_cards
        if [ "$nolink" = "fatal" ]; then
            give_up "No Network interface found !"
        fi
    fi

    # Only consider interface that have a Link ok
    log "Waiting a few seconds to catch network link"
    sleep 10

    log "List of available network devices is :$DEVICE_LIST"
}

get_network_configuration() {
    searched_iface=$1
    OLD_IFS=$IFS
    IFS=","
    other="dhcp"
    for entry in $IP; do
        iface_name=$(echo $entry | cut -d ":" -f 1)
        iface_config=$(echo $entry | cut -d ":" -f 2)
        if [ "$iface_name" = "$searched_iface" ] || [ "$iface_name" = "all" ]; then
            echo -n $iface_config
            return
        fi
        if [ "$iface_name" = "other" ]; then
            other=$iface_config
        fi
    done
    IFS=$OLD_IFS
    # If no configuration got found, we consider this is dhcp
    echo -n $other
}


probe_network_devices() {
# Let's wait 30 seconds to get a DHCP answer
# Default is very very long....
DHCP_TIMEOUT=30
DHCP_GRACE_TIME=10
DHCP_NO_RACETIMING=1
echo "timeout $DHCP_TIMEOUT;" >> /etc/dhclient.conf

mkdir -p /var/lib/lldpad
lldpad -d
MAX_RUN=6
RUN=0
while true; do

    enable_network_links fatal 3

    CARRIER_DEVICE_LIST=
    DHCP_IFACES_COUNT=0
    PIDS=
    pushd /sys/class/net >/dev/null
    for iface in *; do
        if [ "$iface" = "lo" ]; then
            continue
        fi

        # Let's check if the network interface reports some carrier
        # If so, let's try to get a DHCP answer from here
        if [ "$(cat /sys/class/net/$iface/carrier)" = "1" ]; then
            CARRIER_DEVICE_LIST="$CARRIER_DEVICE_LIST $iface"
            log "Enabling LLDP rx on $iface"
            lldptool set-lldp -i $iface adminstatus=rxtx &>/dev/null
            # Let's run all the dhclients in parallel
            config=$(get_network_configuration $iface)
            case "$config" in
                "dhcp")
                    DHCP_IFACES_COUNT=$(($DHCP_IFACES_COUNT + 1))
                    ( log "Waiting for $iface to get a DHCP answer."
                        if [ -r /var/run/dhclient-$iface.pid ]; then
                            kill $(cat /var/run/dhclient-$iface.pid)
                        fi
                        dhclient -d -pf /var/run/dhclient-$iface.pid $iface >> /$iface.log 2>&1 &
                        count=$DHCP_GRACE_TIME
                        while [ $count -gt 0 ] && ! ifconfig $iface|grep -q 'inet addr'; do
                            sleep 1
                            count=$(($count - 1))
                        done
                    ) &
                    PIDS="$PIDS $!"
                    sleep $DHCP_NO_RACETIMING # Don't race dhcp clients too much
                ;;
                "none")
                    log "Ignoring network interface $iface"
                ;;
                *)
                    log "Setting up $iface with $config"
                    ip addr add $config dev $iface
                ;;
            esac
        else
            log "Rejecting Interface $iface : carrier not detected"
        fi
    done
    popd >/dev/null

    log "Valid interfaces with Carrier were $CARRIER_DEVICE_LIST"

    if [ $DHCP_IFACES_COUNT -eq 0 ]; then
        # We don't have any DHCP interface to wait, let's exit
        break;
    else
        # We now have to let enough time to let all the racing dhcpclient finishing
        DHCP_WAITING_TIME=$((DHCP_TIMEOUT + $DHCP_GRACE_TIME + $DHCP_IFACES_COUNT*$DHCP_NO_RACETIMING + 3))
        log "Waiting for $DHCP_IFACES_COUNT DHCP anwsers to come up in $DHCP_WAITING_TIME sec"

        while [ -n "$PIDS" ]; do
        NEW_PIDS=
        for p in $(echo $PIDS); do
            if [ -d /proc/$p ]; then
                NEW_PIDS="$NEW_PIDS $p"
            fi
        done
        DHCP_WAITING_TIME=$(($DHCP_WAITING_TIME - 1))
        if [ -n "$NEW_PIDS" -a $DHCP_WAITING_TIME -gt 0 ]; then
            PIDS="$NEW_PIDS"
            sleep 1
        else
            PIDS=
        fi
        done

        IP_SET=$(ip -4 a  | grep -iw "inet" | grep -v "127.0.0.1" | wc -l)
        if [ "$IP_SET" -gt 0 ]; then
            log "Found $IP_SET interfaces properly configured"
            # We found at least once DHCP server so we can continue
            # the install procedure
            break
        fi

        RUN=$(( $RUN + 1 ))
        if [ "$RUN" != "$MAX_RUN" ]; then
            log "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
            log "!! NO DHCP FOUND ! Waiting 10 seconds before trying again. !!"
            log "!! ($RUN / $MAX_RUN)                                       !!"
            log "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
	    sleep 10
        else
            log "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
            log "!! NO DHCP FOUND after $RUN tries.                         !!"
            log "!! last chance, let dhclient handle the discovery          !!"
            log "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
	    killall dhclient
	    dhclient
	    break
        fi
    fi
done

# Let's wait up to 45 seconds to get all links getting their lldp status
WAIT_LLDP_TIME=5
NB_LLDP_LOOPS=9

if [ -n "$CARRIER_DEVICE_LIST" ]; then
    for loop in `seq 1 $NB_LLDP_LOOPS`; do
        NB_DEVICES=$(echo "$CARRIER_DEVICE_LIST" | wc -w)
        log "Searching for LLDP information on $NB_DEVICES interfaces (loop $loop / $NB_LLDP_LOOPS)"
        for iface in $CARRIER_DEVICE_LIST; do
            NB_PKTS=$(lldptool -S -i $iface | grep "Total Frames Received" | awk '{print $5}')
            if [ "$NB_PKTS" != "0" ]; then
                NB_DEVICES=$(( $NB_DEVICES - 1 ))
            fi
        done
        # If all links had one LLDP packet at least, let's exit earlier
        if [ $NB_DEVICES -eq 0 ]; then
            log "Received at least one LLDP packet per valid interface"
            log "End of LLDP discovery process"
            return;
        fi
        sleep $WAIT_LLDP_TIME
    done
fi
}

probe_kernel_modules

step "Starting services"
if [ -x /etc/init.d/sysklogd ]; then
    /etc/init.d/sysklogd start
fi

if [ -x /etc/init.d/klogd ]; then
    /etc/init.d/klogd start
fi

# It's all over netlink now
echo "" > /proc/sys/kernel/hotplug

mcelog --ignorenodev --filter --daemon --logfile /mcelog

# On RHEL7, we need lvmetad to run to make {lv}* commands working
if [ -x /sbin/lvmetad ]; then
    lvmetad
fi

################################################################################
# Hardware detection starts here
################################################################################
probe_pci_devices
probe_network_devices

ip a

if [ "$RUNBENCH" = "1" ]; then
    BENCH_ARG="--benchmark"
fi

step "Running discovery"

ironic-discoverd-ramdisk --use-hardware-detect $BENCH_ARG --bootif "$BOOTIF" \
    -L /run/initramfs/rdsosreport.txt -L /log \
    "$discoverd_callback_url" || give_up "Failed to discover hardware"

case "$ONSUCCESS" in
    "reboot")
        log "Automatic rebooting as required by ONSUCCESS"
        do_reboot
        ;;
    "halt")
        log "Automatic poweroff as required by ONSUCCESS"
        do_halt
        ;;
    "console")
        log "ONSUCCESS=console, launching an interactive shell"
        do_console
        ;;
    *)
        give_up "Unsupported ONSUCCESS=$ONSUCCESS value"
        ;;
esac
