понедельник, 8 сентября 2025 г.

ALT Linux: Настройка VMware VM Customization Specifications для ОС ALT Linux

К сожалению, при использовании "VM Customization Specifications" при развертывании ОС ALT Linux скрипты "open-vm-toos" не могут корректно сконфигурировать такие параметры как имя хоста, сетевые настройки и т.д. для данной системы.
Чтобы решить эту проблему, применим скрипт, который возьмет на себя функционал, не корректно работающий в самих тулзах.

Выполним следующие шаги для настройки "VM Customization Specifications":

1. Подготавливаем шаблон ОС ATL Linux с установкой необходимого ПО, которое уже должно быть в комплекте с новыми хостами.
2. Обязательно устанавливаем "open-vm-tools" и проверяем, что служба "vmtoolsd" находится в автозапуске:
sudo apt-get install open-vm-tools
sudo systemctl enable vmtoolsd && sudo systemctl start vmtoolsd


3. Включаем поддержку скриптов для кастомизации:
sudo vmware-toolbox-cmd config set deployPkg enable-custom-scripts true

Для проверки текущего состояния параметра выполните:
sudo vmware-toolbox-cmd config get deployPkg enable-custom-scripts

4. Далее настраиваем "VM Customization Specifications" на vCenter Server. При настройке указываем: тип ОС - Linux, правило формирования имени хоста, часовой пояс, TCP/IP настройки и добавляем следующий скрипт:

#!/bin/bash

# Функция для преобразования маски подсети в префикс CIDR
mask2cidr() {
    local mask=$1
    local n=0
    IFS=.
    for byte in $mask; do
        case $byte in
            255) n=$((n+8));;
            254) n=$((n+7));;
            252) n=$((n+6));;
            248) n=$((n+5));;
            240) n=$((n+4));;
            224) n=$((n+3));;
            192) n=$((n+2));;
            128) n=$((n+1));;
            0);;
            *) echo 24; return 1;;
        esac
    done
    echo $n
}

# Функция определения метода управления сетью
get_network_manager() {
    local interface=$1
    local options_file="/etc/net/ifaces/$interface/options"

    # Проверяем существование файла настроек
    if [ -f "$options_file" ]; then
        local nm_controlled=$(grep -E "^NM_CONTROLLED=" "$options_file" | cut -d= -f2)
        local disabled=$(grep -E "^DISABLED=" "$options_file" | cut -d= -f2)
        local systemd_controlled=$(grep -E "^SYSTEMD_CONTROLLED=" "$options_file" | cut -d= -f2)

        if [ "$nm_controlled" = "yes" ]; then
            echo "NetworkManager"
        elif [ "$disabled" = "no" ]; then
            echo "EtcNet"
        elif [ "$systemd_controlled" = "yes" ]; then
            echo "SystemD"
        else
            echo "Unknown"
        fi
    else
        # Если файл не существует, проверяем активные сервисы
        if systemctl is-active NetworkManager >/dev/null 2>&1; then
            echo "NetworkManager"
        elif systemctl is-active systemd-networkd >/dev/null 2>&1 && \
             systemctl is-active systemd-resolved >/dev/null 2>&1; then
            echo "SystemD"
        elif systemctl is-active network >/dev/null 2>&1; then
            echo "EtcNet"
        else
            echo "Unknown"
        fi
    fi
}

# Функция настройки NetworkManager
configure_networkmanager() {
    local interface=$1
    local bootproto=$2
    local ipaddr=$3
    local netmask=$4
    local gateway=$5
    local dns_servers=$6
    local domain=$7

    # Создаем или модифицируем соединение
    if [ "$bootproto" = "static" ]; then
        local prefix_length=$(mask2cidr "$netmask")
        nmcli con add type ethernet con-name "$interface" ifname "$interface" ip4 "$ipaddr/$prefix_length" gw4 "$gateway"
        if [ -n "$dns_servers" ]; then
            nmcli con mod "$interface" ipv4.dns "$dns_servers"
        fi
        if [ -n "$domain" ]; then
            nmcli con mod "$interface" ipv4.dns-search "$domain"
        fi
    else
        nmcli con add type ethernet con-name "$interface" ifname "$interface" ipv4.method auto
    fi

    # Активируем соединение
    nmcli con up "$interface"
}

