Featured image of post Ubuntu从Netplan迁移到NetworkManager:自动迁移脚本与故障解决

Ubuntu从Netplan迁移到NetworkManager:自动迁移脚本与故障解决

适用于ubuntu解决 nmtui 无配置、IP 正常但网络管理异常问题

故障典型表现

  1. 接口物理层正常 能获取IP地址: 执行
1
2
ip -a
# 显示ens网卡状态UP,已获得IP地址192.168.1.2/24
  1. nmtui内缺少网卡配置
1
2
nmtui
# 执行nmtui,缺少网卡配置且无法配置
  1. NetworkManager管理异常 设备unmanaged: 执行
1
2
nmcli
# 显示ens网卡设备状态显示"unmanaged"

设备状态显示"unmanaged"

解决方案-迁移到 NetworkManager 脚本

** 请注意脚本可能会因Windows/Linux换行符不兼容导致执行错误。请务必保存为Linux支持形式。 ** 迁移脚本功能 **

  1. 系统检测
    • 自动识别操作系统类型(Ubuntu、Debian、CentOS、RHEL、Fedora等)
    • 根据系统类型使用相应的包管理器
  2. 自动安装
    • 检查NetworkManager是否已安装
    • 如未安装,自动安装适合当前系统的NetworkManager
    • 支持多种包管理器(apt、yum、dnf、zypper、pacman)
  3. 服务状态管理
    • 检查NetworkManager服务状态
    • 自动启用和启动服务
    • 详细的错误处理和日志输出
  4. 增强的兼容性
    • 支持更多网络接口命名模式(ens、eth、enp、eno、wlan、wlp)
    • 更好的错误处理和回退机制

** 使用方法 ** root登录,上传脚本后chmod赋予执行权限,执行

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
#!/bin/bash

# 通用网络服务迁移到NetworkManager脚本
set -e

echo "=================================================="
echo "   通用网络服务迁移到NetworkManager"
echo "=================================================="

RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'

log_info() { echo -e "${GREEN}[INFO]${NC} $1"; }
log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
log_error() { echo -e "${RED}[ERROR]${NC} $1"; }
log_debug() { echo -e "${BLUE}[DEBUG]${NC} $1"; }

# 检测系统类型
detect_os() {
    if [ -f /etc/os-release ]; then
        . /etc/os-release
        OS_NAME=$ID
        OS_VERSION=$VERSION_ID
    else
        log_error "无法检测操作系统类型"
        exit 1
    fi
    
    log_info "检测到操作系统: $OS_NAME $OS_VERSION"
}

# 检查并安装NetworkManager
install_networkmanager() {
    log_info "检查NetworkManager安装状态..."
    
    if command -v nmcli >/dev/null 2>&1 && systemctl list-unit-files | grep -q NetworkManager; then
        log_info "NetworkManager 已安装"
        return 0
    fi
    
    log_warn "NetworkManager 未安装,开始安装..."
    
    case $OS_NAME in
        ubuntu|debian)
            export DEBIAN_FRONTEND=noninteractive
            apt-get update
            apt-get install -y network-manager
            ;;
        centos|rhel|fedora|rocky|alma)
            if command -v dnf >/dev/null 2>&1; then
                dnf install -y NetworkManager
            else
                yum install -y NetworkManager
            fi
            ;;
        opensuse*|sles)
            zypper install -y NetworkManager
            ;;
        arch)
            pacman -Sy --noconfirm networkmanager
            ;;
        *)
            log_error "不支持的操作系统: $OS_NAME"
            exit 1
            ;;
    esac
    
    # 验证安装
    if command -v nmcli >/dev/null 2>&1; then
        log_info "✅ NetworkManager 安装成功"
    else
        log_error "❌ NetworkManager 安装失败"
        exit 1
    fi
}

# 检查NetworkManager服务状态
check_networkmanager_service() {
    log_info "检查NetworkManager服务状态..."
    
    if ! systemctl is-enabled NetworkManager >/dev/null 2>&1; then
        log_warn "NetworkManager 服务未启用,正在启用..."
        systemctl enable NetworkManager
    fi
    
    if systemctl is-active NetworkManager >/dev/null 2>&1; then
        log_info "NetworkManager 服务正在运行"
    else
        log_warn "NetworkManager 服务未运行,正在启动..."
        systemctl start NetworkManager
    fi
}

# 获取所有网络接口
get_network_interfaces() {
    ip -o link show | awk -F': ' '{print $2}' | grep -E '^(ens|eth|enp|eno|wlan|wlp)' | grep -v lo
}

