#!/bin/bash

PATH=/bin:/sbin:/usr/bin:/usr/sbin:usr/local/bin:/usr/local/sbin:/usr/lib/ksvd/bin
export PATH

UDEV_RULE_CONFIG='/etc/udev/rules.d/70-persistent-net.rules'
IFCFG_DIR='/etc/sysconfig/network-scripts/'
DEV_DIR='/sys/class/net/'
LOGFILE='/var/log/ifcfg_cluster_manage.log'

WRITE_NET_RULES='/lib/udev/write_net_rules'
RULE_GENERATOR_FUNCTIONS='/lib/udev/rule_generator.functions'
PERSISTENT_NET_GENERATOR_RULES='/lib/udev/rules.d/75-persistent-net-generator.rules'

interface_name_array=()       #### define for ethX
interface_mac_array=()        #### define for ethX
interface_is_slave_array=()   #### define for ethX

other_interface_name_array=()       ### define for other NIC
other_interface_mac_array=()        ### define for other NIC
other_interface_is_slave_array=()   #### define for other NIC

abandon_mac_array=()
abandon_mac_count=0
recovery_mac_array=()
recovery_mac_count=0

PRE='ethx'
PRELEN=${#PRE}

log(){
    local prefix=`date "+%Y-%m-%d %H:%M:%S"`" : "
    echo "${prefix}" $@ >> ${LOGFILE}
}

warn(){
    local prefix=`date "+%Y-%m-%d %H:%M:%S"`" : WARN : "
    echo "${prefix}" $@ >> ${LOGFILE}
}

fatal(){
    local prefix=`date "+%Y-%m-%d %H:%M:%S"`" : ERROR : "
    echo "${prefix}" $@ >> ${LOGFILE}
}

remove_udev_default_rule(){
    if [ -f ${WRITE_NET_RULES} ]; then
        rm -rf ${WRITE_NET_RULES}
    fi

    if [ -f ${RULE_GENERATOR_FUNCTIONS} ]; then
        rm -rf ${RULE_GENERATOR_FUNCTIONS}
    fi

    if [ -f ${PERSISTENT_NET_GENERATOR_RULES} ]; then
        rm -rf ${PERSISTENT_NET_GENERATOR_RULES}
    fi
}

modify_boot_cfg(){
    bootfiles=`grep -r net.ifnames /boot/  | awk -F ':' '{print $1}' | sort | uniq`
    for bootfile in ${bootfiles}
    do
        if  grep -q "net.ifnames=1" ${bootfile} > /dev/null 2>&1; then
            log "net.ifname is already '1' in  ${bootfile}"
        else
            sed -i 's/net.ifnames=0/net.ifnames=1/g'  ${bootfile}
            log "sed -i 's/net.ifnames=0/net.ifnames=1/g'  ${bootfile}"
        fi
    done
}

generate_ifcfg_config(){
        local ifcfgfile="${IFCFG_DIR}/ifcfg-$1"
        cat > ${ifcfgfile}  <<EOF
###generated  by ${PROG}
TYPE=Ethernet
BOOTPROTO=static
NAME=$1
DEVICE=$1
ONBOOT=yes

EOF

}

modify_ifcfg_config(){
    for((n=0;n<${#interface_name_array[*]};n++))
    do
        ifcfg_file="${IFCFG_DIR}/ifcfg-${interface_name_array[$n]}"
        if [ -f "${ifcfg_file}" ]; then
            if  grep -q "BOOTPROTO=static" ${ifcfg_file} > /dev/null 2>&1; then
                log "BOOTPROTO is already 'static' in  ${ifcfg_file}"
            else
                sed -i 's/BOOTPROTO=.*/BOOTPROTO=static/g'  ${ifcfg_file}
                log "sed -i 's/BOOTPROTO=.*/BOOTPROTO=static/g'  ${ifcfg_file}"
            fi
            if  grep -q "ONBOOT=yes" ${ifcfg_file} > /dev/null 2>&1; then
                log "ONBOOT is already 'yes' in  ${ifcfg_file}"
            else
                sed -i 's/ONBOOT=.*/ONBOOT=yes/g'  ${ifcfg_file}
                log "sed -i 's/ONBOOT=.*/ONBOOT=yes/g'  ${ifcfg_file}"
            fi
        else
            warn "${ifcfg_file} not exist, generate it !"
            generate_ifcfg_config ${interface_name_array[$n]}
        fi
    done
}

modify_slave_config(){
    ifcfgfiles=`grep -r "DEVICE=$1$"  /etc/sysconfig/network-scripts/ | awk -F : '{print $1}'`
    for f in ${ifcfgfiles}
    do
        sed -i "s/DEVICE=$1$/DEVICE=$2/g"  ${f}
        log "sed -i \"s/DEVICE=$1/DEVICE=$2/g\"  ${f}"
    done
}

get_bonding_slave_mac(){
    bondings=`ls /proc/net/bonding/ 2>/dev/null`
    for bonding in $bondings
    do
        local ifnames=`cat /proc/net/bonding/${bonding} |grep "Slave Interface" | awk '{print $3}'`
        local macs=`cat /proc/net/bonding/${bonding} |grep "Permanent HW addr"|awk '{print $4}'`
        local ifnames_array=()
        local macs_array=()
        i=0
        for var in $ifnames
        do
            ifnames_array[$i]=$var
            ((i=i+1))
        done

        i=0
        for var in $macs
        do
            macs_array[$i]=$var
            ((i=i+1))
        done

        if [ ${#ifnames_array[*]} -ne ${#macs_array[*]} ];then
            warn "ifnames != macs, ifnames=${ifnames}, macs=${macs}"
        fi

        for((n=0;n<${#ifnames_array[*]};n++))
        do
            if [ "$1"  == "${ifnames_array[$n]}" ];then
                if [ "${macs_array[$n]}" != "" ];then
                    echo "${macs_array[$n]}"
                    return
                fi
            fi
        done
    done

    echo ""
    return
}

get_slave_mac(){
    typeset -l hwaddr
    ####find from kernel
    hwaddr=`get_bonding_slave_mac  "$1"`
    if [ "$hwaddr" != "" ];then
        echo "$hwaddr"
        log "get_bonding_slave_mac:${hwaddr}"
        return
    fi

    files=`ls /etc/sysconfig/network-scripts/ifcfg-*`
    for file in $files
    do
        unset SLAVE DEVICE HWADDR
        . $file

        ####find from config
        if [ "$SLAVE" == "yes" -a "$DEVICE" == "$1" -a "$HWADDR" != "" ];then
            hwaddr="$HWADDR"
            echo "$hwaddr"
            log "get_slave_mac:${hwaddr} for ${1}"
            return
        fi
    done
    echo ""
}

generate_udev_rules(){

    all_vir_interfaces=`ls /sys/devices/virtual/net/`
    all_phy_interfaces=`ls /sys/class/net/ |grep -v "${all_vir_interfaces}" |grep -v bonding_masters`

    log "all_phy_interfaces=${all_phy_interfaces}"

    if [ "$ACTION" == "modify" ];then
        if [ ! -f ${UDEV_RULE_CONFIG}  ];then
            echo "###generated  by ${PROG}" >> ${UDEV_RULE_CONFIG}
        fi
        log "modify  ${UDEV_RULE_CONFIG}"
    else
        ###remove default rule
        remove_udev_default_rule

        rm -rf ${UDEV_RULE_CONFIG}
        echo "###generated  by ${PROG}" >> ${UDEV_RULE_CONFIG}
        log "generate  ${UDEV_RULE_CONFIG}"
    fi

    i=0
    j=0
    for iface in ${all_phy_interfaces}
    do
        mac=`cat ${DEV_DIR}/${iface}/address`
        slave_mac=`get_slave_mac ${iface} ${mac}`
       
       	###store abandon MAC
	if [ "$slave_mac" != "" -a "$mac" != "$slave_mac" ];then
            abandon_mac_array[$abandon_mac_count]="${mac}"
            ((abandon_mac_count=abandon_mac_count+1))
            log "store ${mac}"
            mac="${slave_mac}"
        fi

        if  [ ${iface:0:${PRELEN}} != "${PRE}" ]; then
            if [ "${slave_mac}" != "" ]; then
                other_interface_is_slave_array[$j]="yes"
            else
                other_interface_is_slave_array[$j]="no"
            fi
            other_interface_name_array[$j]=${iface}
            other_interface_mac_array[$j]=${mac}
            ((j=j+1))
        else
            if [ "${slave_mac}" != "" ]; then
                interface_is_slave_array[$i]="yes"
            else
                interface_is_slave_array[$i]="no"
            fi
            interface_name_array[$i]=${iface}
            interface_mac_array[$i]=${mac}
            ((i=i+1))
        fi
    done
    

    ####find free ifname
    x=0
    for((n=0;n<65535;n++))
    do
        FOUND=0
        tmp_iface="${PRE}$n"
        if [ $x -ge $j ];then
            break
        fi

        for var in ${interface_name_array[@]}
        do
            if [ "$var" == "$tmp_iface" ];then
                FOUND=1
		break
	    fi
        done

        if [ $j -ne 0 -a $FOUND -eq 0 -a $x -lt $j ];then
            ### rename NIC
            ifconfig ${other_interface_name_array[$x]} down >> ${LOGFILE} 2>&1;
            ip link set ${other_interface_name_array[$x]} name ${tmp_iface} >> ${LOGFILE} 2>&1;
            if [ -f ${IFCFG_DIR}/ifcfg-${other_interface_name_array[$x]} ]; then
                mv -f ${IFCFG_DIR}/ifcfg-${other_interface_name_array[$x]} ${IFCFG_DIR}/ifcfg-${tmp_iface}
                sed -i "s/NAME=.*/NAME=${tmp_iface}/g"  ${IFCFG_DIR}/ifcfg-${tmp_iface}
                sed -i "s/DEVICE=.*/DEVICE=${tmp_iface}/g"  ${IFCFG_DIR}/ifcfg-${tmp_iface}
            fi
            ifconfig ${tmp_iface} up >> ${LOGFILE} 2>&1;
            
            modify_slave_config ${other_interface_name_array[$x]} ${tmp_iface}

            warn "${other_interface_name_array[$x]} of ${other_interface_mac_array[$x]} invalid, Must fix it to ${tmp_iface}!"
            other_interface_name_array[$x]="${tmp_iface}"
            ((x=x+1))
        fi
    done

    if [ $x -ne $j ];then
        fatal "interface name more than 65535! interfaces=${all_phy_interfaces}. Must Manual repair it" 
    fi

    ###put together
    for((n=0;n<${#other_interface_name_array[*]};n++))
    do
        interface_name_array[$i]=${other_interface_name_array[$n]}
        interface_mac_array[$i]=${other_interface_mac_array[$n]}
        interface_is_slave_array[$i]=${other_interface_is_slave_array[$n]}
        ((i=i+1))
    done

    ###recovery mac
    for((n=0;n<${#abandon_mac_array[*]};n++))
    do
        is_repeat=0
        for((m=0;m<${#interface_name_array[*]};m++))
        do
            if [ "${interface_mac_array[${m}]}" == "${abandon_mac_array[${n}]}" ];then
                is_repeat=1
                break
	    fi
        done
        if [ ${is_repeat} -eq 0 ]; then
            log "save ${abandon_mac_array[${n}]}"
            recovery_mac_array[$recovery_mac_count]="${abandon_mac_array[${n}]}"
            ((recovery_mac_count=recovery_mac_count+1))	    
        fi
    done
    for((n=0;n<${#interface_name_array[*]};n++))
    do
        for((m=n+1;m<${#interface_name_array[*]};m++))
        do
            if [ "${interface_mac_array[${m}]}" == "${interface_mac_array[${n}]}" ];then
                log "${interface_name_array[${m}]}:${interface_mac_array[${m}]}:${interface_is_slave_array[${m}]},${interface_name_array[${n}]}:${interface_mac_array[${n}]}:${interface_is_slave_array[${n}]}!"
                if [ "${interface_is_slave_array[${m}]}" == "no" -a "${interface_is_slave_array[${n}]}" == "yes" ];then
                    ((recovery_mac_count=recovery_mac_count-1))	    
                    log "${interface_name_array[${m}]}:${interface_mac_array[${m}]}->${recovery_mac_array[$recovery_mac_count]}"
                    interface_mac_array[${m}]=${recovery_mac_array[$recovery_mac_count]}

                elif [ "${interface_is_slave_array[${m}]}" == "yes" -a "${interface_is_slave_array[${n}]}" == "no" ];then
                    ((recovery_mac_count=recovery_mac_count-1))	    
                    log "${interface_name_array[${n}]}:${interface_mac_array[${n}]}->${recovery_mac_array[$recovery_mac_count]}"
                    interface_mac_array[${n}]=${recovery_mac_array[$recovery_mac_count]}

                else
                    fatal "${interface_name_array[${m}]}:${interface_mac_array[${m}]}:${interface_is_slave_array[${m}]},${interface_name_array[${n}]}:${interface_mac_array[${n}]}:${interface_is_slave_array[${n}]} is error!"
                fi
            fi
        done
    done

    for((n=0;n<${#interface_name_array[*]};n++))
    do
        if  grep -q "${interface_mac_array[${n}]}" ${UDEV_RULE_CONFIG} > /dev/null 2>&1; then
            log "'${interface_mac_array[${n}]}' is already in  ${UDEV_RULE_CONFIG}"
            continue
        fi
        rule="ACTION==\"add\", SUBSYSTEM==\"net\", DRIVERS==\"?*\", ATTR{address}==\"${interface_mac_array[${n}]}\", NAME=\"${interface_name_array[${n}]}\""
        echo ${rule} >> ${UDEV_RULE_CONFIG}
        log "echo ${rule} >> ${UDEV_RULE_CONFIG}"
    done

    modify_boot_cfg

}

usage(){
        cat <<EOT

        usage: $1 <action>  <log>  
                  action:  create|modify
                  log:     log file
EOT
}

if [ $# -ne 2 ]; then
    usage $0
    exit 1
fi

PROG=${0}
ACTION=${1}
LOGFILE=${2}

case "$ACTION" in
    create) 
        generate_udev_rules
        modify_ifcfg_config
        sync
        log "sync; ret=$?"
        ;;
    modify) 
        generate_udev_rules
        modify_ifcfg_config
        sync
        log "sync; ret=$?"
        ;;
    *) 
        echo "ERROR: unknow action: ${ACTION}";
        exit 1
        ;;
esac

