#!/bin/bash
set -euo pipefail
# 配色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # 重置颜色

# ====================== 第一步：权限检查 ======================
if [ "$(id -u)" -ne 0 ]; then
    echo -e "${RED}错误：请以 root 权限运行此脚本！${NC}"
    exit 1
fi

# ====================== 第二步：自动识别物理网卡 + 自由组合选择 ======================
echo -e "${YELLOW}=== 第一步：自由选择2个物理网卡进行聚合 ===${NC}"
# 1. 识别所有物理网卡（排除虚拟接口）
PHYSICAL_NICS=($(ip -br link show | grep -vE 'lo|docker|bond|veth|tap|tun' | awk '{print $1}'))
if [ ${#PHYSICAL_NICS[@]} -lt 2 ]; then
    echo -e "${RED}错误：未识别到至少2个物理网卡！当前识别到的网卡：${PHYSICAL_NICS[*]}${NC}"
    exit 1
fi

# 2. 列出所有物理网卡，带编号 --- 统一格式：(1) ens192 ---
echo "📜 识别到以下可用物理网卡（共 ${#PHYSICAL_NICS[@]} 个）："
for i in "${!PHYSICAL_NICS[@]}"; do
    echo "   ($((i+1))) ${PHYSICAL_NICS[$i]}"
done

# 3. 自由选择2个网卡，循环校验输入合法性
while true; do
    read -p "❓ 请输入要聚合的2个网卡的编号（用空格分隔，如 1 3，自由组合）：" NIC_NUM1 NIC_NUM2
    # 校验：是否输入了2个编号
    if [ -z "$NIC_NUM1" ] || [ -z "$NIC_NUM2" ]; then
        echo -e "${YELLOW}警告：请输入2个有效的网卡编号！${NC}"
        continue
    fi
    # 校验：编号是否为数字
    if ! [[ "$NIC_NUM1" =~ ^[0-9]+$ ]] || ! [[ "$NIC_NUM2" =~ ^[0-9]+$ ]]; then
        echo -e "${YELLOW}警告：编号必须为数字！${NC}"
        continue
    fi
    # 校验：编号是否在有效范围内
    if [ "$NIC_NUM1" -lt 1 ] || [ "$NIC_NUM1" -gt ${#PHYSICAL_NICS[@]} ] || [ "$NIC_NUM2" -lt 1 ] || [ "$NIC_NUM2" -gt ${#PHYSICAL_NICS[@]} ]; then
        echo -e "${YELLOW}警告：编号超出范围！有效编号为 1 ~ ${#PHYSICAL_NICS[@]}${NC}"
        continue
    fi
    # 校验：两个编号是否相同（不能选择同一个网卡）
    if [ "$NIC_NUM1" -eq "$NIC_NUM2" ]; then
        echo -e "${YELLOW}警告：不能选择同一个网卡进行聚合！请输入两个不同的编号${NC}"
        continue
    fi
    # 输入合法，退出循环
    break
done

# 4. 根据编号获取选中的网卡名称
NIC1=${PHYSICAL_NICS[$((NIC_NUM1-1))]}
NIC2=${PHYSICAL_NICS[$((NIC_NUM2-1))]}

# 5. 确认选择的网卡组合
echo -e "\n✅ 你选择的聚合网卡组合：$NIC1（编号 $NIC_NUM1）、$NIC2（编号 $NIC_NUM2）"
read -p "❓ 确认使用以上组合进行聚合？[y/n]（默认 y）：" CONFIRM
CONFIRM=${CONFIRM:-y}
if [ "$CONFIRM" != "y" ] && [ "$CONFIRM" != "Y" ]; then
    echo -e "${YELLOW}ℹ️  用户取消选择，脚本退出${NC}"
    exit 0
fi

# ====================== 第三步：自动识别网络配置 ======================
echo -e "\n${YELLOW}=== 第二步：自动识别系统网络配置 ===${NC}"
# 1. 识别默认路由的网卡（提取原有IP/网关的基准网卡）
DEFAULT_NIC=$(ip route show default | awk '{print $5}' | head -n 1 || true)
if [ -z "$DEFAULT_NIC" ]; then
    echo -e "${YELLOW}警告：未识别到默认路由网卡，将使用第一个选中的网卡 $NIC1 作为基准${NC}"
    DEFAULT_NIC=$NIC1
fi

# 2. 自动识别 IPv4 配置（IP/掩码/网关/DNS）
IPV4_CIDR=$(ip addr show $DEFAULT_NIC | grep -w inet | awk '{print $2}' | head -n 1 || true)
IPV4_GATEWAY=$(ip route show default | awk '{print $3}' | head -n 1 || true)
IPV4_DNS=$(grep -E '^nameserver' /etc/resolv.conf | awk '{print $2}' | head -n 1 || true)
# 容错：若未识别到则提示用户手动输入
if [ -z "$IPV4_CIDR" ]; then
    read -p "❓ 未识别到 IPv4 地址（格式如 10.0.0.99/24），请手动输入：" IPV4_CIDR
fi
if [ -z "$IPV4_GATEWAY" ]; then
    read -p "❓ 未识别到默认网关，请手动输入（如 10.0.0.1）：" IPV4_GATEWAY
fi
if [ -z "$IPV4_DNS" ]; then
    read -p "❓ 未识别到 DNS 服务器，请手动输入（如 10.0.0.53）：" IPV4_DNS
fi
echo -e "✅ 自动识别 IPv4 配置：
  - IP/掩码：$IPV4_CIDR
  - 网关：$IPV4_GATEWAY
  - DNS：$IPV4_DNS"

# 3. 自动识别 IPv6 配置（仅识别，永不禁用IPv6）
IPV6_CIDR=$(ip addr show $DEFAULT_NIC | grep -w inet6 | awk '{print $2}' | head -n 1 || true)
IPV6_GATEWAY=$(ip -6 route show default | awk '{print $3}' | head -n 1 || true)
IPV6_DNS=$(grep -E '^nameserver' /etc/resolv.conf | awk '/:/ {print $2}' | head -n 1 || true)
# 移除 IPV6_ENABLE 变量，不再判断是否禁用
if [ -n "$IPV6_CIDR" ] && [ -n "$IPV6_GATEWAY" ]; then
    echo -e "✅ 自动识别 IPv6 配置：
  - IP/掩码：$IPV6_CIDR
  - 网关：$IPV6_GATEWAY
  - DNS：$IPV6_DNS"
else
    # 修改提示语：仅说明未识别到，保留IPv6功能，不提及禁用
    echo -e "${YELLOW}ℹ️  未识别到有效的 IPv6 配置，将保留 IPv6 功能（可后续手动配置）${NC}"
fi

# ====================== 第四步：交互式选择聚合模式 ======================
echo -e "\n${YELLOW}=== 第三步：选择聚合模式 ===${NC}"
echo "请选择聚合模式（输入数字即可）："
# 核心修改：聚合模式列表格式统一为 (1) (2) (3)，括号完全完整
echo "(1) balance-rr (mode 0) - 轮询负载均衡（无交换机可用，虚拟机推荐）"
echo "(2) active-backup (mode 1) - 主备冗余（断网自动切换，稳定性优先）"
echo "(3) 802.3ad (mode 4) - LACP聚合（需交换机配合，物理机推荐）"
read -p "请选择 [1/2/3]（默认 1）：" BOND_MODE_NUM
BOND_MODE_NUM=${BOND_MODE_NUM:-1}

case $BOND_MODE_NUM in
    1) BOND_MODE="balance-rr" ;;
    2) BOND_MODE="active-backup" ;;
    3) BOND_MODE="802.3ad" ;;
    *) BOND_MODE="balance-rr" ;;