# 备份现有网络配置
backup_network_config() {
    log_info "备份网络配置..."
    local backup_dir="/etc/network_backup_$(date +%Y%m%d_%H%M%S)"
    mkdir -p "$backup_dir"
    
    log_info "创建备份目录: $backup_dir"
    
    # 备份各种网络配置
    [ -f /etc/network/interfaces ] && cp /etc/network/interfaces "$backup_dir/" && log_debug "备份了 /etc/network/interfaces"
    [ -d /etc/netplan ] && cp -r /etc/netplan "$backup_dir/" && log_debug "备份了 /etc/netplan"
    [ -d /etc/sysconfig/network-scripts ] && cp -r /etc/sysconfig/network-scripts "$backup_dir/" && log_debug "备份了 /etc/sysconfig/network-scripts"
    [ -d /etc/NetworkManager ] && cp -r /etc/NetworkManager "$backup_dir/systemd_NetworkManager_backup" && log_debug "备份了 /etc/NetworkManager"
    
    # 备份当前网络状态
    ip addr show > "$backup_dir/ip_addr.txt"
    ip route show > "$backup_dir/ip_route.txt"
    cat /etc/resolv.conf > "$backup_dir/resolv.conf.txt" 2>/dev/null || true
    
    # 备份服务状态
    systemctl list-units --type=service | grep -E '(network|NetworkManager|netplan)' > "$backup_dir/services.txt" 2>/dev/null || true
    
    log_info "网络配置已备份到: $backup_dir"
    echo "$backup_dir"
}

# 收集当前网络配置信息
collect_current_network_config() {
    log_info "收集当前网络配置..."
    
    declare -gA interface_configs
    local interfaces=$(get_network_interfaces)
    
    if [ -z "$interfaces" ]; then
        log_warn "未找到物理网络接口,尝试列出所有接口..."
        interfaces=$(ip -o link show | awk -F': ' '{print $2}' | grep -v lo)
    fi
    
    for iface in $interfaces; do
        log_info "分析接口: $iface"
        
        # 获取接口MAC地址
        local mac_addr=$(ip link show "$iface" | grep -oP 'link/ether \K[0-9a-f:]+' | head -1)
        
        # 获取IP地址信息
        local ipv4_info=$(ip -4 addr show dev "$iface" | grep inet | head -1)
        local ipv6_info=$(ip -6 addr show dev "$iface" | grep inet6 | head -1)
        
        # 获取路由信息
        local gateway=$(ip route show default dev "$iface" 2>/dev/null | awk '/default/ {print $3}' | head -1)
        if [ -z "$gateway" ]; then
            gateway=$(ip route show default | awk '/default/ {print $3}' | head -1)
        fi
        
        # 获取DNS信息
        local dns_servers=$(grep nameserver /etc/resolv.conf 2>/dev/null | awk '{print $2}' | tr '\n' ' ' | sed 's/ $//')
        
        # 存储配置信息
        interface_configs["${iface}_mac"]="$mac_addr"
        interface_configs["${iface}_ipv4"]="$ipv4_info"
        interface_configs["${iface}_ipv6"]="$ipv6_info"
        interface_configs["${iface}_gateway"]="$gateway"
        interface_configs["${iface}_dns"]="$dns_servers"
        interface_configs["${iface}_state"]=$(ip link show "$iface" | grep -q "state UP" && echo "up" || echo "down")
        
        log_info "接口 $iface 状态: ${interface_configs["${iface}_state"]}"
        [ -n "$mac_addr" ] && log_debug "接口 $iface MAC: $mac_addr"
        [ -n "$ipv4_info" ] && log_info "接口 $iface IPv4: $ipv4_info"
        [ -n "$ipv6_info" ] && log_debug "接口 $iface IPv6: $ipv6_info"
        [ -n "$gateway" ] && log_info "接口 $iface 网关: $gateway"
        [ -n "$dns_servers" ] && log_info "接口 $iface DNS: $dns_servers"
    done
}

# 1. 停止所有网络服务
stop_all_network_services() {
    log_info "停止所有网络服务..."
    
    # 停止并禁用所有可能的网络管理服务
    services="systemd-networkd networking NetworkManager netplan"
    
    for service in $services; do
        if systemctl is-active --quiet $service 2>/dev/null; then
            systemctl stop $service
            systemctl disable $service
            log_info "已停止: $service"
        fi
    done
    
    # 确保没有残留进程
    pkill -f dhclient || true
    pkill -f wpa_supplicant || true
    sleep 2
}

