#!/bin/bash

VER=0.0.3

PROJECT=kec-ipv6-setup-utils

SCRIPT_LOG_SAVE_PATH=/var/log/kec-ipv6-setup-utils
SCRIPT_LOG_FILE=${SCRIPT_LOG_SAVE_PATH}/kec-ipv6-setup-utils_$(date +"%Y%m%d").log

SYSCTL_CONF_PATH="/etc/sysctl.d"
SYSCTL_CONF_FILE="/etc/sysctl.d/100-sysctl.conf"

FUNCTION_RETURN_VALUE=0
SYSTEM_NEED_REBOOT=0
NETWORK_NEED_RESTART=0

DES_DIR=""
ISSUE=""
ISSUE_VERSION=""
major_version=""
minor_version=""

ERROR_NOT_ROOT_ACCOUNT=100     # not root account
ERROR_INVALID_COMMAND=101      # invalid command
ERROR_INIT_HOME=102            # cannot create directory
ERROR_NOT_DHCP_CLIENT=103      # not install dhcp client
ERROR_REMOVE_FILE=104          # remove file or path failed
ERROR_NOT_LOG_FILE=105         # not found log file
ERROR_FUNC_RUNNING_ERROR=106   # the function running error
ERROR_NOT_SUPPORT_VERSION=107  # not support system issue
ERROR_FORCE_DELETE_FILE=108    # delete file fail
ERROR_CHANGE_PROC=109          # change proc fail

ERROR_UNKNOWN=200              # unknown error

LOG_LEVEL_ERROR="Error"
LOG_LEVEL_INFO="Info"
LOG_LEVEL_WARN="Warn"
LOG_LEVEL_DEBUG="Debug"

function _is_root() {
    if [ `id -u` -ne 0 ]; then
        return 1
    fi
}

function _startswith() {
    local _str="$1"
    local _sub="$2"
    echo "$_str" | grep "^$_sub" >/dev/null 2>&1
}

function version() {
    echo "$PROJECT"
    echo "v$VER"
}

function showhelp() {
    version
    echo "Usage: $PROJECT  command ...[parameters]....
Commands:
    --help, -h, -V           Show this help message.
    --version, -v, -V        Show version info.
    --enable                 Enable ipv6.
    --disable                Disable ipv6.
    "
}

function _init_home() {
    local _ret1
    local _ret2

    mkdir -p "$SCRIPT_LOG_SAVE_PATH" 1>/dev/null 2>/dev/null
    _ret1=$?
    mkdir -p "$SYSCTL_CONF_PATH"
    _ret2=$?

    if [ "$_ret1" -ne 0 -o "$_ret2" -ne 0 ]; then
        echo "cannot create directory $SCRIPT_LOG_SAVE_PATH and $SYSCTL_CONF_PATH"
        exit $ERROR_INIT_HOME
    fi
}

_get_date() {
    date +"%Y-%m-%d %H:%M:%S"
}

function _check_rpm_dhclient () {
    # check dhclient software
    rpm -qa | grep -E "dhclient|dhcp-client" 1>/dev/null 2>/dev/null
    if [ "$?" -ne "0" ]; then
        _log_error "dhclient or dhcp-client(centos 8) not installed"
        exit $ERROR_NOT_DHCP_CLIENT
    fi
}

function _check_deb_dhclient () {
    # check isc-dhcp-client software
    dpkg -l | grep "isc-dhcp-client" 1>/dev/null 2>/dev/null
    if [ "$?" -ne "0" ]; then
        _log_error "isc-dhcp-client not installed"
        exit $ERROR_NOT_DHCP_CLIENT
    fi
}

# check software isc-dhcp-client(ubuntu debian) dhclient(fedora centos)
function _check_dhcp_client () {
    if [[ "$ISSUE" == "centos" ]]; then
        _check_rpm_dhclient
    elif [[ "$ISSUE" == "fedora" ]]; then
        _check_rpm_dhclient
    elif [[ "$ISSUE" == "debian" ]]; then
        _check_deb_dhclient
    elif [[ "$ISSUE" == "ubuntu" ]]; then
        _check_deb_dhclient
    else
        _log_warn "The $ISSUE not in [centos, fedora, debian, ubuntu], do not support automatic check, please manually check the dhcp client software is installed."
        showhelp
        exit $ERROR_NOT_SUPPORT_VERSION
    fi
}

function _modify_file_grub2_config() {
    local _target_config_file=$1
    local _target_info=$2
    local _config_action=$3

    grep -n -E 'linux[16]*[[:space:]]+(/boot)?/vmlinuz-.*root=.*' "$_target_config_file" | \
    while read _line; do
        if [[ "$_config_action" == "add" ]]; then
            if ! echo "$_line" | grep -q "$_target_info"; then
                _line_no=${_line%%:*}
                chmod 600 "$_target_config_file"
                sed -i "$_line_no s/$/ $_target_info/" "$_target_config_file"
            fi
        elif [[ "$_config_action" == "delete" ]]; then
            if echo "$_line" | grep -q "$_target_info"; then
                _line_no=${_line%%:*}
                chmod 600 "$_target_config_file"
                sed -i "$_line_no s/$_target_info//" "$_target_config_file"
            fi
        fi
    done
}

function _modify_default_grub() {
    local _kernel_boot_parameter=$1
    local _config_action=$2
    local _default_config_grub_file=/etc/default/grub

    # file does not exist
    [[ ! -f $_default_config_grub_file ]] && return

    if [[ "$_config_action" == "add" ]]; then
        if ! grep -q 'GRUB_CMDLINE_LINUX=' $_default_config_grub_file; then
            echo "GRUB_CMDLINE_LINUX=\" $_kernel_boot_parameter\"" >> $_default_config_grub_file
        else
            if ! grep -q "GRUB_CMDLINE_LINUX=\".*$_kernel_boot_parameter" $_default_config_grub_file; then
                sed -i "/GRUB_CMDLINE_LINUX=\"/s/\"$/ $_kernel_boot_parameter\"/" $_default_config_grub_file
            fi
        fi
    elif [[ "$_config_action" == "delete" ]]; then
        if grep -q "GRUB_CMDLINE_LINUX=\".*$_kernel_boot_parameter" $_default_config_grub_file; then
            sed -i "/GRUB_CMDLINE_LINUX=/s/$_kernel_boot_parameter//" $_default_config_grub_file
        fi
    fi
}