esac
echo -e "✅ 已选择聚合模式：$BOND_MODE"

# ====================== 第五步：检查并安装依赖 + 生成配置 ======================
echo -e "\n${YELLOW}=== 第四步：检查并安装依赖，生成配置 ===${NC}"
# 1. 检查 netplan.io 是否已安装
echo "🔧 检查 netplan.io 安装状态..."
if dpkg -s netplan.io > /dev/null 2>&1; then
    echo -e "${GREEN}✅ netplan.io 已安装，跳过安装步骤${NC}"
else
    echo -e "${YELLOW}ℹ️  netplan.io 未安装，开始安装...${NC}"
    apt update && apt install -y netplan.io > /dev/null 2>&1
    if dpkg -s netplan.io > /dev/null 2>&1; then
        echo -e "${GREEN}✅ netplan.io 安装成功${NC}"
    else
        echo -e "${RED}❌ netplan.io 安装失败！请手动安装后重新运行脚本${NC}"
        exit 1
    fi
fi

# 2. 创建 netplan 目录（防止缺失）
mkdir -p /etc/netplan

# 3. 生成 netplan 配置文件（核心修复：分步构建配置，避免格式错乱）
CONFIG_FILE="/etc/netplan/01-bond0.yaml"
echo "🔧 生成聚合配置文件 $CONFIG_FILE..."

# 分步构建配置内容，确保缩进/换行绝对正确
cat > $CONFIG_FILE << EOF
network:
  version: 2
  renderer: networkd
  # 声明物理网卡（仅关闭DHCP，保留IPv6功能）
  ethernets:
    $NIC1:
      dhcp4: no
      dhcp6: no  # 仅关闭DHCPv6，不禁用IPv6
    $NIC2:
      dhcp4: no
      dhcp6: no  # 仅关闭DHCPv6，不禁用IPv6
  # 定义 bond0 聚合接口（保留IPv6功能）
  bonds:
    bond0:
      dhcp4: no
      dhcp6: no  # 仅关闭DHCPv6，不禁用IPv6
      addresses:
        - $IPV4_CIDR