# 2. 清理网络配置
cleanup_network_config() {
    log_info "清理现有网络配置..."
    
    # 删除所有NetworkManager连接
    if command -v nmcli >/dev/null 2>&1; then
        nmcli --terse connection show | cut -d: -f1 | while read conn; do
            nmcli connection delete "$conn" 2>/dev/null || true
        done
    fi
    
    # 清理各种网络配置
    rm -f /etc/network/interfaces.d/* || true
    rm -f /etc/NetworkManager/conf.d/* || true
    rm -f /usr/lib/NetworkManager/conf.d/10-globally-managed-devices.conf || true
    
    # 清理systemd-networkd配置
    if [ -d /etc/systemd/network ]; then
        mv /etc/systemd/network /etc/systemd/network.backup
        log_info "已备份systemd-networkd配置"
    fi
    
    # 清理netplan配置(不删除,只修改renderer)
    if [ -d /etc/netplan ]; then
        for file in /etc/netplan/*.yaml /etc/netplan/*.yml; do
            if [ -f "$file" ]; then
                cp "$file" "$file.backup"
                sed -i 's/renderer:.*/renderer: NetworkManager/' "$file"
                log_debug "已修改netplan配置: $file"
            fi
        done
    fi
}

# 3. 配置NetworkManager
configure_networkmanager() {
    log_info "配置NetworkManager..."
    
    mkdir -p /etc/NetworkManager/conf.d
    
    cat > /etc/NetworkManager/NetworkManager.conf << 'EOF'
[main]
plugins=keyfile
dns=default

[keyfile]
unmanaged-devices=none

[device]
wifi.scan-rand-mac-address=no

[connection]
ipv6.dhcp-duid=stable
ipv6.dhcp-iaid=mac
EOF

    # 创建强制管理所有设备的配置
    cat > /etc/NetworkManager/conf.d/10-manage-all.conf << 'EOF'
[keyfile]
unmanaged-devices=none
EOF
    
    log_info "NetworkManager配置完成"
}

# 4. 配置Netplan使用NetworkManager
configure_netplan() {
    log_info "配置Netplan..."
    
    if [ -d "/etc/netplan" ]; then
        local netplan_file=$(find /etc/netplan -name "*.yaml" -o -name "*.yml" | head -1)
        if [ -z "$netplan_file" ]; then
            netplan_file="/etc/netplan/01-network-manager.yaml"
            cat > "$netplan_file" << 'EOF'
network:
  version: 2
  renderer: NetworkManager
EOF
            log_info "创建新的netplan配置: $netplan_file"
        else
            log_info "修改现有netplan配置: $netplan_file"
        fi
        
        netplan generate 2>/dev/null || true
        netplan apply 2>/dev/null || true
    else
        log_debug "系统未使用netplan,跳过配置"
    fi
}

# 5. 启动NetworkManager
start_networkmanager() {
    log_info "启动NetworkManager服务..."
    
    systemctl enable NetworkManager
    systemctl start NetworkManager
    
    # 等待服务启动
    for i in {1..20}; do
        if systemctl is-active --quiet NetworkManager; then
            log_info "NetworkManager 启动成功"
            break
        fi
        sleep 1
    done
    
    if ! systemctl is-active --quiet NetworkManager; then
        log_error "NetworkManager启动失败"
        journalctl -u NetworkManager --no-pager -l | tail -20
        exit 1
    fi
    
    sleep 3
}

# 6. 重新检测所有网络设备
rediscover_all_devices() {
    log_info "重新检测所有网络设备..."
    
    local interfaces=$(get_network_interfaces)
    
    if [ -z "$interfaces" ]; then
        log_warn "未找到网络接口,等待系统重新检测..."
        sleep 5
        interfaces=$(get_network_interfaces)
    fi
    
    for iface in $interfaces; do
        log_info "处理网络接口: $iface"
        
        # 设置设备为托管模式
        nmcli device set "$iface" managed yes 2>/dev/null || log_warn "无法设置 $iface 为托管模式"
        
        # 重新应用设备配置
        nmcli device reapply "$iface" 2>/dev/null || log_debug "无法重新应用 $iface 配置"
        
        # 确保设备状态
        nmcli device connect "$iface" 2>/dev/null || log_debug "无法连接 $iface 设备"
    done
    
    # 重启NetworkManager以重新扫描所有设备
    systemctl restart NetworkManager
    sleep 5
}