# Функция настройки EtcNet
configure_etcnet() {
    local interface=$1
    local bootproto=$2
    local ipaddr=$3
    local netmask=$4
    local gateway=$5
    local dns_servers=$6
    local domain=$7

    local iface_dir="/etc/net/ifaces/$interface"

    mkdir -p "$iface_dir"

    # Обновляем файл options в зависимости от типа настройки
    if [ -f "$iface_dir/options" ]; then
        # Обновляем существующий файл options, сохраняя другие параметры
        if [ "$bootproto" = "static" ]; then
            # Удаляем старые параметры BOOTPROTO и SYSTEMD_BOOTPROTO если есть
            sed -i '/^BOOTPROTO=/d' "$iface_dir/options"
            sed -i '/^SYSTEMD_BOOTPROTO=/d' "$iface_dir/options"
            # Добавляем новые параметры
            echo "BOOTPROTO=static" >> "$iface_dir/options"
            echo "SYSTEMD_BOOTPROTO=static" >> "$iface_dir/options"
        else
            # Удаляем старые параметры BOOTPROTO и SYSTEMD_BOOTPROTO если есть
            sed -i '/^BOOTPROTO=/d' "$iface_dir/options"
            sed -i '/^SYSTEMD_BOOTPROTO=/d' "$iface_dir/options"
            # Добавляем новые параметры
            echo "BOOTPROTO=dhcp" >> "$iface_dir/options"
            echo "SYSTEMD_BOOTPROTO=dhcp4" >> "$iface_dir/options"
        fi
    else
        # Создаем новый файл options
        if [ "$bootproto" = "static" ]; then
            cat > "$iface_dir/options" <<EOF
BOOTPROTO=static
SYSTEMD_BOOTPROTO=static
EOF
        else
            cat > "$iface_dir/options" <<EOF
BOOTPROTO=dhcp
SYSTEMD_BOOTPROTO=dhcp4
EOF
        fi
    fi

    if [ "$bootproto" = "static" ]; then
        # Записываем IP-адрес с префиксом CIDR
        PREFIX_LENGTH=$(mask2cidr "$netmask")
        if [ -z "$PREFIX_LENGTH" ]; then
            PREFIX_LENGTH=24
        fi
        echo "$ipaddr/$PREFIX_LENGTH" > "$iface_dir/ipv4address"

        # Записываем маршрут по умолчанию
        if [ -n "$gateway" ]; then
            echo "default via $gateway" > "$iface_dir/ipv4route"
        fi

        # Настраиваем DNS через resolv.conf в директории интерфейса
        if [ -n "$dns_servers" ]; then
            # Создаем resolv.conf в директории интерфейса
            echo "# Generated by network configuration script" > "$iface_dir/resolv.conf"
            if [ -n "$domain" ]; then
                echo "domain $domain" >> "$iface_dir/resolv.conf"
                echo "search $domain" >> "$iface_dir/resolv.conf"
            fi

            # Добавляем DNS-серверы
            for dns in $dns_servers; do
                echo "nameserver $dns" >> "$iface_dir/resolv.conf"
            done
        fi

        # Освобождаем DHCP-аренду для интерфейса
        if command -v /sbin/dhcpcd >/dev/null 2>&1; then
            /sbin/dhcpcd -k "$interface" 2>/dev/null || true
        fi
    else
        # Для DHCP очищаем возможные предыдущие статические настройки
        rm -f "$iface_dir/ipv4address"
        rm -f "$iface_dir/ipv4route"
        rm -f "$iface_dir/resolv.conf"

        # Файл type больше не создаем, как было указано в требованиях
    fi

    # Добавляем интерфейс в порядок обработки
    local order_file="/etc/net/ifaces.order"
    if ! grep -q "^$interface$" "$order_file" 2>/dev/null; then
        echo "$interface" >> "$order_file"
    fi

    # Перезапускаем сеть
    systemctl restart network
}

