#!/usr/bin/env bash

set -e

# 帮助函数
help()
{
    echo "Usage:"
    echo "    instanta_bonding <add|del> [-m master_dev] [-i ip addr] [-n netmask] [-s slave_dev ...] [-t master_dev] [-g default gateway]"
    echo ""
    echo "Examples:"
    echo "    instanta_bonding add -m bond0 -i 10.0.0.99/24 -s 'swift95f0 swift95f1'"
    echo "    instanta_bonding add -m bond0 -i 10.0.0.99/24 -s swift95f0"
    echo "    instanta_bonding add -m bond0 -i 10.0.0.99/24 -s 'swift95f0 swift95f1' -g 10.0.0.9"
    echo "    instanta_bonding add -m bond0 -s 'swift95f0 swift95f1'"
    echo "    instanta_bonding add -t 'bond0 bond1'"
    echo "    instanta_bonding del -m bond0 -s 'swift95f0 swift95f1'"
    echo "    instanta_bonding del -m 'bond0 bond1'"
    echo "    instanta_bonding del -t 'bond0 bond1'"
    echo "    instanta_bonding del"
}

# 安装bonding驱动
bonding_driver_load()
{
    # 检查驱动是否安装了
    check_result=`lsmod | awk '{print $1}' | sed -n '/bonding/p'`
    if [ "$check_result" != "bonding" ] # 驱动未安装
    then
        modprobe bonding
    fi
}

# 添加全部bond，命令：yusurbonding add -m bond0 -i 10.0.0.99/24 -s 'swift95f0 swift95f1'
add_all()
{
    # 安装bonding驱动
    bonding_driver_load

    # 从instanta_bondmasters获取全部的bond设备
    master_devs=`cat /sys/class/net/instanta_bondmasters`
    if [ -n "$master_devs" ]
    then
        # 如果instanta_bondmasters中含有bond设备，遍历这些设备
        for var in $master_devs
        do
            # 此bond设备的slave配置文件不存在给出提示
            if [ ! -e "/sys/class/net/$var/bonding/slaves" ] ; then
                echo "Bond iface $var slaves config file not exist"
            else
                slave_devs=(`cat /sys/class/net/$var/bonding/slaves`)
                for var1 in ${slave_devs[*]}
                do
                    for var2 in $3
                    do
                        if [ "$var1"X == "$var2"X ]
                        then
                            echo "Bond iface $var has $var1"
                            exit -1
                        fi
                    done
                done
            fi
        done
    fi

    # 检查要添加的bond是否已添加
    if [ ! -e "/sys/class/net/$1" ] ; then
        echo +"$1" > /sys/class/net/bonding_masters
    else
        echo "Bond iface $1 exists"
        exit -1
    fi

    # 检查bond是否添加成功，未成功则报错
    if [ ! -e "/sys/class/net/$1" ]
    then
        echo "Bond iface $1 failed creation"
        exit -1
    fi

    # 检查instanta_bondmasters是存在，如果不存在则报错
    if [ ! -e "/sys/class/net/instanta_bondmasters" ]
    then
        echo "Instanta bond driver not loaded correctly"
        exit -1
    fi

    # 把新bond设备down掉
    ip link set $1 down
    # 配置新bond设备为主备模式
    echo active-backup > /sys/class/net/"$1"/bonding/mode
    # 配置新bond设备链路检测时间间隔为200毫秒
    echo 200 > /sys/class/net/"$1"/bonding/miimon
    echo "Created and setup bond $1..."

    # 给新bond设备添加所有slave物理网口 (参数3可以解析为数组)
    for var in $3
    do
        # 把物理网口down掉
        ip link set dev "$var" down
        # 添加物理网口到新bond设备的slave配置中
        echo \+"$var" > /sys/class/net/"$1"/bonding/slaves
        echo "Added slave ${var}..."
        # 把物理网口up起来
        ip link set dev "$var" up
        # 清除新slave设备的ip
        ip addr flush dev "$var"
    done

    # 添加新bond设备到instanta_bondmasters
    echo \+"$1" > /sys/class/net/instanta_bondmasters

    # 清除新bond设备的ip
    ip addr flush dev $1
    # 设置新bond设备ip并up起来
    ip addr add dev $1 $2 &&
    ip link set $1 up
    #设置网关
    if [ -n "$4" ];then
        route add -net 0.0.0.0/0  gw $4
    fi
    echo "set $1 ip is $2"
}

