VMRack
首页
产品
解决方案
定价
支持
推广联盟
帮助文档
云服务器
计费说明
到期和续费
快速入门
连接实例
管理实例
存储管理
网络与安全
违规处理
资源转移
内网 IP 地址
常见问题
CDN
计费说明
快速入门
控制台指南
常见问题
对象存储
计费说明
快速入门
控制台指南
常见问题
云转码
计费说明
快速入门
任务管理
工作流管理
预设管理
队列管理
常见问题
裸金属服务器
计费说明
到期和续费
快速入门
连接实例
管理实例
网络与安全
IP 变更指南
违规处理
资源转移
常见问题
  1. 帮助文档
  2. /
  3. 裸金属服务器
  4. /
  5. IP 变更指南

IP 变更指南

一、使用场景说明

本指南适用于裸金属服务器使用中,需要对操作系统 IPv4 地址进行新增、变更或删除的情形。此时,您需要登录服务器,执行我们提供的 IP 变更脚本。

二、操作说明

1. 准备脚本

根据服务器的操作系统选择对应的脚本,并将脚本内容保存为文件,例如:

change-ip.sh

可以使用以下命令创建并编辑脚本文件:

vi change-ip.sh

将对应的脚本内容复制到文件中并保存。保存完成后,为脚本添加执行权限:

chmod +x change-ip.sh

2. 配置目标 IP 地址

打开脚本文件,找到 NEW_IPS 配置项:

NEW_IPS=()

将需要配置的 IPv4 地址(可在IP管理页面一键复制)填写到 NEW_IPS 中(也可不填,完全使用命令行参数指定见4)。多个 IP 地址使用空格分隔,例如:

NEW_IPS=("192.168.1.20" "192.168.1.30")

上述配置表示脚本执行后,将为服务器配置以下两个 IPv4 地址:

  • 192.168.1.20

  • 192.168.1.30

3. 执行脚本

完成 IP 地址配置后,使用 sudo 执行脚本:

sudo ./change-ip.sh

脚本将按照配置的目标 IP 地址执行变更操作。

4. 通过命令行参数指定 IP

除在脚本中配置 NEW_IPS 外,也可以在执行脚本时通过命令行参数传入 IP 地址。

例如:

sudo ./change-ip.sh "192.168.1.10" "192.168.1.11"

命令行参数中的 IP 地址会与脚本 NEW_IPS 中配置的 IP 地址进行合并并去重,然后统一应用。

例如,脚本中配置:

NEW_IPS=("192.168.1.20" "192.168.1.30")

执行时传入:

sudo ./change-ip.sh "192.168.1.10" "192.168.1.11"

最终脚本处理的 IP 地址为:

  • 192.168.1.10

  • 192.168.1.11

  • 192.168.1.20

  • 192.168.1.30

如果同一个 IP 同时存在于 NEW_IPS 和命令行参数中,只会保留一个。

5. 使用流程

完整操作流程如下:

选择对应操作系统的脚本
    ↓
创建 change-ip.sh 文件
    ↓
将脚本内容复制到文件中
    ↓
添加执行权限
    ↓
配置 NEW_IPS
    ↓
执行脚本
    ↓
完成 IPv4 地址变更

三、脚本内容

1.CentOS7.9 脚本

change-ip.sh 文件:

#!/usr/bin/env bash
set -o pipefail

# IPs configured here are merged with command-line IPs.
# Example: NEW_IPS=("192.169.21.7" "192.169.21.8")
NEW_IPS=()

CONFIG_DIR="/etc/sysconfig/network-scripts"
INTERFACE=""
CONFIG_FILE=""
BACKUP_FILE=""
PREFIX=""
changes_started=0
success=0

declare -a OLD_CIDRS=()
declare -a NEW_IPS_RESULT=()

log_info() {
    printf '[INFO] %s\n' "$*"
}

log_warn() {
    printf '[WARN] %s\n' "$*" >&2
}

log_error() {
    printf '[ERROR] %s\n' "$*" >&2
}

die() {
    log_error "$*"
    exit 1
}