# --- Функция очистки логов и временных файлов ---
clean_system() {
    # Очистка логов (с сохранением структуры каталогов)
    find /var/log -type f -name "*.log" -exec truncate -s 0 {} \;
    find /var/log -type f -name "*.gz" -delete
    find /var/log -type f -name "*.old" -delete
    find /var/log -type f -name "lastlog" -exec rm -f {} \;

    # Очистка временных файлов
    rm -rf /tmp/*
    rm -rf /var/tmp/*

    # Очистка истории команд и пользовательских данных
    for user_home in /home/*; do
        if [[ -d "$user_home" ]]; then
            user=$(basename "$user_home")
            # Очистка bash_history и других историй
            truncate -s 0 "$user_home/.bash_history" 2>/dev/null || true
            # Очистка кэшей приложений
            rm -rf "$user_home/.cache/*" 2>/dev/null || true
        fi
    done
    # Очистка root
    truncate -s 0 /root/.bash_history 2>/dev/null || true
    rm -rf /root/.cache/* 2>/dev/null || true

    # Удаление случайных seed-файлов
    rm -f /var/lib/systemd/random-seed
}

# --- Функция удаления SSH host keys ---
reset_ssh_keys() {
    rm -f /etc/ssh/ssh_host_*
    # Ключи будут сгенерированы при следующем запуске SSH-сервера
}

if [ "$1" = "precustomization" ]; then
    # Выполняем очистку системы
    clean_system
    reset_ssh_keys

    # Проверяем и создаем директорию /etc/sysconfig/network-scripts если нужно
    if [ ! -d "/etc/sysconfig/network-scripts" ]; then
        mkdir -p /etc/sysconfig/network-scripts
    fi
    # Этап предварительной настройки
    VMCUST_DIR=$(ls -d /var/run/.vmware-imgcust* 2>/dev/null | head -n 1)
    if [ -n "$VMCUST_DIR" ]; then
        CUST_CFG_PATH="$VMCUST_DIR/cust.cfg"
        if [ -f "$CUST_CFG_PATH" ]; then
            cp "$CUST_CFG_PATH" "/root/cust.cfg"
        fi
    fi
elif [ "$1" = "postcustomization" ]; then
    # Этап пост-настройки
    if [ ! -f "/root/cust.cfg" ]; then
        exit 1
    fi

    CFG_FILE="/root/cust.cfg"

    # Парсим параметры конфигурации
    BOOTPROTO=$(awk -F' = ' '/^BOOTPROTO/ {line=$2} END{print tolower(line)}' "$CFG_FILE")
    IPADDR=$(awk -F' = ' '/^IPADDR/ {line=$2} END{print line}' "$CFG_FILE")
    NETMASK=$(awk -F' = ' '/^NETMASK/ {line=$2} END{print line}' "$CFG_FILE")
    GATEWAY=$(awk -F' = ' '/^GATEWAY/ {line=$2} END{print line}' "$CFG_FILE")
    HOSTNAME=$(awk -F' = ' '/^HOSTNAME/ {line=$2} END{print line}' "$CFG_FILE")
    DOMAIN=$(awk -F' = ' '/^DOMAINNAME/ {line=$2} END{print line}' "$CFG_FILE")
    MACADDR=$(awk -F' = ' '/^MACADDR/ {line=$2} END{print tolower(line)}' "$CFG_FILE")
    DNS_SERVERS=$(awk -F' = ' '/^NAMESERVER\|[0-9]/ {print $2}' "$CFG_FILE" | tr '\n' ' ')
    DNS_FROM_DHCP=$(awk -F' = ' '/^DNSFROMDHCP/ {line=$2} END{print tolower(line)}' "$CFG_FILE")
    TIMEZONE=$(awk -F' = ' '/^TIMEZONE/ {line=$2} END{print line}' "$CFG_FILE")
    UTC=$(awk -F' = ' '/^UTC/ {line=$2} END{print tolower(line)}' "$CFG_FILE")

    # Определяем сетевой интерфейс
    INTERFACE=$(ip -o link | awk -v mac="$MACADDR" 'tolower($0) ~ mac {gsub(":", "", $2); print $2}')
    if [ -z "$INTERFACE" ]; then
        INTERFACE=$(ip route | awk '/default/ {print $5; exit}')
    fi

    # Определяем метод управления сетью
    NET_MGR=$(get_network_manager "$INTERFACE")

    case "$NET_MGR" in
        "NetworkManager")
            configure_networkmanager "$INTERFACE" "$BOOTPROTO" "$IPADDR" "$NETMASK" "$GATEWAY" "$DNS_SERVERS" "$DOMAIN"
            ;;
        "EtcNet")
            configure_etcnet "$INTERFACE" "$BOOTPROTO" "$IPADDR" "$NETMASK" "$GATEWAY" "$DNS_SERVERS" "$DOMAIN"
            ;;
        "SystemD")
            # Существующая настройка SystemD
            rm -f /etc/systemd/network/*.network
            rm -f /etc/systemd/resolved.conf.d/*.conf

            # Создаем базовую конфигурацию для loopback-интерфейса
            cat > /etc/systemd/network/00-loopback.network <<EOF
[Match]
Name=lo

[Network]
Address=127.0.0.1/8
Address=::1/128
EOF

            # Создаем конфигурацию для основного интерфейса
            CONFIG_FILE="/etc/systemd/network/alterator-${INTERFACE}.network"

            if [ "$BOOTPROTO" = "static" ]; then
                if [ -z "$IPADDR" ] || [ -z "$NETMASK" ]; then
                    exit 1
                fi

                PREFIX_LENGTH=$(mask2cidr "$NETMASK")
                if [ -z "$PREFIX_LENGTH" ]; then
                    PREFIX_LENGTH=24
                fi

                # Создаем конфиг для статического IP с DNS
                cat > "$CONFIG_FILE" <<EOF
[Match]
Name=$INTERFACE

[Network]
Address=$IPADDR/$PREFIX_LENGTH
Gateway=$GATEWAY
EOF

                # Добавляем DNS серверы, если нужно
                if { [ "$BOOTPROTO" = "static" ] || [ "$DNS_FROM_DHCP" = "no" ]; } && [ -n "$DNS_SERVERS" ]; then
                    for dns in $DNS_SERVERS; do
                        echo "DNS=$dns" >> "$CONFIG_FILE"
                    done
                fi
            else
                # Создаем конфиг для DHCP
                cat > "$CONFIG_FILE" <<EOF
[Match]
Name=$INTERFACE

[Network]
DHCP=ipv4

[DHCPv4]
UseDomains=true
UseDNS=yes
EOF

                # Добавляем DNS серверы, если указано не использовать DNS от DHCP
                if [ "$DNS_FROM_DHCP" = "no" ] && [ -n "$DNS_SERVERS" ]; then
                    for dns in $DNS_SERVERS; do
                        echo "DNS=$dns" >> "$CONFIG_FILE"
                    done
                fi
            fi

            systemctl restart systemd-networkd
            systemctl restart systemd-resolved
            ;;
        *)
            echo "Неизвестный метод управления сетью"
            exit 1
            ;;
    esac

    # Общие настройки
    hostnamectl set-hostname "$HOSTNAME"
    sed -i "/127\.0\.1\.1/d" /etc/hosts
    echo "127.0.1.1 $HOSTNAME.$DOMAIN $HOSTNAME" >> /etc/hosts

     # Настройка часового пояса
    if [ -n "$TIMEZONE" ]; then
        timedatectl set-timezone "$TIMEZONE"
    fi

    # Настройка формата времени BIOS (UTC или локальное)
    if [ -n "$UTC" ]; then
        if [ "$UTC" = "yes" ]; then
            # Устанавливаем UTC время в BIOS
            timedatectl set-local-rtc 0
        else
            # Устанавливаем локальное время в BIOS
            timedatectl set-local-rtc 1
        fi
    fi

    # Сохраняем конфигурацию
    {
        echo "CONFIG_SOURCE=$CFG_FILE"
        echo "BOOTPROTO=$BOOTPROTO"
        echo "INTERFACE=$INTERFACE"
        echo "HOSTNAME=$HOSTNAME"
        echo "DOMAIN=$DOMAIN"
        echo "TIMEZONE=$TIMEZONE"
        echo "UTC=$UTC"
        [ -n "$DNS_FROM_DHCP" ] && echo "DNS_FROM_DHCP=$DNS_FROM_DHCP"
        [ -n "$DNS_SERVERS" ] && echo "DNS_SERVERS=\"$DNS_SERVERS\""
    }

    if [ "$BOOTPROTO" = "static" ]; then
        {
            echo "IP_ADDRESS=$IPADDR"
            echo "NETMASK=$NETMASK"
            echo "PREFIX_LENGTH=$PREFIX_LENGTH"
            [ -n "$GATEWAY" ] && echo "GATEWAY=$GATEWAY"
        }
    fi
    rm -f "/root/cust.cfg"
##############################################################################################
    # МЕСТО ДЛЯ ДОПОЛНИТЕЛЬНОЙ НАСТРОЙКИ СИСТЕМЫ
    # Здесь можно добавить команды для окончательной настройки машины:
    # - Доустановка необходимого программного обеспечения
    # - Настройка через Ansible или другие системы управления конфигурациями
    # - Выполнение пользовательских скриптов
    # - Настройка мониторинга, логирования и других сервисов
##############################################################################################
fi

Данный скрипт охватывает все возможные варианты настройки сети: EtcNet, NetworkManager, Systemd-Networkd, как со статическим IP, так и с DHCP.

Комментариев нет:

Отправить комментарий