EOF

# 单独添加IPv6地址（确保缩进正确，单独一行）
if [ -n "$IPV6_CIDR" ]; then
    echo "        - $IPV6_CIDR" >> $CONFIG_FILE
fi

# 继续添加nameservers
cat >> $CONFIG_FILE << EOF
      nameservers:
        addresses:
          - $IPV4_DNS
EOF

# 单独添加IPv6 DNS（确保缩进正确）
if [ -n "$IPV6_DNS" ]; then
    echo "          - $IPV6_DNS" >> $CONFIG_FILE
fi

# 继续添加interfaces和routes
cat >> $CONFIG_FILE << EOF
      interfaces: [$NIC1, $NIC2]
      routes:
        - to: 0.0.0.0/0
          via: $IPV4_GATEWAY
EOF

# 单独添加IPv6路由（确保缩进正确）
if [ -n "$IPV6_GATEWAY" ]; then
    echo "        - to: ::/0" >> $CONFIG_FILE
    echo "          via: $IPV6_GATEWAY" >> $CONFIG_FILE
fi

# 最后添加parameters
cat >> $CONFIG_FILE << EOF
      parameters:
        mode: $BOND_MODE
        mii-monitor-interval: 100
EOF

# 单独添加802.3ad的lacp-rate（确保缩进正确）
if [ "$BOND_MODE" = "802.3ad" ]; then
    echo "        lacp-rate: fast" >> $CONFIG_FILE
    echo "        transmit-hash-policy: layer3+4" >> $CONFIG_FILE
fi

# 4. 设置配置文件权限（netplan 要求）
chmod 600 $CONFIG_FILE
chown root:root $CONFIG_FILE

# ====================== 第六步：验证 + 应用配置 ======================
echo -e "\n${YELLOW}=== 第五步：验证并应用配置 ===${NC}"
# 1. 验证配置语法
echo "🔧 验证配置文件语法..."
if netplan generate; then
    echo -e "${GREEN}✅ 配置语法验证通过${NC}"
else
    echo -e "${RED}❌ 配置语法错误！以下是配置文件内容，供排查：${NC}"
    cat $CONFIG_FILE
    exit 1
fi

# 2. 应用配置
echo "🔧 应用聚合配置..."
netplan apply > /dev/null 2>&1
systemctl restart systemd-networkd > /dev/null 2>&1

# ====================== 第七步：验证生效状态 ======================
echo -e "\n${YELLOW}=== 第六步：验证聚合生效状态 ===${NC}"
# 1. 检查 bond0 接口
if ip link show bond0 2>/dev/null | grep -q "UP"; then
    echo -e "${GREEN}✅ bond0 接口已创建并激活${NC}"
else
    echo -e "${RED}❌ bond0 接口创建失败${NC}"
    exit 1
fi

# 2. 检查聚合状态
BOND_STATUS=$(cat /proc/net/bonding/bond0 2>/dev/null | grep "Bonding Mode" | awk -F': ' '{print $2}')
if [ -n "$BOND_STATUS" ]; then
    echo -e "${GREEN}✅ 聚合模式生效：$BOND_STATUS${NC}"
else
    echo -e "${YELLOW}ℹ️  未识别到聚合状态，但 bond0 接口已创建${NC}"
fi

# 3. 测试网络连通性 --- 已修复：增加延迟和重试机制 ---
echo "🔧 测试网络连通性（ping 网关）..."
# 延迟 5 秒，给 802.3ad 足够的 LACP 协商时间
sleep 5
# 重试 3 次，每次超时 2 秒，只要有一次成功就认为网络正常
if ping -c 3 -W 2 $IPV4_GATEWAY > /dev/null 2>&1; then
    echo -e "${GREEN}✅ 网络连通性正常！${NC}"
else
    echo -e "${YELLOW}⚠️  ping 网关失败，但聚合配置已应用，请检查：
  1. 网关 $IPV4_GATEWAY 是否可达
  2. 虚拟机网络模式是否为桥接
  3. 防火墙是否拦截 ICMP 包
  4. 802.3ad 模式下，交换机是否已配置 LACP 聚合组${NC}"
fi

# 最终提示（补充IPv6说明）
echo -e "\n${GREEN}🎉 链路聚合配置完成！
📌 关键信息：
  - 聚合接口：bond0
  - 物理网卡：$NIC1、$NIC2
  - 聚合模式：$BOND_MODE
  - 配置文件：$CONFIG_FILE
  - IPv6 状态：已保留 IPv6 功能，可后续手动配置${NC}"


