#!/bin/bash
# Copyright (c) 2014, Cisco Systems, Inc. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#  Redistributions of source code must retain the above copyright
#  notice, this list of conditions and the following disclaimer.
#  Redistributions in binary form must reproduce the above copyright
#  notice, this list of conditions and the following disclaimer in
#  the documentation and/or other materials provided with the
#  distribution.
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
#  Script to check that environment is set up correctly for usnic_verbs
#
# Script to gather information to help tech support diagnose problems

#
# Gather info about enic driver
#
do_enic()
{
    find /lib/modules -name enic.ko -ls > find-ko 2>&1
    modinfo enic > modinfo 2>&1
    [[ -f /sys/module/enic/version ]] && cp /sys/module/enic/version version 2> /dev/null
}

#
# Gather info about usnic_verbs driver
#
do_usnic_verbs()
{
    infodir=$PWD

    local umntdbgfs=0
    local drv=/sys/module/usnic_verbs
    local dbginfo=/sys/kernel/debug/usnic_verbs
    local drvparm=/sys/module/usnic_verbs/parameters
    find /lib/modules -name usnic_verbs.ko -ls > find-ko 2>&1
    modinfo usnic_verbs > modinfo 2>&1
    [[ -f ${drv}/version ]] && cp ${drv}/version usnic_verbs_version 2> /dev/null
    cp ${drvparm}/* . 2> /dev/null
    mount -t debugfs nodev /sys/kernel/debug 2>/dev/null
    [[ "$?" == 0 ]] && umntdbgfs=1
    [[ -f ${dbginfo}/build-info ]] && cp ${dbginfo}/build-info build-info 2> /dev/null
    [[ "$umntdbgfs" == 1 ]] && umount /sys/kernel/debug 2>/dev/null
    ibv_devinfo -v > ibv_devinfo 2>&1

    # collect data from /sys/class/infiniband/usnic_*
    if [[ -d /sys/class/infiniband ]]; then
        cd /sys/class/infiniband
        for nic in usnic_*; do
            cd /sys/class/infiniband/${nic} || continue
            mkdir -p ${infodir}/infiniband/${nic} || continue
            tar cf - . | (cd ${infodir}/infiniband/${nic}; tar xf -)
        done
        cd ${infodir}
    fi > infiniband.out 2>&1
}

#
# Gather info about libusnic_verbs
#
do_libusnic_verbs()
{
    local i
    local lib=/usr/lib64/libusnic_verbs-rdmav2.so
    ls -l ${lib} > ls-l 2>&1
    cp ${lib} . 2> /dev/null
    lsof 2> /dev/null | grep libusnic > lsof 2>&1
    local librun=(`lsof 2> /dev/null | grep libusnic | awk -F' ' '{print $9}' | sort | uniq`)
    { for i in ${librun[@]}; do
        echo "== $i"
        strings $i | grep libusnic_verbs
      done } &> libusnic_installed
}

#
# Gather info about openmpi, if openmpi-cisco is installed
# source openmpi-vars.sh to use ompi_info in this package
#
set_ompi_env()
{
    ompi_name=openmpi-cisco
    env_sh=$(rpm -ql ${ompi_name} | grep "openmpi-vars.sh" 2> /dev/null)
    if [[ -z ${env_sh} ]]; then
        echo "openmpi-vars.sh cannot be found" > vars.out
        return
    fi

    source ${env_sh} 2> /dev/null
}

do_openmpi()
{
    set_ompi_env

    ompi_info_path=$(which ompi_info 2> /dev/null)
    if [[ $? != 0 ]]; then
        echo -n "Cannot find ompi_info, check if Open MPI is installed or user "
        echo "enviroments are set properly to run ompi_info"
        echo "Cannot find ompi_info" > notes
        return
    fi

    ompi_info --all --parsable > ompi_info.txt 2>&1

    # If not successful, just return
    if [[ $? != 0 ]]; then
        echo "The ompi_info executable failed to execute successfully."
        echo "Check to ensure that Open MPI is installed/setup properly."
        return
    fi

    ompi_home=$(ompi_info --parsable 2> /dev/null | grep path:prefix: | cut -d: -f3-)
    echo "mpihome=$mpihome" > notes
    echo "" >> notes
    echo "Other dirs close by:" >> notes
    ls -l ${mpihome}/.. >> notes
}

#
# Generic data that all components need
#
do_common()
{
    infodir=$PWD

    env > env 2>&1
    parse_list $LD_LIBRARY_PATH : > LD_LIBRARY_PATH
    parse_list $PATH : > PATH

    dmesg > dmesg 2>&1
    cat /var/log/messages > messages 2>&1
    rpm -qa > rpm-qa 2>&1
    uname -a > uname-a 2>&1

    lspci -vv > lspci-vv 2>&1

    ifconfig -a > ifconfig-a 2>&1
    ip -s link > ip_link 2>&1
    ip -s addr > ip_addr 2>&1
    ip rule > ip_rule 2>&1
    ip route > ip_route 2>&1
    ip route list table local > ip_rt_local 2>&1
    ip route list table cache > ip_rt_cache 2>&1
    ip -s neigh show > ip-s_neigh_show 2>&1
    tc -s qdisc show > tc-s-qdisc 2>&1
    tc -s filter show > tc-s-filter 2>&1
    tc -s class show > tc-s-class 2>&1
    netstat -tupan > netstat-tupan 2>&1
    netstat -s > netstat-s 2>&1
    ss -s > ss-s 2>&1
    ss -aepi > ss-aepi 2>&1
    sysctl -a > sysctl-a 2>&1
    getenforce > getenforce 2>&1
    iptables -nt filter -L > iptables-filter-L 2>&1
    iptables -nt nat -L > iptables-nat-L 2>&1
    iptables -nt mangle -L > iptables-mangle-L 2>&1
    iptables -nt raw -L > iptables-raw-L 2>&1
    brctl show > brctl_show 2>&1
    ps -aefww > ps-aefww 2>&1
    cat /etc/resolv.conf > resolv.conf 2>&1
    cat /etc/hosts > hosts 2>&1
    {
        find /proc -maxdepth 1 -type f | while read f; do
            [[ ${f} == /proc/kmsg ]] && continue
            [[ ${f} == /proc/kcore ]] && continue
            [[ ${f} == /proc/sysrq-trigger ]] && continue
            dir=${f%/*}
            local_dir=${PWD}${dir}
            [[ -d ${local_dir} ]] || mkdir -p ${local_dir}
            cp $f ${local_dir}
        done
        find /proc/sys/net/ /proc/net/ /proc/bus/ /proc/irq/ /proc/driver/ -type f | while read f; do
            [[ ${f} == /proc/sys/ipv4/route/flush ]] && continue
            [[ ${f} == /proc/sys/ipv6/route/flush ]] && continue
            [[ ${f} == /proc/net/rpc/* ]] && continue
            dir=${f%/*}
            local_dir=${PWD}${dir}
            [[ -d ${local_dir} ]] || mkdir -p ${local_dir}
            cp $f ${local_dir}
        done
    } 2>/dev/null

    (
        cd /sys/class/net
        for iface in *; do
            ifdir=${infodir}/${iface}
            mkdir -p ${ifdir}
            ethtool ${iface} > ${ifdir}/ethtool 2>&1
            ethtool -S ${iface} > ${ifdir}/ethtool-S 2>&1
            ethtool -P ${iface} > ${ifdir}/ethtool-P 2>&1
            ethtool -c ${iface} > ${ifdir}/ethtool-c 2>&1
            ethtool -k ${iface} > ${ifdir}/ethtool-k 2>&1
            ethtool -i ${iface} > ${ifdir}/ethtool-i 2>&1
        done
    )
}

#
# parse and pretty-print a list
#
parse_list()
{
    list=$1
    sep=$2
    OFS=$IFS; IFS=${sep}; set $list; IFS=$OFS
    while (( $# > 0 )); do
        echo "  $1"
        shift
    done
}

#
# main - collect data for all components and tar it up
#
main()
{
    # Make sure user is root
    if [[ "${UID}" != 0 ]]; then
        echo "Please run this script as root" 1>&2
        exit 1
    fi

    #
    # Create a directory for collection
    #
    base=${1-/tmp}
    if [[ ! -d "${base}" ]]; then
        echo "Usage: tech_support_info [collection_dir]"
        echo "Collects information for tech support and packages it in a tarball" 1>&2
        echo "Uses <collection_dir> as a staging area, defaults to /tmp" 1>&2
        echo "<collection_dir> must already exist" 1>&2
        exit 1
    fi
    cd ${base}
    if [[ $? != 0 ]]; then
        echo "Cannod chdir to ${base}" 1>&2
        exit 1
    fi

    pkg=tech_support_info.$(date +%Y%m%d%H%M)
    mkdir ${pkg}
    if [[ $? != 0 ]]; then
        echo "Cannod create ${base}/${pkg}" 1>&2
        exit 1
    fi

    #
    # run each component's collector
    #
    components="common openmpi enic usnic_verbs libusnic_verbs"
    for c in ${components}; do
        mkdir ${base}/${pkg}/${c}
        if [[ $? != 0 ]]; then
            echo "Cannod create ${base}/${pkg}/${c}" 1>&2
            exit 1
        fi
        cd ${base}/${pkg}/${c}
        echo -n "Collecting $c info ..."
        eval do_${c}
        echo " done"
    done

    #
    # create the tarball
    #
    tarball=${pkg}.tar.xz
    cd ${base}

    echo -n "Creating and compressing tarball ..."
    tar Jcf ${tarball} ${pkg}
    echo " done"

    if [[ $? = 0 ]]; then
        echo "Created ${base}/${tarball}"
    else
        echo "Error running tar to create ${tarball}" 1>&2
        exit 1
    fi

    # remove packaging dir
    rm -rf ${pkg}

    exit 0
}

main $*