usage() {
    cat <<EOF
Usage:
    $0 <IP1> [IP2] [IP3] ...

Examples:
    $0 192.169.21.7
    $0 192.169.21.7 192.169.21.8
EOF
}

valid_ipv4() {
    local ip="$1"
    local octet
    local -a octets

    [[ "$ip" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]] ||
        return 1

    IFS=. read -r -a octets <<< "$ip"

    for octet in "${octets[@]}"; do
        (( 10#$octet <= 255 )) || return 1
    done
}

add_unique_ip() {
    local ip="$1"
    local existing

    for existing in "${NEW_IPS_RESULT[@]}"; do
        [ "$existing" = "$ip" ] && return 0
    done

    NEW_IPS_RESULT+=("$ip")
}

detect_interface() {
    INTERFACE="$(
        ip -4 route get 1.1.1.1 2>/dev/null |
            awk '{
                for (i = 1; i <= NF; i++) {
                    if ($i == "dev") {
                        print $(i + 1)
                        exit
                    }
                }
            }'
    )"

    [ -n "$INTERFACE" ] ||
        INTERFACE="$(
            ip -4 route show default 2>/dev/null |
                awk 'NR == 1 {print $5}'
        )"

    [ -n "$INTERFACE" ] ||
        die "Unable to determine the default IPv4 interface"

    [ "$INTERFACE" != "lo" ] ||
        die "The detected interface is loopback"
}

get_config_value() {
    local key="$1"

    awk -F= -v key="$key" '
        $0 !~ /^[[:space:]]*#/ && $1 == key {
            value = substr($0, index($0, "=") + 1)
            print value
            exit
        }
    ' "$CONFIG_FILE" |
        sed \
            -e 's/^[[:space:]]*//' \
            -e 's/[[:space:]]*$//' \
            -e 's/^"//' \
            -e 's/"$//' \
            -e "s/^'//" \
            -e "s/'$//"
}

get_current_addresses() {
    OLD_CIDRS=()

    while IFS= read -r cidr; do
        [ -n "$cidr" ] && OLD_CIDRS+=("$cidr")
    done < <(
        ip -4 -o addr show dev "$INTERFACE" scope global 2>/dev/null |
            awk '{print $4}'
    )
}