function _modify_grub_config_file() {
    local _config_info=$1
    local _config_action=$2
    local _debian_grub2_cfg_file="/boot/grub/grub.cfg"
    local _rhel_grub2_cfg_file="/boot/grub2/grub.cfg"

    # Ubuntu >= 9.10 or Debian
    if [[ -f $_debian_grub2_cfg_file ]]; then
        _modify_file_grub2_config $_debian_grub2_cfg_file "$_config_info" "$_config_action"
    fi

    # CentOS 7 or Fedora >= 16
    if [[ -f $_rhel_grub2_cfg_file ]]; then
        _modify_file_grub2_config $_rhel_grub2_cfg_file "$_config_info" "$_config_action"
    fi
}


function _printargs() {
    if [ -z "$2" ]; then
        printf -- "%s" "$1"
    else
        echo "Error: Too many parameters of function _printargs"
        exit $ERROR_UNKNOWN
    fi
    printf "\n"
}

function _log_info() {
    local _log_message="$1"
    local _log_level="$LOG_LEVEL_INFO"

    local _all_log_message="[$(_get_date)] [$_log_level ] [$_log_message]"
    _log_writer "$_all_log_message"
    _log_print "$_all_log_message"
}

function _log_warn() {
    local _log_message="$1"
    local _log_level="$LOG_LEVEL_WARN"

    local _all_log_message="[$(_get_date)] [$_log_level ] [$_log_message]"
    _log_writer "$_all_log_message"
    _log_print "$_all_log_message"
}

function _log_debug() {
    local _log_message="$1"
    local _log_level="$LOG_LEVEL_DEBUG"

    local _all_log_message="[$(_get_date)] [$_log_level] [$_log_message]"
    _log_writer "$_all_log_message"
}

function _log_error() {
    local _log_message="$1"
    local _log_level="$LOG_LEVEL_ERROR"

    local _all_log_message="[$(_get_date)] [$_log_level] [$_log_message]"
    _log_writer "$_all_log_message"
    _log_print "$_all_log_message"
}

function _log_print () {
    local _all_log_message=$1

    # print log
    echo -e "$_all_log_message"
}

function _log_writer () {
    local _all_log_message=$1

    [ -z "$SCRIPT_LOG_FILE" ] && exit $ERROR_NOT_LOG_FILE
    # write log to file
    _printargs "$_all_log_message" >>"$SCRIPT_LOG_FILE"
}

function _is_digit()
{
    local _string=$1
    _ret=`expr match $_string "[0-9]*$"`
    if [ ${_ret} -gt 0 ]; then
        echo 1     # is digit
    else
        echo 0     # not is digit
    fi
}

# this function support the centos7, not need modify
function _get_centos()
{
    for i in issue issue.net centos-release redhat-release ; do
        [[ ! -f $DES_DIR/etc/$i ]] && continue
        grep -i -q 'CentOS' $DES_DIR/etc/$i
        if [[ $? -eq 0 ]]; then
            vm_os_distro='centos'
            get_issue_ok_file=$DES_DIR/etc/$i
            return
        fi
    done

    if [  "$get_issue_ok_file" == "" ]; then
        if [ -f $DES_DIR/boot/grub/menu.lst ]; then
            _default=`cat $DES_DIR/boot/grub/menu.lst | grep "^default[ =][0-9]\+" | awk -F' |=' '{print $NF}'`
            let _default=$_default+1
            
            cat $DES_DIR/boot/grub/menu.lst | grep ^title | head -n $_default | tail -n 1 | grep -i "CentOS" 1>/dev/null 2>&1      
            _return_value=$?
            
            if [ $_return_value -ne 1 ]; then
                vm_os_distro="centos"
                return
            fi
        fi
    fi
}

function _get_ubuntu()
{
    grep -i "Ubuntu" $DES_DIR/etc/issue >& /dev/null
    if [[ $? == 0 ]]; then
        vm_os_distro='ubuntu'
        get_issue_ok_file="$DES_DIR/etc/issue"
        return
    fi
    if [ -f $DES_DIR/etc/lsb-release ]; then
        cat $DES_DIR/etc/lsb-release | grep -i "Ubuntu" 1>/dev/null  2>&1
        _return_value=$?
        if [ $_return_value -ne 1 ]; then
            vm_os_distro="ubuntu"
            get_issue_ok_file="$DES_DIR/etc/lsb-release"
            return
        fi
    fi

    if [  "$get_issue_ok_file" == "" ]; then
       if [ -f $DES_DIR/boot/grub/grub.cfg ]; then
            _default=`cat $DES_DIR/boot/grub/grub.cfg | grep default  | grep -v -E "^#|next_entry" | grep -i "default=.*"| awk -F '=' {'print $2'} | sed "s/\"//g"` 2>/dev/null
            let _default=$_default+1
            
            cat $DES_DIR/boot/grub/grub.cfg | grep ^title | head -n $_default | tail -n 1 | grep -i "Ubuntu" 1>/dev/null 2>&1      
            _return_value=$?
            
            if [ $_return_value -ne 1 ]; then
                vm_os_distro="ubuntu"
                return
            fi
       fi
    fi
}

function _get_debian()
{
    for i in 'issue' 'issue.net' 'os-release'; do
        if grep -i -q 'Debian' $DES_DIR/etc/$i >& /dev/null ; then
            vm_os_distro='debian'
            get_issue_ok_file="$DES_DIR/etc/$i"
            return
        fi
	done
    if [ -f  $DES_DIR/etc/lsb-release ]; then
        cat $DES_DIR/etc/lsb-release | grep -i "Debian" 1>/dev/null  2>&1
        _return_value=$?
        if [ $_return_value -ne 1 ]; then
            vm_os_distro="debian"
            get_issue_ok_file="$DES_DIR/etc/lsb-release"
            return
        fi
    fi
    
    if [  "$get_issue_ok_file" == "" ]; then
        if [  -f $DES_DIR/boot/grub/grub.cfg ]; then
            _default=`cat $DES_DIR/boot/grub/grub.cfg | grep default  | grep -v -E "^#|next_entry" | grep -i "default=.*"| awk -F '=' {'print $2'} | sed "s/\"//g"` 2>/dev/null
            let _default=$_default+1
        
            cat $DES_DIR/boot/grub/grub.cfg | grep ^title | head -n $_default | tail -n 1 | grep -i "Debian" 1>/dev/null 2>&1      
            _return_value=$?
        
            if [ $_return_value -ne 1 ]; then
                vm_os_distro="debian"
                return
            fi
        fi
    fi

}

