От консоли к веб-интерфейсу: создание автоматического веб-инсталлятора для ALD Pro на Flask и Python

от автора

Что такое ALD Pro и зачем его едят можно почитать в других статья на Хабре:

  1. https://habr.com/ru/companies/astralinux/news/857124/

  2. https://habr.com/ru/companies/astralinux/articles/734788/

  3. https://habr.com/ru/companies/astralinux/news/857892/

А я расскажу о своем пути в установке данного комплекса, а также какие проблемы были у меня, чтобы их не было у Вас:)

Ну и конечно же, как и любая моя статья, эта также будет связана со скриптами, автоматизацией и удобствами для конечного пользователя.

Глава 1: Зачем нужна автоматизация для установки ALD Pro?

ALD Pro — это мощное решение для управления доменами и аутентификации в инфраструктуре Astra Linux. Однако его установка и настройка — это многоэтапный процесс, требующий выполнения множества команд в терминале, редактирования конфигурационных файлов и перезагрузки системы. Вручную это не только отнимает много времени, но и чревато ошибками, особенно для администраторов, которые не часто сталкиваются с подобными задачами.

Типичные проблемы ручной установки:

  1. Сложность: Необходимость точного ввода команд, настройки сети, DNS, репозиториев и других параметров.

  2. Время: Процесс может занять несколько часов, особенно если требуется обновление системы или повторение шагов из-за ошибок.

  3. Человеческий фактор: Опечатки, пропущенные шаги или неверные параметры могут привести к неработоспособности системы.

  4. Документация: Официальные инструкции часто объемны и требуют адаптации под конкретную инфраструктуру.

Почему автоматизация?

Автоматизация установки ALD Pro решает эти проблемы, предоставляя следующие преимущества:

  1. Скорость: Скрипт выполняет все шаги за считанные минуты, минимизируя время простоя.

  2. Точность: Исключаются ошибки, связанные с ручным вводом. Каждый шаг проверяется, а параметры валидируются.

  3. Удобство: Администратору не нужно запоминать последовательность действий — достаточно запустить скрипт и следовать подсказкам.

  4. Масштабируемость: Один и тот же скрипт можно использовать для развертывания ALD Pro на множестве серверов, обеспечивая идентичную конфигурацию.

  5. Поддержка разных сценариев: Возможность выбора версии ALD Pro, настройки глобального каталога или модуля синхронизации через интерактивный интерфейс.

Эволюция инструментов

Изначально я использовал bash-скрипты с zenity для создания простого графического интерфейса. Это работало, но имело ограничения:

  • Сложность поддержки: добавление новых функций усложняло код.

  • Ограниченный UI: zenity предоставляет базовые диалоговые окна, но не подходит для сложных сценариев.

  • Нет возможности удаленного управления.

Переход на Python и Flask позволил:

  1. Создать полноценный веб-интерфейс с интуитивной навигацией.

  2. Реализовать прогресс-бар, логирование и обработку ошибок.

  3. Обеспечить доступ к установке с любого устройства в сети.

  4. Легко расширять функционал (например, добавление поддержки новых версий ALD Pro).

Автоматизация установки ALD Pro — это не просто удобство, а необходимость для современных ИТ-инфраструктур. Она экономит время, снижает риски ошибок и позволяет сосредоточиться на более важных задачах. Дальше я расскажу, как прошел путь от простых bash-скриптов до веб-интерфейса на Flask, и какие подводные камни встретил на этом пути.


Глава 2: Bash + Zenity — Первая версия автоматизации

Украду, свою же картинку с другой статьи. Bash навсегда <3

Украду, свою же картинку с другой статьи. Bash навсегда <3

1. Почему начали с Bash?

  • Простота: Bash — это стандартный инструмент администрирования Linux, не требующий дополнительных зависимостей.

  • Скорость разработки: для простых задач не нужны сложные языки.

  • Доступность: все команды установки ALD Pro уже выполнялись вручную, оставалось лишь объединить их в скрипт.

2. Структура bash-скрипта