# 添加bond slaves，命令：yusurbonding add -m bond0 -i 10.0.0.99/24 -s swift95f0
add_slaves()
{
    # 安装bonding驱动
    bonding_driver_load
    # 检查slaves是否为空，为空则报错
    if [ -z "$2" ]
    then
        echo "Slaves couldn't be empty"
        exit 1
    fi
    # 检查此bond是否存在，不存在则报错
    if [ ! -e "/sys/class/net/$1" ]
    then
        echo "Bond iface $1 wasn't created"
        exit -1
    fi
    # 检查此bond的slave配置文件是否存在，不存在则报错
    if [ ! -e "/sys/class/net/$1/bonding/slaves" ]
    then
        echo "Bond iface $1 slaves config file not exist, please recreate bond iface"
        exit -1
    fi

    # 从instanta_bondmasters获取全部的bond设备
    master_devs=`cat /sys/class/net/instanta_bondmasters`
    if [ -n "$master_devs" ]
    then
        # 如果instanta_bondmasters中含有bond设备，遍历这些设备
        for var in $master_devs
        do
            # 此bond设备的slave配置文件不存在给出提示
            if [ ! -e "/sys/class/net/$var/bonding/slaves" ] ; then
                echo "Bond iface $var slaves config file not exist"
            else
                slave_devs=(`cat /sys/class/net/$var/bonding/slaves`)
                for var1 in ${slave_devs[*]}
                do
                    for var2 in $2
                    do
                        if [ "$var1"X == "$var2"X ]
                        then
                            echo "Bond iface $var has $var1"
                            exit -1
                        fi
                    done
                done
            fi
        done
    fi

    # 给此bond添加slave (参数2可以解析为数组)
    for var in $2
    do
        # 把物理网口down掉
        ip link set dev "$var" down
        # 添加物理网口到此bond设备的slave配置中
        echo \+"$var" > /sys/class/net/"$1"/bonding/slaves
        echo "Added slave ${var}..."
        # 把物理网口up起来
        ip link set dev "$var" up
        # 清除新slave设备的ip
        ip addr flush dev "$var"
    done
}

# 删除指定bond设备的slave物理网口，命令：yusurbonding del -m bond0 -s 'swift95f0 swift95f1'
del_slaves()
{
    # 安装bonding驱动
    bonding_driver_load
    # 遍历bond设备列表
    for var in $1
    do
        # 检查bond设备是否存在，不存在则报错
        if [ ! -e "/sys/class/net/$var" ]
        then
            echo "Bond iface $var wasn't exist"
            exit -1
        fi
        # 删除此bond设备的所有slave物理网口 (把参数2当做数组)
        for var1 in $2
        do
            # 从bond设备配置文件中删除此物理网口
            echo -"$var1" > /sys/class/net/"$var"/bonding/slaves
            echo "Deleted slave ${var1}..."
            # TS-564问题修改，删除slave后先down再up，规避删除bond后slave不能收发包的问题
            ip link set dev "$var1" down
            ip link set dev "$var1" up
        done
    done
}

# 删除指定的bond设备，命令：yusurbonding del -m 'bond0 bond1'
del_masters()
{
    # 安装bonding驱动
    bonding_driver_load
    # 删除所有bond设备 (参数1可以解析为数组)
    for var in $1
    do
        # 检查此bond设备是否存在，不存在则报错
        if [ ! -e "/sys/class/net/$var" ]; then
            echo "Bond iface $var wasn't exist"
        else
            # 获取此bond设备的全部slave物理网口
            res=`cat /sys/class/net/$var/bonding/slaves`
            # 如果此bond设备的slave物理网口不为空，说明还未删除，报错
            if [ -n "$res" ]; then
                echo "Bond iface $var has slaves, please delete slaves first"
                exit 1
            else
                # 删除此bond设备
                echo -"$var" > /sys/class/net/instanta_bondmasters
                res=`cat /sys/class/net/instanta_bondmasters | awk '{print $0}' | sed -n "/\<$var\>/p"`
                if [ -n "$res" ]; then
                    echo "Delete instanta bond $var failed, being used by user process..."
                    exit 1
                fi
                echo -"$var" > /sys/class/net/bonding_masters
                if [ -e "/sys/class/net/$var" ]; then
                    # 有些内核版本存在一次删不掉的情况
                    sleep 0.5
                    echo -"$var" > /sys/class/net/bonding_masters
                fi
                # 有些同过nmcli添加的bond设备无法删除，通过nmcli检查是否存在这样的bond
                res=`nmcli connection show 2> /dev/null | awk '{print $1}' | sed -n "/\<$var\>/p"`
                if [ -n "$res" ]; then
                    # 存在这样的bond，通过nmcli再删除一次
                    nmcli connection delete $var 2 > /dev/null
                    echo "Deleted bond ${var} by nmcli..."
                else
                    echo "Deleted bond ${var}..."
                fi
            fi
        fi
    done
}

del_dev()
{
    # 安装bonding驱动
    bonding_driver_load
    master_devs=$1
    if [ -n "$master_devs" ]; then
        # 如果instanta_bondmasters中含有bond设备，遍历这些设备逐个删除
        for var in $master_devs
        do
            # 此bond设备的slave配置文件不存在，说明之前添加的有错误，直接报错
            if [ ! -e "/sys/class/net/$var/bonding/slaves" ]
            then
                echo "Bond iface $var slaves config file not exist, please recreate bond iface"
                exit -1
            fi
            # 从此bond设备的slave配置文件获取所有的slave物理网口
            slave_devs=(`cat /sys/class/net/$var/bonding/slaves`)
            # 删除此bond设备的所有slave物理网口
            del_slaves $var "${slave_devs[*]}"
            # 删除此bond设备
            del_masters $var
        done
    else
        # instanta_bondmasters没有bond设备信息直接报错
        echo "There are no bond devices"
        exit 1
    fi
}