# 7. 为所有接口创建网络连接
create_network_connections() {
    log_info "为所有网络接口创建连接..."
    
    local interfaces=$(get_network_interfaces)
    
    for iface in $interfaces; do
        log_info "为接口 $iface 创建网络连接..."
        
        # 删除现有连接
        nmcli connection delete "$iface" 2>/dev/null || true
        
        # 获取该接口的配置信息
        local ipv4_config="${interface_configs["${iface}_ipv4"]}"
        local gateway="${interface_configs["${iface}_gateway"]}"
        local dns_servers="${interface_configs["${iface}_dns"]}"
        local interface_state="${interface_configs["${iface}_state"]}"
        
        # 创建基本连接
        if ! nmcli connection add type ethernet ifname "$iface" con-name "$iface" autoconnect yes 2>/dev/null; then
            log_warn "无法为 $iface 创建连接,尝试其他方式..."
            continue
        fi
        
        # 如果有静态IP配置,则配置静态IP
        if [[ "$ipv4_config" =~ ([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+/[0-9]+) ]]; then
            local ip_cidr="${BASH_REMATCH[1]}"
            local ip_address=$(echo "$ip_cidr" | cut -d'/' -f1)
            local prefix=$(echo "$ip_cidr" | cut -d'/' -f2)
            
            log_info "为 $iface 配置静态IP: $ip_cidr"
            
            nmcli connection modify "$iface" ipv4.method manual \
                ipv4.addresses "$ip_cidr" \
                ipv4.gateway "$gateway" \
                ipv4.dns "$dns_servers"
                
        else
            # 使用DHCP
            log_info "为 $iface 配置DHCP"
            nmcli connection modify "$iface" ipv4.method auto
            if [ -n "$dns_servers" ]; then
                nmcli connection modify "$iface" ipv4.dns "$dns_servers"
            fi
        fi
        
        # 激活连接(如果接口之前是up状态)
        if [ "$interface_state" = "up" ]; then
            if nmcli connection up "$iface" 2>/dev/null; then
                log_info "✅ 接口 $iface 连接激活成功"
            else
                log_warn "⚠️  接口 $iface 连接激活失败"
            fi
        else
            log_info "接口 $iface 保持down状态"
        fi
        
        sleep 1
    done
}

# 8. 最终验证和诊断
final_diagnosis() {
    log_info "执行最终诊断..."
    
    echo ""
    echo "=== 系统网络设备 ==="
    ip link show
    echo ""
    
    echo "=== IP地址信息 ==="
    ip addr show
    echo ""
    
    echo "=== 路由表 ==="
    ip route show
    echo ""
    
    echo "=== NetworkManager设备状态 ==="
    nmcli device status
    echo ""
    
    echo "=== NetworkManager连接状态 ==="
    nmcli connection show --active
    echo ""
    
    echo "=== 网络服务状态 ==="
    systemctl status NetworkManager --no-pager -l | head -10
    echo ""
    
    # 检查网络连通性
    log_info "测试网络连通性..."
    if ping -c 2 -W 3 8.8.8.8 >/dev/null 2>&1; then
        log_info "✅ 网络连通性测试通过"
    else
        log_warn "⚠️  网络连通性测试失败,但迁移已完成"
        log_warn "请检查网络配置或手动重启网络接口"
    fi
}

# 显示迁移摘要
show_migration_summary() {
    log_info "=== 迁移摘要 ==="
    echo "备份位置: $backup_dir"
    echo "处理的接口: $(get_network_interfaces | tr '\n' ' ')"
    echo "NetworkManager 状态: $(systemctl is-active NetworkManager)"
    echo ""
    log_info "迁移完成!如果遇到网络问题,请检查备份目录中的配置。"
}

# 主函数
main() {
    log_info "开始通用网络服务迁移流程..."
    
    # 检测系统
    detect_os
    
    # 安装NetworkManager
    install_networkmanager
    
    # 备份配置
    local backup_dir=$(backup_network_config)
    
    # 收集当前配置
    collect_current_network_config
    
    # 执行迁移步骤
    stop_all_network_services
    cleanup_network_config
    configure_networkmanager
    configure_netplan
    start_networkmanager
    rediscover_all_devices
    create_network_connections
    final_diagnosis
    show_migration_summary
}

# 参数检查
if [ "$1" = "--help" ] || [ "$1" = "-h" ]; then
    echo "通用网络服务迁移脚本"
    echo "用法: $0"
    echo ""
    echo "功能:"
    echo "  1. 自动安装NetworkManager(如需要)"
    echo "  2. 备份现有网络配置"
    echo "  3. 自动检测所有网络接口"
    echo "  4. 迁移到NetworkManager"
    echo "  5. 保持原有的IP配置"
    echo ""
    echo "支持的操作系统: Ubuntu, Debian, CentOS, RHEL, Fedora, openSUSE, Arch"
    echo "注意: 此脚本需要root权限执行"
    exit 0
fi

# 检查root权限
if [ "$EUID" -ne 0 ]; then
    log_error "请使用root权限运行此脚本"
    exit 1
fi

# 执行主函数
main "$@"

Licensed under CC BY-NC-SA 4.0
最后更新于 Dec 01, 2025 01:00 UTC
this is the end :)