Разберём ключевые части скрипта:

2.1. Проверки перед установкой

internet_error="У вас проблемы с доступом к сайту dl.astralinux.ru. Проверьте настройку интернет соединения и правильность dns." if ping -c 1 dl.astralinux.ru &> /dev/null; then     echo "Доступ к репозиторию есть" else     zenity --info --text="$internet_error"     exit 1 fi 
  • Проверка Интернет-соединения перед началом установки.

  • Использование zenity для вывода ошибок в GUI.

2.2. Работа с администратором системы и правами sudo

if id -nG | grep -qw "astra-admin"; then   echo ok else    zenity --info --text="Пользователь не принадлежит группе astra-admin. Необходимо зайди под пользователем с правами администратора."   exit 1 fi
passwd=$(zenity --forms --title="Пароль для администратора" \   --text="Введите пароль администратора" \   --add-password="Пароль") echo "$passwd" | sudo -Sv >/dev/null 2>&1 if [ $? -ne 0 ]; then   #остальной код else   zenity --info --text="Неправильный пароль от sudo. Перезапустите скрипт."   exit 1 fi
  • Интерактивный ввод пароля через zenity.

  • Проверка корректности перед выполнением привилегированных команд.

2.3. Динамические параметры установки

form_data=$(zenity --forms --title="Введите данные" --text="Введите данные:" \         --add-entry="Введите имя контроллера домена имя типа: dc" \         --add-entry="Введите имя домена типа: domain.test" \         --add-entry="Введите имя полное доменное имя типа: dc.domain.test" \         --add-entry="Введите статический ip-address вашего будущего домена типа: 10.10.10.10" \         --add-entry="Введите маску подсети вашего будущего домена типа: 255.255.255.0" \         --add-entry="Введите gateway сети вашего будущего домена типа: 10.10.10.1" \         --add-entry="Введите ваш dns для доступа в интернет: 10.10.10.9" \         --add-password="Придумайте пароль для администратора домена:" \         --add-combo="Версия ALDPro" \         --combo-values="2.2.1|2.3.0|2.4.0" )        # Разбиение строки с данными на отдельные переменные       small_fqdn=$(echo "$form_data" | awk -F '|' '{print $1}')       big_fqdn=$(echo "$form_data" | awk -F '|' '{print $2}')       fqdn=$(echo "$form_data" | awk -F '|' '{print $3}')       ipaddres=$(echo "$form_data" | awk -F '|' '{print $4}')       mask=$(echo "$form_data" | awk -F '|' '{print $5}')       gateway=$(echo "$form_data" | awk -F '|' '{print $6}')       dns=$(echo "$form_data" | awk -F '|' '{print $7}')       passwd_dom=$(echo "$form_data" | awk -F '|' '{print $8}')       version_aldpro=$(echo "$form_data" | awk -F '|' '{print $9}') 
  • Графический ввод параметров (FQDN, IP, пароль и т.д.).

  • Демонстрация примеров заполнения, для удобства пользователя.

2.4. Настройка сети и репозиториев