function _get_fedora()
{
    for i in issue issue.net redhat-release ; do
        [[ ! -f $DES_DIR/etc/$i ]] && continue
        grep -i -q 'Fedora' $DES_DIR/etc/$i
        if [[ $? -eq 0 ]]; then
            vm_os_distro='fedora'
            get_issue_ok_file=$DES_DIR/etc/$i
            return
        fi
    done

}


function _get_distro()
{
    vm_os_distro=""
    get_issue_ok_file=""

func_list=(
_get_centos
_get_fedora
_get_ubuntu
_get_debian
)
	for f in ${func_list[@]}; do
		$f
		[[ -n $vm_os_distro ]] && return
	done
}

# this function support the centos7, not need modify
function _get_redhat_centos_version()
{

    if [  "$ISSUE" == "redhat" -o "$ISSUE" == "centos" ]; then
        if [ -f $DES_DIR/etc/redhat-release ]; then
            major_version=`cat $DES_DIR/etc/redhat-release | head -n 1 | awk -F'.' {'print $1'} | awk {'print $NF'}`
            minor_version=`cat $DES_DIR/etc/redhat-release | head -n 1 | awk -F'.' {'print $2'} | awk {'print $1'}`
            if [ "$major_version" != "" ]; then
                _return_major_version=$(_is_digit $major_version)
                if [ $_return_major_version -ne 1 ]; then
                    major_is_ok=0
                else
                    major_is_ok=1
                fi
            else
                _return_major_version=0
                major_is_ok=0
            fi
            if [ "$minor_version" != "" ]; then
                _return_minor_version=$(_is_digit $minor_version)
                if [ $_return_minor_version -ne 1 ]; then
                   minor_is_ok=0
                else
                   minor_is_ok=1
                fi
            else
                _return_minor_version=0
                minor_is_ok=0
            fi
            if [ $_return_major_version -eq 1 -a $_return_minor_version -eq 1 ]; then
                ISSUE_VERSION="$major_version"."$minor_version"
                return
            fi
        fi
    fi
}

function _get_ubuntu_version()
{
    if [  "$ISSUE" == "ubuntu" ]; then
        if [ -f $DES_DIR/etc/lsb-release ]; then
            major_version=`cat $DES_DIR/etc/lsb-release | grep DISTRIB_RELEASE | awk -F'=' {'print $2'} | awk -F'.' {'print $1'} | awk {'print $NF'}`
        
            minor_version=`cat $DES_DIR/etc/lsb-release | grep DISTRIB_RELEASE | awk -F'=' {'print $2'} | awk -F'.' {'print $2'} | awk {'print $1'}`
            if [ "$major_version" != "" ]; then
                _return_major_version=$(_is_digit $major_version)
                if [ $_return_major_version -ne 1 ]; then
                    major_is_ok=0
                else
                    major_is_ok=1
                fi
            else
               _return_major_version=0
               major_is_ok=0
            fi
            if [ "$minor_version" != "" ]; then
                _return_minor_version=$(_is_digit $minor_version)
                if [ $_return_minor_version -ne 1 ]; then
                    minor_is_ok=0
                else
                    minor_is_ok=1
                fi   
            else
                _return_minor_version=0
                minor_is_ok=0
            fi
            if [ $_return_major_version -eq 1 -a $_return_minor_version -eq 1 ]; then
                ISSUE_VERSION="$major_version"."$minor_version"
                return
            fi
        fi
    fi
}

function _get_debian_version()
{
    if [ "$ISSUE" == "debian" ]; then
        if [ -f $DES_DIR/etc/debian_version ]; then
            major_version=`cat $DES_DIR/etc/debian_version | awk -F'.' {'print $1'}`
            minor_version=`cat $DES_DIR/etc/debian_version | awk -F'.' {'print $2'}`
            
            if [ "$major_version" != "" ]; then
                _return_major_version=$(_is_digit $major_version)
                if [ $_return_major_version -ne 1 ]; then
                    major_is_ok=0
                else
                    major_is_ok=1
                fi
            else
                _return_major_version=0
                major_is_ok=0
            fi
            if [ "$minor_version" != "" ]; then
                _return_minor_version=$(_is_digit $minor_version)
                if [ $_return_minor_version -ne 1 ]; then
                    minor_is_ok=0
                else
                    minor_is_ok=1
                fi
            else
                _return_minor_version=0
                minor_is_ok=0
            fi
            if [ $_return_major_version -eq 1 -a $_return_minor_version -eq 1 ]; then
                ISSUE_VERSION="$major_version"."$minor_version"
                return
            fi
        elif [[ -f $DES_DIR/etc/os-release ]]; then
            major_version=$(awk -F'=' '/VERSION_ID/ {print $2}' $DES_DIR/etc/os-release | sed 's/"//g')
			if [[ $(_is_digit $major_version) == '1' ]]; then
				major_is_ok=1
				return
			fi
        else
			for i in 'issue' 'issue.net' ; do
				major_version=$(grep -E 'Debian.* [[:digit:]]' -o $DES_DIR/etc/$i | awk '{print $NF}')
				if [[ $(_is_digit $major_version) == '1' ]]; then
					major_is_ok=1
					return
				fi
			done
        fi
    fi
}

function _get_fedora_version()
{
    if [  "$ISSUE" == "fedora" ]; then
        if [ -f $DES_DIR/etc/redhat-release ]; then
            major_version=`cat $DES_DIR/etc/redhat-release | head -n 1 | awk -F' ' {'print $3'} | awk {'print $NF'}`
            if [ "$major_version" != "" ]; then
                _return_major_version=$(_is_digit $major_version)
                if [ $_return_major_version -ne 1 ]; then
                    major_is_ok=0
                else
                    major_is_ok=1
                fi
            else
                _return_major_version=0
                major_is_ok=0
            fi
            if [ $_return_major_version -eq 1 ]; then
                ISSUE_VERSION="$major_version"
                return
            fi
        fi
    fi
}