get_prefix() {
    local cidr
    local config_prefix

    for cidr in "${OLD_CIDRS[@]}"; do
        PREFIX="${cidr##*/}"
        break
    done

    if [ -z "$PREFIX" ]; then
        config_prefix="$(get_config_value "PREFIX0")"

        [ -n "$config_prefix" ] ||
            config_prefix="$(get_config_value "PREFIX")"

        PREFIX="$config_prefix"
    fi

    [[ "$PREFIX" =~ ^[0-9]+$ ]] &&
        (( 10#$PREFIX <= 32 )) ||
        die "Unable to determine a valid IPv4 prefix"

    PREFIX=$((10#$PREFIX))
}

create_backup() {
    local backup_dir

    backup_dir="$(mktemp -d "/root/change-ip-backup-${INTERFACE}.XXXXXX")" ||
        die "Failed to create backup directory"

    chmod 700 "$backup_dir"
    BACKUP_FILE="$backup_dir/$(basename "$CONFIG_FILE")"

    cp -p "$CONFIG_FILE" "$BACKUP_FILE" ||
        die "Failed to create backup: $BACKUP_FILE"

    log_info "Backup created: $BACKUP_FILE"
}

update_config() {
    local temp_file
    local i

    temp_file="$(mktemp "${CONFIG_FILE}.tmp.XXXXXX")" ||
        die "Failed to create temporary configuration file"

    awk '
        $0 !~ /^[[:space:]]*IPADDR[0-9]*=/ &&
        $0 !~ /^[[:space:]]*PREFIX[0-9]*=/ &&
        $0 !~ /^[[:space:]]*NETMASK[0-9]*=/ &&
        $0 !~ /^[[:space:]]*DEVTIMEOUT=/ {
            print
        }
    ' "$CONFIG_FILE" > "$temp_file" || {
        rm -f "$temp_file"
        die "Failed to process configuration file"
    }

    for ((i = 0; i < ${#NEW_IPS_RESULT[@]}; i++)); do
        printf 'IPADDR%d=%s\n' "$i" "${NEW_IPS_RESULT[$i]}" >> "$temp_file"
        printf 'PREFIX%d=%s\n' "$i" "$PREFIX" >> "$temp_file"
    done

    # Keep DEVTIMEOUT as the final line of the ifcfg file.
    printf 'DEVTIMEOUT=60\n' >> "$temp_file"

    chmod --reference="$CONFIG_FILE" "$temp_file" 2>/dev/null || true
    chown --reference="$CONFIG_FILE" "$temp_file" 2>/dev/null || true

    mv -f "$temp_file" "$CONFIG_FILE" ||
        die "Failed to update $CONFIG_FILE"

    log_info "Configuration file updated"
}

remove_old_runtime_addresses() {
    local old_cidr
    local old_ip
    local new_ip
    local keep

    for old_cidr in "${OLD_CIDRS[@]}"; do
        old_ip="${old_cidr%%/*}"
        keep=false

        for new_ip in "${NEW_IPS_RESULT[@]}"; do
            if [ "$old_ip" = "$new_ip" ]; then
                keep=true
                break
            fi
        done

        [ "$keep" = true ] && continue

        if ip -4 addr show dev "$INTERFACE" | grep -qw "$old_cidr"; then
            log_info "Removing old runtime address: $old_cidr"

            ip -4 addr del "$old_cidr" dev "$INTERFACE" ||
                die "Failed to remove old runtime address: $old_cidr"
        fi
    done
}

verify_runtime() {
    local actual
    local expected

    actual="$(
        ip -4 -o addr show dev "$INTERFACE" scope global |
            awk '{print $4}' |
            sort
    )"

    expected="$(
        for ip in "${NEW_IPS_RESULT[@]}"; do
            printf '%s/%s\n' "$ip" "$PREFIX"
        done | sort
    )"

    if [ "$actual" != "$expected" ]; then
        log_error "Runtime IPv4 addresses do not match the requested list"
        log_error "Actual: ${actual//$'\n'/, }"
        log_error "Expected: ${expected//$'\n'/, }"
        return 1
    fi

    return 0
}

restore_backup() {
    [ -n "$BACKUP_FILE" ] && [ -f "$BACKUP_FILE" ] || return 1

    log_warn "Restoring the original configuration..."

    cp -p "$BACKUP_FILE" "$CONFIG_FILE" ||
        return 1

    ifdown "$INTERFACE" >/dev/null 2>&1 || true

    if ! ifup "$INTERFACE" >/dev/null 2>&1; then
        log_error "Configuration was restored, but interface activation failed"
        return 1
    fi

    log_info "Original configuration restored"
}

cleanup() {
    local status=$?

    trap - EXIT

    if [ "$changes_started" -eq 1 ] && [ "$success" -eq 0 ]; then
        restore_backup ||
            log_error "Automatic rollback failed; use the console to recover the network"
    fi

    exit "$status"
}

main() {
    local bootproto
    local configured_ip
    local -a configured_ips

    [ "$(id -u)" -eq 0 ] ||
        die "Run this script as root or via sudo"

    command -v ip >/dev/null 2>&1 ||
        die "The ip command is not available"

    command -v ifup >/dev/null 2>&1 ||
        die "The ifup command is not available"

    command -v ifdown >/dev/null 2>&1 ||
        die "The ifdown command is not available"

    command -v awk >/dev/null 2>&1 ||
        die "The awk command is not available"

    command -v sed >/dev/null 2>&1 ||
        die "The sed command is not available"

    command -v grep >/dev/null 2>&1 ||
        die "The grep command is not available"

    [ -d "$CONFIG_DIR" ] ||
        die "$CONFIG_DIR does not exist"

    if systemctl is-active --quiet NetworkManager; then
        die "NetworkManager is active. Use the nmcli script instead"
    fi

    configured_ips=("${NEW_IPS[@]}")
    NEW_IPS_RESULT=()

    for configured_ip in "${configured_ips[@]}" "$@"; do
        case "$configured_ip" in
            -h|--help)
                usage
                exit 0
                ;;
        esac

        valid_ipv4 "$configured_ip" ||
            die "Invalid IPv4 address: $configured_ip"

        add_unique_ip "$configured_ip"
    done

    [ "${#NEW_IPS_RESULT[@]}" -gt 0 ] ||
        die "At least one IPv4 address is required"

    detect_interface

    CONFIG_FILE="$CONFIG_DIR/ifcfg-$INTERFACE"
    [ -f "$CONFIG_FILE" ] ||
        die "Configuration file not found: $CONFIG_FILE"

    bootproto="$(get_config_value "BOOTPROTO")"

    case "$bootproto" in
        ""|none|static)
            ;;
        *)
            die "BOOTPROTO=$bootproto is not supported; static IPv4 is required"
            ;;
    esac

    get_current_addresses
    get_prefix

    log_info "Target interface: $INTERFACE"
    log_info "Configuration file: $CONFIG_FILE"
    log_info "Current IPv4 addresses: ${OLD_CIDRS[*]:-none}"
    log_info "New IPv4 addresses:"

    for configured_ip in "${NEW_IPS_RESULT[@]}"; do
        log_info "  $configured_ip/$PREFIX"
    done

    log_info "DEVTIMEOUT: 60"
    log_warn "ifdown/ifup will interrupt the current SSH session"

    create_backup
    changes_started=1

    update_config

    log_info "Reactivating interface..."

    ifdown "$INTERFACE" >/dev/null 2>&1 || true

    ifup "$INTERFACE" ||
        die "Failed to activate interface"

    remove_old_runtime_addresses

    verify_runtime ||
        die "Runtime IPv4 verification failed"

    success=1

    log_info "IPv4 address update completed successfully"
    log_info "Active IPv4 addresses:"

    ip -4 -o addr show dev "$INTERFACE" scope global |
        awk '{print "  " $4}'

    log_info "Backup retained at: $BACKUP_FILE"
}

trap cleanup EXIT
main "$@"

2.CentOS8.3、Rocky9.2、Rocky9.4 脚本

change-ip.sh 文件:

#!/usr/bin/env bash

set -o pipefail

# IP addresses can be specified here or passed through the command line.
# Example: NEW_IPS=("192.168.1.20" "192.168.1.30")
NEW_IPS=()

selected_uuid=""
default_interface=""
old_address_spec=""
modified=0
success=0

die() {
    echo "Error: $*" >&2
    exit 1
}

trim() {
    local value="$1"
    value="${value#"${value%%[![:space:]]*}"}"
    value="${value%"${value##*[![:space:]]}"}"
    printf '%s' "$value"
}

valid_ipv4() {
    local ip="$1"
    local octet
    local -a octets

    [[ "$ip" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]] ||
        return 1

    IFS=. read -r -a octets <<< "$ip"

    for octet in "${octets[@]}"; do
        (( 10#$octet <= 255 )) || return 1
    done
}

add_unique_ip() {
    local ip="$1"
    local existing_ip

    for existing_ip in "${NEW_IPS[@]}"; do
        [ "$existing_ip" = "$ip" ] && return 0
    done

    NEW_IPS+=("$ip")
}

detect_default_interface() {
    ip -4 route get 1.1.1.1 2>/dev/null |
        awk '{
            for (i = 1; i <= NF; i++) {
                if ($i == "dev") {
                    print $(i + 1)
                    exit
                }
            }
        }'
}

normalize_addresses() {
    local addresses="$1"
    local address
    local -a normalized=()

    while IFS= read -r address; do
        address="$(trim "$address")"

        [ -n "$address" ] || continue
        [ "$address" = "--" ] && continue
        [[ "$address" == */* ]] || continue

        normalized+=("$address")
    done < <(printf '%s\n' "$addresses" | tr ',' '\n')

    (IFS=,; printf '%s' "${normalized[*]}")
}

rollback() {
    [ "$modified" -eq 1 ] || return 0
    [ -n "$selected_uuid" ] || return 0
    [ -n "$old_address_spec" ] || return 0

    echo "Attempting to restore the original IPv4 address configuration..." >&2

    nmcli connection modify \
        uuid "$selected_uuid" \
        ipv4.addresses "$old_address_spec" >/dev/null 2>&1 || true

    nmcli connection up \
        uuid "$selected_uuid" \
        ifname "$default_interface" >/dev/null 2>&1 || true
}

cleanup() {
    local status=$?

    if [ "$modified" -eq 1 ] && [ "$success" -eq 0 ]; then
        rollback
    fi

    exit "$status"
}

trap cleanup EXIT

if [ "$(id -u)" -ne 0 ]; then
    die "Please run this script as root or via sudo"
fi

command -v systemctl >/dev/null 2>&1 ||
    die "systemctl not found"

command -v nmcli >/dev/null 2>&1 ||
    die "nmcli not found. Please make sure NetworkManager is installed"

command -v ip >/dev/null 2>&1 ||
    die "ip command not found"

systemctl enable --now NetworkManager ||
    die "Failed to start NetworkManager or enable it at boot"

systemctl is-active --quiet NetworkManager ||
    die "NetworkManager is not running"

# Merge, validate, and deduplicate IP addresses specified in the script and on the command line.
configured_ips=("${NEW_IPS[@]}")
NEW_IPS=()

for ip in "${configured_ips[@]}" "$@"; do
    valid_ipv4 "$ip" ||
        die "Invalid IPv4 address: $ip"

    add_unique_ip "$ip"
done

[ "${#NEW_IPS[@]}" -gt 0 ] ||
    die "No IPv4 address specified. Please fill in NEW_IPS in the script or pass IP addresses through the command line"

default_interface="$(detect_default_interface)"

[ -n "$default_interface" ] ||
    die "Unable to determine the network interface used by the current default IPv4 route"

[ "$default_interface" != "lo" ] ||
    die "A loopback interface was detected. No usable default IPv4 route is available"

ip link show dev "$default_interface" >/dev/null 2>&1 ||
    die "Default route interface does not exist: $default_interface"

selected_uuid="$(
    nmcli -t -f UUID,DEVICE connection show --active 2>/dev/null |
        awk -F: -v device="$default_interface" '$2 == device { print $1; exit }'
)"

[ -n "$selected_uuid" ] ||
    die "Interface $default_interface is not managed by NetworkManager or has no active connection"

connection_type="$(
    nmcli -g connection.type connection show uuid "$selected_uuid" 2>/dev/null
)"

[ "$connection_type" = "802-3-ethernet" ] ||
    die "Target connection is not a standard Ethernet connection: $connection_type; bond, bridge, and VLAN connections are not supported by this script"

ipv4_method="$(
    nmcli -g ipv4.method connection show uuid "$selected_uuid" 2>/dev/null
)"

[ "$ipv4_method" = "manual" ] ||
    die "The IPv4 method of the target connection is not manual: $ipv4_method"

old_addresses="$(
    nmcli -g ipv4.addresses connection show uuid "$selected_uuid" 2>/dev/null
)" || die "Unable to read the IPv4 addresses of the target connection"

old_address_spec="$(normalize_addresses "$old_addresses")"

[ -n "$old_address_spec" ] ||
    die "The target connection has no usable IPv4 CIDR addresses"

first_prefix=""

while IFS= read -r address; do
    address="$(trim "$address")"

    [[ "$address" == */* ]] || continue

    first_prefix="${address##*/}"
    break
done < <(printf '%s\n' "$old_address_spec" | tr ',' '\n')

[[ "$first_prefix" =~ ^[0-9]+$ ]] &&
    (( 10#$first_prefix <= 32 )) ||
    die "Unable to obtain a valid prefix from the original IPv4 address"

new_addresses=()

for ip in "${NEW_IPS[@]}"; do
    new_addresses+=("${ip}/${first_prefix}")
done

address_spec="$(IFS=,; printf '%s' "${new_addresses[*]}")"

echo "Target interface: $default_interface"
echo "Target connection UUID: $selected_uuid"
echo "Original IPv4 addresses: $old_address_spec"
echo "New IPv4 addresses: $address_spec"
echo "Warning: The connection will be reactivated, and the current SSH session may be interrupted."

nmcli connection modify \
    uuid "$selected_uuid" \
    ipv4.addresses "$address_spec" ||
    die "Failed to modify IPv4 addresses"

modified=1

nmcli connection down uuid "$selected_uuid" >/dev/null 2>&1 || true

nmcli connection up \
    uuid "$selected_uuid" \
    ifname "$default_interface" ||
    die "Failed to reactivate the NetworkManager connection"

success=1

echo "IPv4 addresses updated and the connection was successfully reactivated"

3.Ubuntu20、Ubuntu22、Debian12 脚本

change-ip.sh 文件:

#!/usr/bin/env bash
set -o pipefail

# IPs defined here are merged with command-line IPs.
# Example: NEW_IPS=("192.169.21.45" "192.169.21.46")
NEW_IPS=()

backup_dir=""
netplan_file=""
cloud_init_marker_created=0
modified=0
success=0

die() {
    echo "Error: $*" >&2
    exit 1
}

valid_ipv4() {
    local candidate="$1"
    local octet
    local -a octets

    [[ "$candidate" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]] ||
        return 1

    IFS=. read -r -a octets <<< "$candidate"
    for octet in "${octets[@]}"; do
        (( 10#$octet <= 255 )) || return 1
    done
}

add_unique_ip() {
    local candidate="$1"
    local existing

    for existing in "${new_ips[@]}"; do
        [ "$existing" = "$candidate" ] && return 0
    done

    new_ips+=("$candidate")
}

detect_default_interface() {
    ip -4 route get 1.1.1.1 2>/dev/null |
        awk '{
            for (i = 1; i <= NF; i++) {
                if ($i == "dev") {
                    print $(i + 1)
                    exit
                }
            }
        }'
}

rollback() {
    [ "$modified" -eq 1 ] || return 0
    [ -n "$backup_dir" ] || return 0
    [ -d "$backup_dir/netplan" ] || return 0

    echo "Restoring previous Netplan configuration..." >&2
    set +e

    find /etc/netplan -maxdepth 1 -type f \
        \( -name '*.yaml' -o -name '*.yml' \) -delete

    cp -a "$backup_dir/netplan/." /etc/netplan/

    if [ "$cloud_init_marker_created" -eq 1 ]; then
        rm -f /etc/cloud/cloud.cfg.d/99-disable-network-config.cfg
    fi

    netplan generate
    netplan apply
}

cleanup() {
    local status=$?

    trap - EXIT

    if [ "$modified" -eq 1 ] && [ "$success" -eq 0 ]; then
        rollback
    fi

    exit "$status"
}

trap cleanup EXIT

[ "$(id -u)" -eq 0 ] ||
    die "Run this script as root or with sudo"

command -v netplan >/dev/null 2>&1 ||
    die "netplan is not installed"

command -v ip >/dev/null 2>&1 ||
    die "ip command is not installed"

command -v python3 >/dev/null 2>&1 ||
    die "python3 is not installed"

python3 -c 'import yaml' >/dev/null 2>&1 ||
    die "python3-yaml is required: sudo apt-get install -y python3-yaml"

[ -d /etc/netplan ] ||
    die "/etc/netplan does not exist"

interface="$(detect_default_interface)"
[ -n "$interface" ] ||
    die "Cannot detect the default IPv4 route interface"

[ "$interface" != "lo" ] ||
    die "The detected interface is loopback"

ip link show dev "$interface" >/dev/null 2>&1 ||
    die "Interface does not exist: $interface"

configured_ips=("${NEW_IPS[@]}")
new_ips=()

for candidate in "${configured_ips[@]}" "$@"; do
    valid_ipv4 "$candidate" ||
        die "Invalid IPv4 address: $candidate"
    add_unique_ip "$candidate"
done

[ "${#new_ips[@]}" -gt 0 ] ||
    die "Specify at least one IPv4 address"

prefix="$(
    ip -4 -o addr show dev "$interface" scope global |
        awk 'NR == 1 { split($4, fields, "/"); print fields[2] }'
)"

[[ "$prefix" =~ ^([0-9]|[12][0-9]|3[0-2])$ ]] ||
    die "Cannot determine a valid IPv4 prefix from $interface"

address_spec=""
for candidate in "${new_ips[@]}"; do
    address_spec+="${candidate}/${prefix},"
done
address_spec="${address_spec%,}"

netplan_file="$(
    python3 - /etc/netplan "$interface" <<'PY'
import pathlib
import sys
import yaml

root = pathlib.Path(sys.argv[1])
interface = sys.argv[2]
matches = []

for path in sorted(list(root.glob("*.yaml")) + list(root.glob("*.yml"))):
    with path.open(encoding="utf-8") as source:
        document = yaml.safe_load(source) or {}

    ethernets = document.get("network", {}).get("ethernets", {})
    if isinstance(ethernets, dict) and interface in ethernets:
        matches.append(str(path))

if len(matches) != 1:
    print(
        f"Expected exactly one Netplan file defining ethernets.{interface}; found {len(matches)}",
        file=sys.stderr,
    )
    sys.exit(1)

print(matches[0])
PY
)"

backup_dir="$(mktemp -d /root/netplan-backup.XXXXXX)"
chmod 700 "$backup_dir"
cp -a /etc/netplan "$backup_dir/netplan"

if [ "$(basename "$netplan_file")" = "50-cloud-init.yaml" ] &&
   [ ! -e /etc/cloud/cloud.cfg.d/99-disable-network-config.cfg ]; then
    printf '%s\n' 'network: {config: disabled}' \
        > /etc/cloud/cloud.cfg.d/99-disable-network-config.cfg
    chmod 600 /etc/cloud/cloud.cfg.d/99-disable-network-config.cfg
    cloud_init_marker_created=1
fi

echo "Interface: $interface"
echo "Netplan source: $netplan_file"
echo "IPv4 addresses: $address_spec"
echo "Backup: $backup_dir"
echo "Applying configuration directly; the current SSH session may disconnect."

python3 - "$netplan_file" "$interface" "$address_spec" <<'PY'
import os
import pathlib
import stat
import sys
import tempfile
import yaml

path = pathlib.Path(sys.argv[1])
interface = sys.argv[2]
addresses = [address for address in sys.argv[3].split(",") if address]

with path.open(encoding="utf-8") as source:
    document = yaml.safe_load(source) or {}

profile = document["network"]["ethernets"][interface]
profile["addresses"] = addresses

original_mode = stat.S_IMODE(path.stat().st_mode)
fd, temporary_name = tempfile.mkstemp(
    dir=path.parent,
    prefix=f".{path.name}.",
    suffix=".tmp",
)

with os.fdopen(fd, "w", encoding="utf-8") as target:
    yaml.safe_dump(
        document,
        target,
        allow_unicode=True,
        default_flow_style=False,
        sort_keys=False,
    )

os.chmod(temporary_name, original_mode)
os.replace(temporary_name, path)
PY

chmod 600 "$netplan_file"
modified=1

netplan generate ||
    die "Generated Netplan configuration is invalid"

netplan apply ||
    die "Failed to apply Netplan configuration"

success=1
echo "IPv4 configuration applied successfully."

VMRack
  • 产品
  • 云服务器
  • 云服务器
    无限流量
  • 裸金属
  • GPU算力租用
  • CDN
    公测
  • 定制CDN
  • 对象存储
    公测
  • 云转码
    公测
  • 解决方案
  • BYOIP自有IP
  • 硬件定制
  • 服务器托管
  • 资源
  • 定价
  • 帮助文档
  • 专题文章
  • 开发者中心
  • 推广联盟
  • 联系销售
  • 公司
  • 关于我们
  • 服务协议
  • 用户协议
  • 隐私政策
  • 服务等级