if [ "$version_aldpro" == 2.3.0 ]; then #репы 2.3.0 и 1.7.5   echo $passwd | sudo -S bash -c "echo -e 'deb https://download.astralinux.ru/aldpro/frozen/01/2.3.0 1.7_x86-64 main base' > /etc/apt/sources.list.d/aldpro.list"   echo $passwd | sudo -S bash -c "echo -e 'deb https://dl.astralinux.ru/astra/frozen/1.7_x86-64/1.7.5/uu/1/repository-base/ 1.7_x86-64 main contrib non-free' > /etc/apt/sources.list"    echo $passwd | sudo -S bash -c "echo -e 'deb http://download.astralinux.ru/astra/frozen/1.7_x86-64/1.7.5/repository-extended 1.7_x86-64 main contrib non-free' >> /etc/apt/sources.list"                                 elif [ "$version_aldpro" == 2.4.0 ]; then #репы 2.4.0 и 1.7.6   echo $passwd | sudo -S bash -c "echo -e 'deb https://dl.astralinux.ru/aldpro/frozen/01/2.4.0/ 1.7_x86-64 main base' > /etc/apt/sources.list.d/aldpro.list"   echo $passwd | sudo -S bash -c "echo -e 'deb https://dl.astralinux.ru/astra/frozen/1.7_x86-64/1.7.6/repository-base 1.7_x86-64 main contrib non-free' > /etc/apt/sources.list"    echo $passwd | sudo -S bash -c "echo -e 'deb https://dl.astralinux.ru/astra/frozen/1.7_x86-64/1.7.6/repository-update 1.7_x86-64 main contrib non-free' >> /etc/apt/sources.list"      else #репы 2.2.1 и 1.7.4   echo $passwd | sudo -S bash -c "echo -e 'deb https://download.astralinux.ru/aldpro/frozen/01/2.2.1 1.7_x86-64 main base' > /etc/apt/sources.list.d/aldpro.list"   echo $passwd | sudo -S bash -c "echo -e 'deb http://dl.astralinux.ru/astra/frozen/1.7_x86-64/1.7.4/repository-extended 1.7_x86-64 main contrib non-free' > /etc/apt/sources.list"    echo $passwd | sudo -S bash -c "echo -e 'deb http://dl.astralinux.ru/astra/frozen/1.7_x86-64/1.7.4/repository-base 1.7_x86-64 main non-free contrib' >> /etc/apt/sources.list" fi  echo $passwd | sudo -S bash -c  "echo -e 'search $big_fqdn' > /etc/resolv.conf" echo $passwd | sudo -S bash -c  "echo -e 'nameserver 127.0.0.1' >> /etc/resolv.conf"
  • Замена DNS для работы ALD Pro.

  • Добавление репозитория в зависимости от выбранной версии.

2.5. Запуск установки ALD Pro

echo $passwd | sudo -S aldpro-server-install -d "$big_fqdn" -n "$small_fqdn" -p "$passwd_dom" --ip "$ipaddres" --no-reboot --setup_syncer --setup_gc
  • Автоматический вызов официального инсталлятора с нужными параметрами.

3. Плюсы и минусы решения

Плюсы:

  • Минимальные зависимости (bash, zenity, sudo).

  • Быстрая разработка для базового сценария.

  • Подходит для разовых установок.

Минусы:

  • Сложность поддержки: при добавлении новых функций код становится запутанным.

  • Ограниченный UI: Zenity не поддерживает прогресс-бар в реальном времени или сложные формы.

  • Нет обработки ошибок: если что-то пошло не так, скрипт может «зависнуть» без понятного лога.

Глава 3: Переход на Python + Flask — Современный веб-интерфейс

И такое бывает

И такое бывает

1. Почему Bash перестал устраивать?

  • Масштабируемость: добавление новых функций (например, выбор версии ALD Pro) усложняло код.

  • Удобство: администраторы хотели удалённо запускать установку, а не подключаться к серверу через SSH.

  • Интерактивность: нужен был прогресс-бар, логирование и возможность перезапуска прерванной установки.

2. Архитектура Flask-приложения

Рассмотрим ключевые компоненты:

2.1. REST API для управления установкой

@app.route('/installation_progress') def get_progress():     if installation_progress.get('is_complete') and installation_progress.get('access_info'):         return jsonify({             'status': 'complete',             'access_info': installation_progress['access_info'],             'progress': installation_progress         })     return jsonify(installation_progress)  def start_installation(config_data):     try:         # New Step 0: Configure ALDPro repository         ...
  • Запуск установки в отдельном потоке, чтобы не блокировать веб-интерфейс.

2.2. Прогресс в реальном времени