function _get_major_and_minor_version()
{
    major_version=""
    minor_version=""
    major_is_ok=1
    minor_is_ok=1
    if [ "$get_issue_ok_file" == "" ]; then
        return 1
    fi

    if [ "$get_issue_ok_file" != "" -a "$ISSUE" == "fedora" ]; then
        _get_fedora_version
       
        if [ $major_is_ok -eq 0 ]; then
            return 1
        fi
        return
    fi

    if [ "$get_issue_ok_file" == "$DES_DIR/etc/issue" ]; then
        major_version=`cat $get_issue_ok_file | head -n 1 | awk -F'.' {'print $1'} | awk {'print $NF'}`
        minor_version=`cat $get_issue_ok_file | head -n 1 | awk -F'.' {'print $2'} | awk {'print $1'}`
        if [ "$major_version" != "" ]; then
            _return_major_version=$(_is_digit $major_version)
            if [ "$minor_version" != "" ]; then
                _return_minor_version=$(_is_digit $minor_version)
                if [ $_return_minor_version -ne 1 ]; then
                    minor_is_ok=0
                fi
            else
                _return_minor_version=0
                minor_is_ok=0
            fi
                
            if [ $_return_major_version -ne 1 ]; then
                major_is_ok=0
            fi

            if [ $_return_major_version -eq 0 -o $_return_minor_version -eq 0 ]; then
                _get_redhat_centos_version

                _get_ubuntu_version

                _get_debian_version

            else
                ISSUE_VERSION="$major_version"."$minor_version"
                return
            fi
                
        fi
    else
        _get_redhat_centos_version

        _get_ubuntu_version

        #_get_debian_version

    fi
    
    if [ "$ISSUE" == "redhat" ]; then
        if [ $major_is_ok -eq 0 ]; then
            return 1
        fi
    fi
       
    if [ "$ISSUE" == "centos" ]; then
        if [ $major_is_ok -eq 0 ]; then
            return 1
        fi
    fi

    if [ "$ISSUE" == "debian" ]; then
        if [ $major_is_ok -eq 0 ]; then
            return 1
        fi
    fi
        
}

function _convert_issue_to_lower() {
    ISSUE=$vm_os_distro
    ISSUE=$(echo $ISSUE | tr 'A-Z' 'a-z')
}

function _convert_version_to_lower() {
    ISSUE_VERSION=$ISSUE_VERSION
    ISSUE_VERSION=$(echo $ISSUE_VERSION | tr 'A-Z' 'a-z')
}

function _modify_ipv6_config_from_grub() {
    local _config_info=$1
    local _config_action=$2

    _modify_default_grub "$_config_info" "$_config_action"
    _modify_grub_config_file "$_config_info" "$_config_action"
}

function _remove_ipv6_config_from_grub() {
    local _config_info='ipv6.disable=1'
    for i in "$_config_info"
    do
        _config_grub "$i" "delete"
    done
}

function _force_delete_file () {
    local _file_path=$1

    rm -f $_file_path
    if [ "$?" -ne "0" ]; then
        _log_error "remove $_file_path failed"
        exit $ERROR_FORCE_DELETE_FILE
    fi
}

function _check_ipv6_module() {
    local _boot_kernel_config_file="/boot/config-$(uname -r)"

    if [[ $SYSTEM_NEED_REBOOT -eq 0 ]]; then
        if grep -q "CONFIG_IPV6=m" $_boot_kernel_config_file; then
            # reload ipv6 module
            modprobe -r ipv6 2>/dev/null 1>/dev/null
            modprobe ipv6 2>/dev/null 1>/dev/null

            ipv6_module_arg=$(lsmod | grep "^ipv6" | awk '{print $3}')
            # check if ipv6 module arg is invalid
            if [[ "$ipv6_module_arg" -eq 0 ]]; then
                FUNCTION_RETURN_VALUE=1
            fi
            _check_return_record_log "check ipv6 modules"
        fi
    fi
}


function _delete_modprobed_disable_conf() {
    local _disable_ipv6_file=/etc/modprobe.d/disable_ipv6.conf

    if [ -f "$_disable_ipv6_file" ]; then
        _force_delete_file $_disable_ipv6_file
        FUNCTION_RETURN_VALUE=$?
        _check_return_record_log "force delete file $_disable_ipv6_file"

        # CentOS 6 not need reboot
        # SYSTEM_NEED_REBOOT=1
    fi
}

function _del_line_config_file() {
    local _file=$1
    local _target=$2

    if grep -i "^${_target}[[:space:]]*=" "$_file" &>/dev/null; then
        sed -i "/^${_target}[[:space:]]*=.*/d" "$_file"
    fi
}