# 删除全部的bond设备，命令：yusurbonding del
del_all()
{
    # 安装bonding驱动
    bonding_driver_load
    # 检查instanta_bondmasters是存在，如果不存在则报错
    if [ ! -e "/sys/class/net/instanta_bondmasters" ]
    then
        echo "Instanta bond driver not loaded correctly"
        exit -1
    fi
    # 从instanta_bondmasters获取全部的bond设备
    master_devs=`cat /sys/class/net/instanta_bondmasters`
    del_dev "${master_devs[*]}"
}

set_yusur_bond_master()
{
    for var in $1
    do
        # 检查bond设备是否存在，不存在则报错
        if [ ! -e "/sys/class/net/$var" ]
        then
            echo "Bond iface $var wasn't exist"
            exit -1
        fi
        echo \+"$var" > /sys/class/net/instanta_bondmasters
    done
}

del_yusur_bond_master()
{
    for var in $1
    do
        echo -"$var" > /sys/class/net/instanta_bondmasters
        res=`cat /sys/class/net/instanta_bondmasters | awk '{print $0}' | sed -n "/\<$var\>/p"`
        if [ -n "$res" ]; then
            echo "Delete instanta bond $var failed, being used by user process..."
            exit 1
        fi
    done
}

# 命令至少带有add或者del，否则给出提示信息
if [ $# -lt 1 ]
then
    help
    exit 1
fi

cmd=$1
if [ "$cmd"X == "add"X ]; then # bond添加处理
    # bond添加处理，参数要大于3个
    if [ $# -lt 3 ]
    then
        help
        exit 1
    fi
    # 输入的bond设备
    master_dev=""
    # 输入的ip地址
    ip=""
    # 输入的slave物理网口列表
    slave_devs=()
    #-t参数带的master列表
    master_devs=()
    gateway=()
    while [ -n "$2" ];
    do
        case $2 in
            -m)
                shift 1 # 跳过-m，到了-m后面的参数值
                master_dev=$2 # 读取参数值
                shift 1 # 跳过这个参数值，到了下一个option，如-i
                ;;
            -i)
                shift 1
                ip=$2
                shift 1
                ;;
            -s)
                shift 1
                slave_devs=($2)
                shift 1
                ;;
            -t)
                shift 1
                master_devs=($2)
                shift 1
                ;;
            -g)
                shift 1
                gateway=($2)
                shift 1
                ;;
            *)
                # 匹配到不识别的参数，命令格式错误，打印帮助信息
                help
                exit 1
                ;;
        esac
    done

    if [ -n "$master_devs" ]
    then
        set_yusur_bond_master "${master_devs[*]}"
        exit 0
    fi

    # 检查输入的bond设备是否为空，为空则报错
    if [ -z "$master_dev" ]
    then
        echo "the -m option could not be empty"
        exit 1
    fi
    if [ -n "$ip" ]; then
        # ip不为空，说明是添加新的bond设备
        add_all $master_dev $ip "${slave_devs[*]}" $gateway
    else
        # ip为空，说明是给旧bond设备添加新的slave物理网口
        add_slaves $master_dev "${slave_devs[*]}"
    fi

elif [ "$cmd"X == "del"X ]; then # bond删除处理
    # 输入的bond设备列表
    master_dev=()
    # 输入的slave物理网口列表
    slave_devs=()
    #-t参数带的master列表
    master_devs=()
    while [ -n "$2" ];
    do
        case $2 in
            -m)
                shift 1
                master_dev=($2)
                shift 1
                ;;
            -s)
                shift 1
                slave_devs=($2)
                shift 1
                ;;
            -t)
                shift 1
                master_devs=($2)
                shift 1
                ;;
            *)
                help
                exit 1
                ;;
        esac
    done

    if [ -n "$master_devs" ]
    then
        del_yusur_bond_master "${master_devs[*]}"
        exit 0
    fi

    if [ -n "$master_dev" ]; then
        if [ -n "$slave_devs" ]; then
            # bond设备列表和slave列表都不为空，说明是给旧的bond设备删除slave
            del_slaves "${master_dev[*]}" "${slave_devs[*]}"
        else
            # 删除子口以及bond口设备
            del_dev "${master_dev[*]}"
        fi
    else
        if [ -n "$slave_devs" ]; then
            # bond设备列表为空，slave列表不为空，说明用户想给指定bond设备删除slave
            echo "The '-m' option couldn't be empty"
            exit 1
        else
            # bond设备列表和slave列表都为空，说明是删除全部bond设备
            del_all
        fi
    fi
else
    # 如果第一个参数不是add或者del，打印帮助信息
    help
    exit 1
fi

exit 0