@app.route('/submit', methods=['POST'])   # Reset progress and SAVE CONFIG DATA global installation_progress installation_progress = {     'current_step': 0,     'total_steps': 9,     'steps': [         {'name': 'Настройка репозитория ALD Pro и имени хоста', 'status': 'pending', 'start_time': None, 'end_time': None},         {'name': 'Настройка сети и файла hosts', 'status': 'pending', 'start_time': None, 'end_time': None},         {'name': 'Обновление системы', 'status': 'pending', 'start_time': None, 'end_time': None},         {'name': 'Установка компонентов ALD Pro', 'status': 'pending', 'start_time': None, 'end_time': None},         {'name': 'Установка ALD Pro', 'status': 'pending', 'start_time': None, 'end_time': None},         {'name': 'Отключение DNSSEC', 'status': 'pending', 'start_time': None, 'end_time': None},         {'name': 'Перезапуск служб', 'status': 'pending', 'start_time': None, 'end_time': None},         {'name': 'Очистка', 'status': 'pending', 'start_time': None, 'end_time': None},     ],     'start_time': None,     'end_time': None,     'is_complete': False,     'error': None,     'config_data': config_data  # Сохраняем конфигурацию для возможного повтора } threading.Thread(target=start_installation, args=(config_data,)).start() return render_template('progress.html', reboot_info=REBOOT_INFO)
  • Frontend (progress.html) опрашивает этот endpoint и обновляет прогресс.

2.3. Основной процесс установки

@app.route('/installation_progress') def get_progress():     if installation_progress.get('is_complete') and installation_progress.get('access_info'):         return jsonify({             'status': 'complete',             'access_info': installation_progress['access_info'],             'progress': installation_progress         })     return jsonify(installation_progress)  def start_installation(config_data):     try:         # New Step 0: Configure ALDPro repository         update_progress(0, 'in_progress')         time.sleep(1)                  version = config_data['version_aldpro']         repo_content = ""                  if version == "2.5.0":             repo_content = "deb https://dl.astralinux.ru/aldpro/frozen/01/2.5.0/ 1.7_x86-64 main base"         elif version == "2.4.1":             repo_content = "deb https://dl.astralinux.ru/aldpro/frozen/01/2.4.1/ 1.7_x86-64 main base"         elif version == "2.4.0":             repo_content = "deb https://dl.astralinux.ru/aldpro/frozen/01/2.4.0/ 1.7_x86-64 main base"         elif version == "2.3.0":             repo_content = "deb https://dl.astralinux.ru/aldpro/frozen/01/2.3.0 1.7_x86-64 main base"         elif version == "local":             repo_content = config_data['local_repo_path']         else:             raise Exception(f"Неизвестная версия ALDPro: {version}")                  # Write repository file         with open("/etc/apt/sources.list.d/aldpro.list", "w") as f:             f.write(repo_content + "\n")           # Меняем имя хоста         fqdn = config_data.get('fqdn', '')         if fqdn:             cmd = f"hostnamectl set-hostname {fqdn}"             success, output = run_command(cmd)             if not success:                 raise Exception(f"Ошибка смены имени хоста: {output}")                           update_progress(0, 'completed')         time.sleep(1)                  # Step 1: Network config         update_progress(1, 'in_progress')         time.sleep(1)             # Определяем активный сетевой интерфейс         get_active_iface_cmd = "ip route | grep default | awk '{print $5}' | head -n 1"         success, output = run_command(get_active_iface_cmd)         if not success or not output.strip():             raise Exception("Не удалось определить активный сетевой интерфейс")                  active_interface = output.strip()         # Проверяем, активен ли NetworkManager         nm_active = False         check_nm_cmd = "systemctl is-active NetworkManager"         success, output = run_command(check_nm_cmd)                  if success and output.strip() == "active":             nm_active = True                  commands = []                  # Если NetworkManager активен, добавляем команды для его отключения         if nm_active:             commands.extend([                 "systemctl stop NetworkManager",                 "systemctl disable NetworkManager",                 "systemctl mask NetworkManager",                 # Ждем немного, чтобы служба точно остановилась                 "sleep 2"             ])                  # Настраиваем статический IP в /etc/network/interfaces         interfaces_content = f"""# This file describes the network interfaces available on your system source /etc/network/interfaces.d/*  # The loopback network interface auto lo iface lo inet loopback auto {active_interface} iface {active_interface} inet static address {config_data['ipaddres']} netmask {config_data['mask']} gateway {config_data['gateway']}  source /etc/network/interfaces.d/*.cfg"""                  # Добавляем остальные команды настройки сети         commands.extend([             f"echo '{interfaces_content}' > /etc/network/interfaces",             f"sed -i 's/dns-nameservers {config_data.get('dns', '')}/dns-nameservers 127.0.0.1/g' /etc/network/interfaces",             f"echo -e 'search {config_data.get('big_fqdn', '')}' > /etc/resolv.conf",             f"echo -e 'nameserver {config_data.get('dns', '')}' >> /etc/resolv.conf",             # Очистка старых записей localhost             "sed -i '/^127\\.0\\.1\\.1/d' /etc/hosts",             # Добавление новых записей             f"echo '{config_data['ipaddres']} {config_data['fqdn']} {config_data['small_fqdn']}' >> /etc/hosts",             # Перезапускаем сеть для применения изменений             "systemctl restart networking.service",             # Ждем применения настроек сети             "sleep 5"         ])                  for cmd in commands:             success, output = run_command(cmd)             if not success:                 # Для systemctl mask ошибка может быть ожидаемой, если служба уже замаскирована                 if not (nm_active and "mask" in cmd and "is already masked" in output):                     raise Exception(f"Ошибка настройки сети: {output}")             time.sleep(0.5)          update_progress(1, 'completed')         time.sleep(1)          # и остальные шаги 
  • Каждый этап фиксируется в объекте installation_progress.

  • При ошибке можно перезапустить конкретный шаг.