function _modify_config_file() {
    local _file=$1
    local _key=$2
    local _value=$3

    # modify "key"="value" of file
    if ! grep -i "^${_key}[[:space:]]*=" "$_file" &>/dev/null; then
        echo "$_key=$_value" >> "$_file"
    else
        _value=${_value//\//\\/}
        sed -i "s/^${_key}[[:space:]]*=.*/$_key=$_value/" "$_file"
    fi
}

function _convert_proc_to()
{
    local _proc_sys_ipv6="/proc/sys/net/ipv6/"
    local _action="$1"

    if [ "$_action" == "enable" ]; then
        # enable ipv6
        for i in `find $_proc_sys_ipv6 -name '*disable*'`; do echo 0 > $i; done 2>/dev/null
        if [ "$?" -ne 0 ]; then
            _log_error "Fail: config enable ipv6. "
            exit $ERROR_CHANGE_PROC
        fi
    elif [ "$_action" == "disable" ]; then
        # disblae ipv6
        for i in `find $_proc_sys_ipv6 -name '*disable*'`; do echo 1 > $i; done 2>/dev/null
        if [ "$?" -ne 0 ]; then
            _log_error "Fail: config disable ipv6. "
            exit $ERROR_CHANGE_PROC
        fi
    fi
}

function _check_interfaces_sysctl() {
    local _action=$1
    local _interfaces=""
    _interfaces=$(_get_vm_interfaces)

    if [ "$_action" == "enable" ]; then
        for interface in $_interfaces
        do
            _log_info "try to search sysctl config, check $interface"
            grep -r "net.ipv6.conf.$interface.disable_ipv6" /etc/sysctl* 2>/dev/null | awk -F'[=]' '{print $2}' | grep 0 2>/dev/null 1>/dev/null
            if [ "$?" -eq 0 ]; then
                _log_warn "find 'net.ipv6.conf.$interface.disable_ipv6 = 0' for enable ipv6 of $interface, please disable it"
            fi
        done
    elif [ "$_action" == "disable" ]; then
        for interface in $_interfaces
        do
            _log_info "try to search sysctl config, check $interface"
            grep -r "net.ipv6.conf.$interface.disable_ipv6" /etc/sysctl* 2>/dev/null | awk -F'[=]' '{print $2}' | grep 1 2>/dev/null 1>/dev/null
            if [ "$?" -eq 0 ]; then
                _log_warn "find 'net.ipv6.conf.$interface.disable_ipv6 = 1' for disable ipv6 of $interface, please enable it"
            fi
        done
    fi
}

function _enable_sysctl_conf() {
    # modify /etc/sysctl.d/100-sysctl.conf
    local _sysctl_conf_file=$SYSCTL_CONF_FILE
    local _sysctl_net_dev_conf_all="net.ipv6.conf.all.disable_ipv6"
    local _sysctl_net_dev_conf_default="net.ipv6.conf.default.disable_ipv6"
    local _sysctl_net_dev_conf_lo="net.ipv6.conf.lo.disable_ipv6"

    _modify_config_file "$_sysctl_conf_file" $_sysctl_net_dev_conf_all 0
    FUNCTION_RETURN_VALUE=$?
    _check_return_record_log "modify $_sysctl_net_dev_conf_all=0 to $_sysctl_conf_file"
    _modify_config_file "$_sysctl_conf_file" $_sysctl_net_dev_conf_default 0
    FUNCTION_RETURN_VALUE=$?
    _check_return_record_log "modify $_sysctl_net_dev_conf_default=0 to $_sysctl_conf_file"
    _modify_config_file "$_sysctl_conf_file" $_sysctl_net_dev_conf_lo 0
    FUNCTION_RETURN_VALUE=$?
    _check_return_record_log "modify $_sysctl_net_dev_conf_lo=0 to $_sysctl_conf_file"

    _convert_proc_to "enable"
    if [[ $SYSTEM_NEED_REBOOT -eq 0 ]]; then
        sysctl -a 2>/dev/null | grep ipv6 | grep disable 2>/dev/null 1>/dev/null
        FUNCTION_RETURN_VALUE=$?
        _check_return_record_log "update file config information of $_sysctl_conf_file"
    fi
    _check_interfaces_sysctl "disable"
}

function _enable_ipv6_sys_conf() {
    # delete modprobe disable ipv6 config file
    # no use modprobe to disable ipv6
    _delete_modprobed_disable_conf

    # check ipv6 modules
    _check_ipv6_module

    # convert ipv6 /etc/sysctl.d/100-sysctl.conf to enable
    _enable_sysctl_conf
}

function _check_inet6_all_interfaces() {
    if ! ip addr | grep -q inet6; then
        FUNCTION_RETURN_VALUE=1
    fi
    _check_return_record_log "check inet6 of interfaces use 'ip addr'"
}

function _modify_grub_enable_ipv6() {
    # check if need to del grub ipv6 disable
    _delete_config_info_of_grub

    _enable_ipv6_sys_conf

    NETWORK_NEED_RESTART=1
    if [[ $SYSTEM_NEED_REBOOT -eq 0 ]]; then
        # if ipv6 is enable, must can find local inet6 address, check inet6
        _check_inet6_all_interfaces
    fi
}

function _print_ok() {
    local _log_message=""

    _log_message="Succeed: $1"
    _log_debug "$_log_message"
}

function _print_error_and_die() {
    local _log_message=""

    _log_message="Failed: $1"
    _log_error "$_log_message"
    exit $ERROR_FUNC_RUNNING_ERROR
}

function _check_return_record_log() {
    if [ $FUNCTION_RETURN_VALUE -eq 0 ]; then
        _print_ok "$1"
    else
        _print_error_and_die "$1"
    fi
}

function _is_grub_config_infod() {
    local _cmdline_file="/proc/cmdline"
    if grep -q "ipv6.disable=1" $_cmdline_file; then
        echo "1"
    else
        echo "0"
    fi
}

function _delete_config_info_of_grub() {
    # check if need to delete grub ipv6 disable
    local _ipv6_is_disabled=""

    _ipv6_is_disabled=$(_is_grub_config_infod)
    if [ "$_ipv6_is_disabled" == "1" ]; then
        #_remove_ipv6_config_from_grub
        _modify_ipv6_config_from_grub "ipv6.disable=1" "delete"
        FUNCTION_RETURN_VALUE=$?
        SYSTEM_NEED_REBOOT=1
        _check_return_record_log "delete grub ipv6 disable"
    fi
}


function _disable_ip6tables () {
    which systemctl 1>/dev/null 2>/dev/null
    if [ "$?" -eq 0 ]; then
        systemctl status ip6tables 1>/dev/null 2>/dev/null
        if [ "$?" -ne 0 ]; then
            _log_info "not found service ip6tables"
            return
        fi

        systemctl stop ip6tables 1>/dev/null 2>/dev/null
        if [ "$?" -ne 0 ]; then
            _log_warn "fail systemctl stop ip6tables"
        fi
        systemctl disable ip6tables 1>/dev/null 2>/dev/null
        if [ "$?" -ne 0 ]; then
            _log_warn "fail systemctl disable ip6tables"
        fi
    elif which chkconfig 1>/dev/null 2>/dev/null; then
        service ip6tables stop 1>/dev/null 2>/dev/null
        if [ "$?" -ne 0 ]; then
            _log_warn "fail service ip6tables stop"
        fi
        chkconfig ip6tables off 1>/dev/null 2>/dev/null
        if [ "$?" -ne 0 ]; then
            _log_warn "fail chkconfig ip6tables off"
        fi
    fi
}

#  line1 == AAAA  line2 == BBBB
#
#     AAAA
#     BBBB  yes
#
#     AAAA
#
#     BBBB  yes
#
#     AAAA
#     #
#     BBBB  yes
#
#     AAAA
#     hello
#     BBBB  no
function _file_lines_deletor() {
    local _file=$1
    local _line1=$2
    local _line2=$3

    local _line_ret=$(grep -n "^$_line1" "$_file" 2>/dev/null | head -n 1 2>/dev/null | awk -F: '{print $1}' 2>/dev/null) 
    sed -n "$_line_ret,$ p" "$_file" 2>/dev/null | grep -v "^$_line1" 2>/dev/null | sed 's/^[ \t]*//g;s/[ \t]*$//g;/^#/d;/^$/d' 2>/dev/null | head -n 1 2>/dev/null | grep "$_line2" 1>/dev/null 2>/dev/null 
    if [ "$?" -eq 0 ]; then
        sed -i "/^$_line1/,/$_line2/d" "$_file"
    else
        if grep -i "^$_line1" "$_file" &>/dev/null; then
            sed -i "/^$_line1.*/d" "$_file"
        fi
    fi
}

function _file_appent_line() {
    local _file=$1
    local _line=$2

    # append write a new line to file
    echo "$_line" >> "$_file"
}

function _modify_line_config_file() {
    local _file=$1
    local _left_value=$2
    local _option=$3

    # file does not exist
    [[ ! -f $_file ]] && return

    if [[ "$_option" == "add" ]]; then
        if ! grep -i "^$_left_value" "$_file" &>/dev/null; then
            # append write
            echo "$_left_value" >> "$_file"
        else
            # rewrite
            sed -i "s/^$_left_value.*/$_left_value/" "$_file"
        fi
    elif [[ "$_option" == "del" ]]; then
        if grep -i "^$_left_value" "$_file" &>/dev/null; then
            sed -i "/^$_left_value.*/d" "$_file"
        fi
    fi
}

# debian clean ipv6 conf
function _clean_device_dhcp_ipv6_debian() {
    # clean network device ipv6 config
    local _device=$1
    _log_info "Clean ipv6 config for device $_device ..."

    local _interfaces_file="/etc/network/interfaces.d/50-cloud-init.cfg"
    if [ -f "$_interfaces_file" ]; then
        _file_lines_deletor "$_interfaces_file" "iface $_device inet6 dhcp" "pre-up sleep 3"
        FUNCTION_RETURN_VALUE=$?
        _check_return_record_log "delete $_interfaces_file  double line  --> iface $_device inet6 dhcp and pre-up sleep 3 "
    else
        _log_warn "$_interfaces_file is not exist."
    fi

    NETWORK_NEED_RESTART=1
}


function _get_vm_interfaces() {
    local _interfaces=""
    local _net_dir="/sys/class/net/"

    if [ ! -d "$_net_dir" ];then
        echo ""
        return
    fi
    _interfaces=$(ls $_net_dir | grep '^eth[0-9]\+$')
    echo "$_interfaces"
}

### interface

function _config_device_dhcp_ipv6_rhel() {
    # config dev ipv6
    local _device=$1
    _log_info "Add ipv6 config for device $_device ..."

    local _etc_ifcfg_file="/etc/sysconfig/network-scripts/ifcfg-$_device"
    if [ ! -f "$_etc_ifcfg_file" ]; then
        touch "$_etc_ifcfg_file"
    fi
    _modify_config_file "$_etc_ifcfg_file" "IPV6INIT" yes
    FUNCTION_RETURN_VALUE=$?
    _check_return_record_log "modify IPV6INIT=yes to $_etc_ifcfg_file"

    _modify_config_file "$_etc_ifcfg_file" "DHCPV6C" yes
    FUNCTION_RETURN_VALUE=$?
    _check_return_record_log "modify DHCPV6C=yes to $_etc_ifcfg_file"

    _modify_config_file "$_etc_ifcfg_file" "IPV6_AUTOCONF" yes
    FUNCTION_RETURN_VALUE=$?
    _check_return_record_log "modify IPV6_AUTOCONF=yes to $_etc_ifcfg_file"

    _modify_config_file "$_etc_ifcfg_file" "IPV6_DEFROUTE" yes
    FUNCTION_RETURN_VALUE=$?
    _check_return_record_log "modify IPV6_DEFROUTE=yes to $_etc_ifcfg_file"

    NETWORK_NEED_RESTART=1
}


# debian config ipv6
function _config_device_dhcp_ipv6_debian() {
    # config dev ipv6
    local _device=$1
    _log_info "Add ipv6 config for device $_device ..."

    local _interfaces_file="/etc/network/interfaces.d/50-cloud-init.cfg"
    if [ ! -f "$_interfaces_file" ]; then
        touch "$_interfaces_file"
    fi

    # step 1 - delete ipv6 config
    _clean_device_dhcp_ipv6_debian $_device

    # step 2 - config ipv6 1
    _modify_line_config_file "$_interfaces_file" "auto $_device" "add"
    FUNCTION_RETURN_VALUE=$?
    _check_return_record_log "modify 'auto $_device' to $_interfaces_file"
    _file_appent_line "$_interfaces_file" "iface $_device inet6 dhcp"
    FUNCTION_RETURN_VALUE=$?
    _check_return_record_log "appent line 'iface $_device inet6 dhcp' to $_interfaces_file"

    # step 3 - config ipv6 2
    _file_appent_line "$_interfaces_file" "    pre-up sleep 3"
    FUNCTION_RETURN_VALUE=$?
    _check_return_record_log "appent line '    pre-up sleep 3' to $_interfaces_file"

    NETWORK_NEED_RESTART=1
}

# ubuntu config ipv6
function _config_device_dhcp_ipv6_ubuntu() {
    # config dev ipv6
    local _device=$1
    _log_info "Add ipv6 config for device $_device ..."

    local _interfaces_file="/etc/network/interfaces.d/50-cloud-init.cfg"
    if [ ! -f "$_interfaces_file" ]; then
        touch "$_interfaces_file"
    fi
    _modify_line_config_file "$_interfaces_file" "auto $_device" "add"
    FUNCTION_RETURN_VALUE=$?
    _check_return_record_log "modify 'auto $_device' to $_interfaces_file"
    _modify_line_config_file "$_interfaces_file" "iface $_device inet6 dhcp" "add"
    FUNCTION_RETURN_VALUE=$?
    _check_return_record_log "modify 'iface $_device inet6 dhcp' to $_interfaces_file"

    NETWORK_NEED_RESTART=1
}

function _add_ipv6_config_interface_device () {
    local _device=$1
    if [[ "$ISSUE" == "centos" ]]; then
        _config_device_dhcp_ipv6_rhel $_device
    elif [[ "$ISSUE" == "fedora" ]]; then
        _config_device_dhcp_ipv6_rhel $_device
    elif [[ "$ISSUE" == "debian" ]]; then
        _config_device_dhcp_ipv6_debian $_device
    elif [[ "$ISSUE" == "ubuntu" ]]; then
        _config_device_dhcp_ipv6_ubuntu $_device
    else
        _log_warn "The $ISSUE not in [centos, fedora, debian, ubuntu], please manually config $_device."
        showhelp
        exit $ERROR_NOT_SUPPORT_VERSION
    fi
}

function _config_interfaces () {
    local _action="$1"

    # config all interface devices ipv6 enable
    local _interfaces=""
    _interfaces=$(_get_vm_interfaces)

    if [ "$_action" == "add_ipv6" ]; then
        for interface in $_interfaces
        do
            _add_ipv6_config_interface_device "$interface"
        done
    elif [ "$_action" == "del_ipv6" ]; then
        for interface in $_interfaces
        do
            _del_ipv6_config_interface_device "$interface"
        done
    fi
}

function _restart_network_rhel () {
    local _net_file="/etc/init.d/network"

    if [ -f "$_net_file" ]; then
        $_net_file restart >> "$SCRIPT_LOG_FILE" 2>&1
    elif which systemctl > /dev/null 2>&1; then
        if [ "$ISSUE" == "centos" -a "$major_version" == "8" ]; then
            systemctl restart NetworkManager >> "$SCRIPT_LOG_FILE" 2>&1
            return
        fi

        systemctl restart network >> "$SCRIPT_LOG_FILE" 2>&1
    else
        service network restart >> "$SCRIPT_LOG_FILE" 2>&1
    fi
}

function _restart_network_debian () {
    local _target=$1
    local _restart_net_ret=1
    local _action_ret_ubuntu14=0

    # only ubuntu 14
    if [ "$ISSUE" == "ubuntu" -a "$ISSUE_VERSION" == "14.04" ]; then
        ifdown --all >> "$SCRIPT_LOG_FILE" 2>&1 && ifup --all >> "$SCRIPT_LOG_FILE" 2>&1
        _restart_net_ret=$?
        _action_ret_ubuntu14=$_restart_net_ret
        if [ "$_action_ret_ubuntu14" -ne 0 ]; then
            _log_error "restart network failed"
            #exit 1
        fi
        if [ $_restart_net_ret -eq 0 ]; then
            echo "$_restart_net_ret"
            return
        fi
    fi

    local _net_file="/etc/init.d/networking"
    if [ -f "$_net_file" ]; then
        $_net_file restart >> "$SCRIPT_LOG_FILE" 2>&1
        _restart_net_ret=$?
    elif which systemctl > /dev/null 2>&1; then
        systemctl restart networking >> "$SCRIPT_LOG_FILE" 2>&1
        _restart_net_ret=$?
    else
        service networking restart >> "$SCRIPT_LOG_FILE" 2>&1
        _restart_net_ret=$?
    fi
    if [ $_restart_net_ret -eq 0 ]; then
        # restart network success
        return
    fi

    # restart network error
    local _interfaces=""
    _interfaces=$(_get_vm_interfaces)
    if [ -z "$_interfaces" ];then
        FUNCTION_RETURN_VALUE=1
    fi
    _check_return_record_log "get interfaces of vm"

    if [ "$_action" == "disable" ]; then
        for interface in $_interfaces
        do
            (ifconfig "$interface" down >> "$SCRIPT_LOG_FILE" 2>&1) && (ifconfig "$interface" up >> "$SCRIPT_LOG_FILE" 2>&1)
        done
    fi
}

function _restart_network () {
    local _action=$1

    _log_info "Restart network for $_action ipv6 ... "
    if [[ "$ISSUE" == "centos" ]]; then
        _restart_network_rhel
    elif [[ "$ISSUE" == "fedora" ]]; then
        _restart_network_rhel
    elif [[ "$ISSUE" == "debian" ]]; then
        _restart_network_debian $_action
    elif [[ "$ISSUE" == "ubuntu" ]]; then
        _restart_network_debian $_action
    else
        _log_warn "The $ISSUE not in [centos, fedora, debian, ubuntu], do not support automatic restart network service, please manually restart network service."
        showhelp
        exit $ERROR_NOT_SUPPORT_VERSION
    fi
}

function _dhclient_get_ip6_addr() {
    which dhclient 1>/dev/null 2>/dev/null
    if [ "$?" -eq 0 ]; then
        _log_warn "Even if the script restarts the network and does not get the ipv6 address, we will execute 'dhclient -6 &' again, try to get the available address. If you still find that the network card does not have a normal ipv6 address, please try to update the dhcp client software."
        dhclient -6 & 1>/dev/null 2>/dev/null
    fi
}

function _switch_network_ipv6_to() {
    if [[ $NETWORK_NEED_RESTART -eq 1 ]]; then
        # restart network card
        _restart_network "$1"
        FUNCTION_RETURN_VALUE=$?
        _check_return_record_log "restart network device"

        ip addr show | egrep "inet6 .* global" 1>/dev/null 2>/dev/null
        if [ "$?" -ne 0 -a "$1" == "enable" ]; then
            # no global addr and option==Enable
            _dhclient_get_ip6_addr
        fi

        # if inet6 exsits after disabled ipv6, need to reboot.
        if [ "$1" == "disable" ]; then
            if ip addr | grep -q inet6; then
                SYSTEM_NEED_REBOOT=1
            fi
        fi
    fi
    # check if need to reboot
    if [[ $SYSTEM_NEED_REBOOT -eq 1 ]]; then
        _log_warn "IPv6 config $1 finished, need to reboot."
    else
        _log_info "IPv6 config $1 finished."
    fi
}

function enable_ipv6() {
    _log_info "Begin enable ipv6 ..."
    _disable_ip6tables
    _modify_grub_enable_ipv6
    _config_interfaces "add_ipv6"
    _switch_network_ipv6_to "enable"
}

function _disable_sysctl_conf() {
    # check /etc/sysctl.d/100-sysctl.conf
    local _sysctl_conf_file=$SYSCTL_CONF_FILE
    local _sysctl_net_dev_conf_all="net.ipv6.conf.all.disable_ipv6"
    local _sysctl_net_dev_conf_default="net.ipv6.conf.default.disable_ipv6"
    local _sysctl_net_dev_conf_lo="net.ipv6.conf.lo.disable_ipv6"

    _modify_config_file "$_sysctl_conf_file" $_sysctl_net_dev_conf_all 1
    FUNCTION_RETURN_VALUE=$?
    _check_return_record_log "modify $_sysctl_net_dev_conf_all=1 to $_sysctl_conf_file"
    _modify_config_file "$_sysctl_conf_file" $_sysctl_net_dev_conf_default 1
    FUNCTION_RETURN_VALUE=$?
    _check_return_record_log "modify $_sysctl_net_dev_conf_default=1 to $_sysctl_conf_file"
    _modify_config_file "$_sysctl_conf_file" $_sysctl_net_dev_conf_lo 1
    FUNCTION_RETURN_VALUE=$?
    _check_return_record_log "modify $_sysctl_net_dev_conf_lo=1 to $_sysctl_conf_file"

    _convert_proc_to "disable"
}

function _disable_ipv6_sys_conf () {
    # delete modprobe disable ipv6 config file
    # no use modprobe to disable ipv6
    _delete_modprobed_disable_conf

    _delete_config_info_of_grub

    # convert ipv6 /etc/sysctl.d/100-sysctl.conf to disable
    _disable_sysctl_conf
}

# rhel clean ipv6 conf
function _clean_device_dhcp_ipv6_rhel() {
    local _device=$1
    local _etc_ifcfg_file="/etc/sysconfig/network-scripts/ifcfg-$_device"
    _log_info "Clean ipv6 config for device $_device ..."

    if [ -f "$_etc_ifcfg_file" ]; then
        _del_line_config_file "$_etc_ifcfg_file" "IPV6INIT"
        _del_line_config_file "$_etc_ifcfg_file" "DHCPV6C"
        _del_line_config_file "$_etc_ifcfg_file" "IPV6_AUTOCONF"
        _del_line_config_file "$_etc_ifcfg_file" "IPV6_DEFROUTE"
    else
        _log_warn "$_etc_ifcfg_file is not exist."
    fi
}

# ubuntu clean ipv6 conf
function _clean_device_dhcp_ipv6_ubuntu() {
    # clean device ipv6
    local _device=$1
    _log_info "Clean ipv6 config for device $_device ..."

    local _interfaces_file="/etc/network/interfaces.d/50-cloud-init.cfg"
    if [ -f "$_interfaces_file" ]; then
        _modify_line_config_file "$_interfaces_file" "iface $_device inet6 dhcp" "del"
        FUNCTION_RETURN_VALUE=$?
        _check_return_record_log "remove line 'iface $_device inet6 dhcp' from $_interfaces_file"
    else
        _log_warn "$_interfaces_file is not exist."
    fi

    NETWORK_NEED_RESTART=1
}

function _del_ipv6_config_interface_device () {
    local _device=$1
    if [[ "$ISSUE" == "centos" ]]; then
        _clean_device_dhcp_ipv6_rhel $_device
    elif [[ "$ISSUE" == "fedora" ]]; then
        _clean_device_dhcp_ipv6_rhel $_device
    elif [[ "$ISSUE" == "debian" ]]; then
        _clean_device_dhcp_ipv6_debian $_device
    elif [[ "$ISSUE" == "ubuntu" ]]; then
        _clean_device_dhcp_ipv6_ubuntu $_device
    else
        _log_warn "The $ISSUE not in [centos, fedora, debian, ubuntu], please manually config $_device."
        showhelp
        exit $ERROR_NOT_SUPPORT_VERSION
    fi
}

function _inet_disable_ipv6() {
    # check inet6
    local _ip_addr_inet6=""
    _ip_addr_inet6=$(ip addr | grep inet6)
    if [ -n "$_ip_addr_inet6" ]; then
        # check if any global ipv6s are configed
        if echo "$_ip_addr_inet6" | grep -q global; then
            NETWORK_NEED_RESTART=1
        fi
    fi

    _disable_ipv6_sys_conf

    # config all interface devices disable
    _config_interfaces "del_ipv6"

    NETWORK_NEED_RESTART=1
}

function disable_ipv6() {
    _log_info "Begin disable ipv6 ..."

    _inet_disable_ipv6
    _switch_network_ipv6_to "disable"
}

function _process() {
    _CMD=""

    while [ ${#} -gt 0 ]; do
        case "${1}" in
            --help | -h | -H)
                showhelp
                return
                ;;
            --version | -v | -V)
                version
                return
                ;;
            --enable)
                _CMD="enable"
                break
                ;;
            --disable)
                _CMD="disable"
                break
                ;;
            *)
                echo "Unknown parameter : $1"
                return 1
                ;;
        esac
    done

    _is_root
    [ "$?" -ne 0 ] && {
        echo "Please use the root account to excute the checking."
        exit $ERROR_NOT_ROOT_ACCOUNT
    }

    _init_home

    _get_distro

    _convert_issue_to_lower

    _get_major_and_minor_version

    _convert_version_to_lower

    if [ "${_CMD}" == "enable" ]; then
        # check dhcp client
        _check_dhcp_client
    fi

    case "${_CMD}" in
        enable)
            enable_ipv6
            ;;
        disable)
            disable_ipv6
            ;;
        *)
            if [ "$_CMD" ]; then
                echo "Invalid command: $_CMD"
                exit $ERROR_INVALID_COMMAND
            fi
            showhelp
            return 1
            ;;
    esac
}

function begin() {
    if _startswith "$1" '-'; then
        _process "$@"
    else
        echo "Invalid command [$@] ."
        exit $ERROR_INVALID_COMMAND
    fi
}

begin "$@"
