#!/bin/bash
#
# Provides operns_wait_until_ready() which waits for the operns to become ready. 
# By which we mean at least one nic has an IP address assigned to it.
#
# Copyright (c) 2015-2017 by Cisco Systems, Inc.
#

if [[ "$operns_functions_sourced" != true ]]; then
    operns_functions_sourced=true
    . /etc/init.d/functions

    if [[ "$spirit_log_sourced" != true ]]; then
        . /etc/init.d/spirit_log.sh
    fi
fi

#
# Function to check if this is ThinXR or not.
#
function operns_is_thinxr()
{
    [[ -e "/opt/cisco/thinxr/am_i_thinxr" ]]
}

# Function to identify the network namespace of a given PID.
#
# The built in 'ip netns identify' command requires that a PID's
# assigned network namespace have the same inode *and* device id
# as any known namespace to identify it. This fails in our LXCs
# because the netns is mapped from the host and hence has a different
# device id than expected. This may be a bug in our setup but it's
# easy enough to work around for now.
function netns_identify {
    test -n "$1" || return 1

    local pid=$1
    local inum=`stat -L -c '%i' /proc/$pid/ns/net 2>/dev/null`
    test -n "$inum" || return 1
    find -L /var/run/netns -inum "$inum" 2>/dev/null | cut -d/ -f5
    return 0
}

# Function to determine whether the current process is running in
# the XR namespace or a different (app-hosting) namespace
function in_xrnns {
    local ns=`netns_identify $$`
    case "$ns" in
        xrnns)
            return 0
            ;;
        *.libvirt)
            # LXC namespace (hostos only)
            return 0
            ;;
        "")
            # System default namespace (hostos or calvados)
            return 0
            ;;
        *)
            # Anything else is an app-hosting namespace
            return 1
            ;;
    esac
}

#
# How long to wait overall for at least one XR nic to come up.
#
OPERNS_MGMT_NIC_WAIT_MAX=12 # * OPERNS_DELAY_BETWEEN_POLLS

#
# How long between each attempt to see if XR nics are up.
#
OPERNS_DELAY_BETWEEN_POLLS=5

function operns_log()
{
    platform_log "$*"
    /usr/bin/logger "$0: $*"

    #
    # If already logging to XR, print nothing. If manually started,
    # show some progress
    #
    if [[ `tty` == "/dev/pts/0" ]]; then
        platform_log_console "$*"
    else
        echo `date` "$0: $*"

        if [[ "$operns_log_first" == "" ]]; then
            operns_log_first=no
            echo `date` "$0: Press ^C to stop if needed."
        fi
    fi
}

#
# Scan the nics within the operns and break out at the first one that we find.
# We don't really care if it is up or down or has an IP address. All that we
# do care about is that the nic exists and hence operns infra is "ready"
#
function operns_find_first_valid_nic()
{
    local filter=$1
    if [[ -z "$2" ]]; then
        local vrf_name="global-vrf"
    else
        local vrf_name="$2"
    fi
    local nics=$(ip netns exec "$vrf_name"    \
                    cat /proc/net/dev   | \
                 awk '{print $1}'       | \
                 grep "$filter"         | \
                 grep -v "^Inter\-"     | \
                 sed 's/://g')

    for nic in $nics
    do
        (ip netns exec "$vrf_name" ifconfig $nic) 2>/dev/null | grep -q "\<UP\>.*MTU"
        if [[ $? -eq 0 ]]; then
            operns_log "Found nic, $nic"
            true
            return
        fi
    done
    false
}

#
# Wait forever, until the operns is ready. If at least one nic has an IP
# address, we're done.
#
function operns_any_nic_wait()
{
    operns_log "Waiting for OPERNS interface creation..."
    local vrf_name="$1"
    while true
    do
        operns_find_first_valid_nic "^[A-Z]" "$vrf_name"
        if [[ $? -eq 0 ]]; then
            break;
        fi

        sleep $OPERNS_DELAY_BETWEEN_POLLS
    done
}

#
# Wait a short period of time for the management nic to become ready
#
function operns_mgmt_nic_wait()
{
    operns_log "Waiting for OPERNS management interface creation..."

    local count=0
    local vrf_name="$1"
    while true
    do
        operns_find_first_valid_nic "^Mg" "$vrf_name"
        if [[ $? -eq 0 ]]; then
            break;
        fi

        count=`expr $count + 1`
        if [[ $count -gt $OPERNS_MGMT_NIC_WAIT_MAX ]]; then
            operns_log "Waiting for OPERNS management interface... timed out"
            return
        fi

        sleep $OPERNS_DELAY_BETWEEN_POLLS
    done
}

#
# Waits for at least one XR nic to come up within operns and then waits a bit
# longer still until the XR mgmt nic is up. After this, third party services
# should have the access they need to XR interfaces.
#
function operns_wait_until_ready()
{
    local vrf_name="$1"

    if ! operns_is_thinxr; then
        # On ThinXR, we run sshd even if there are no interfaces in the VRF
        # yet.
        operns_any_nic_wait "$vrf_name"
        operns_mgmt_nic_wait "$vrf_name"
    fi

    operns_log "OPERNS is ready"
}

#
# Wrapper for commands that only work if run in XRNNS.
# Transparent (no modification) if already in XRNNS, otherwise will
# prepend the necessary commands to execute the command in xrnns.
#
function operns_to_xrnns_cmd()
{
    local prefix=""

    if ! in_xrnns; then
        prefix="ip netns exec xrnns"
        # ip netns exec requires root privileges
        if [ `id -u` -ne 0 ]; then
            prefix="sudo ${prefix}"
        fi
    fi
    ${prefix} "$@"
}