2.4. Валидация данных

<div class="form-group">     <label for="passwd_dom">Пароль администратора домена:         <span class="help-icon">?             <span class="tooltip">Пароль должен содержать: минимум 8 символов, заглавные и строчные буквы, цифры</span>         </span>     </label>     <input type="password" id="passwd_dom" name="passwd_dom" value="{{ config.passwd_dom }}"          pattern="(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{8,}"          title="Пароль должен содержать минимум 8 символов, включая заглавные и строчные буквы, и цифры"          required         oninput="validatePassword()">          <div class="password-requirements">         <div class="requirement">             <span id="length-icon" class="requirement-icon">❌</span>             <span>Минимум 8 символов</span>         </div>         <div class="requirement">             <span id="uppercase-icon" class="requirement-icon">❌</span>             <span>Хотя бы одна заглавная буква</span>         </div>         <div class="requirement">             <span id="lowercase-icon" class="requirement-icon">❌</span>             <span>Хотя бы одна строчная буква</span>         </div>         <div class="requirement">             <span id="number-icon" class="requirement-icon">❌</span>             <span>Хотя бы одна цифра</span>         </div>     </div> </div>
  • Проверка паролей перед запуском.

3. Преимущества перед Bash

Гибкость:

  • Можно добавлять новые функции без переписывания всей логики.

  • Поддержка разных версий ALD Pro через конфигурацию.

Удобство:

  • Веб-интерфейс доступен с любого устройства в сети.

  • Прогресс-бар, логи и возможность повтора неудачного шага.

Надёжность:

  • Обработка ошибок на каждом этапе.

  • Возможность перезапуска прерванной установки.

4. Итог

Переход на Python + Flask превратил простой скрипт в полноценный инструмент администрирования, который:

  • экономит время,

  • уменьшает количество ошибок,

  • даёт больше контроля над процессом.

5. Новый интерфейс

проверка исходной системы

проверка исходной системы
дисклеймер

дисклеймер
форма для заполнения

форма для заполнения
прогресс бар

прогресс бар

Источники

Если вы дочитали до конца, то исходники можно посмотреть:

GitFlick — исходники кода, и собранные deb-пакеты;

YouTube — видео по работе web-версии установщика;

RuTube — видео на отечественной площадке по работе web-версии установщика;


ссылка на оригинал статьи https://habr.com/ru/articles/930698/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *