#!/bin/bash

# Скрипт помогает установить пакеты SoftWLC плейбуками Ansible
# Предварительно устанавливает необходимые пакеты на хост, включая докер для запуска Ansible
# После происходит первоналаьная настройка, с указанием IP-адреса хоста, на который будет установлен SoftWLC (включая саму машину с Ansible) и данных для входа по SSH для Ansible.
# Затем уже Ansible последовательно устанавливает и настраивает все нужные пакеты комплекса на указанном в предыдущем шаге хосте (включая установку Docker в случае отсутствия):
#
# eltex-ems-core            : Серверная и клиентская часть СУ EMS
# eltex-ems-vsftpd          : Серверная и клиентская часть СУ EMS
# eltex-ems-tftpd           : Серверная и клиентская часть СУ EMS
# eltex-radius              : RADIUS-сервер
# eltex-radius-exporter     : Экспортер метрик сервера RADIUS
# eltex-radius-nbi          : Северный мост (northbound) для стыка SoftWLC с вышестоящими OSS/BSS
# eltex-ngw                 : Сервис, предоставляющий возможность отправки уведомлений (СМС, звонков), используемый другими пакетами комплекса
# eltex-apb                 : Сервис для взаимодействия точек доступа
# eltex-pcrf                : Служба управления политиками доступа (используется BRAS)
# eltex-mercury             : Сервис по управлению Hotspot пользователями
# eltex-portal              : Портал для авторизации клиентов WiFi в схеме 'Hotspot'
# eltex-portal-constructor  : Web-приложение для создания и редактирования порталов для авторизации
# eltex-wifi-cab            : Личный кабинет оператора услуги Wi-Fi
# eltex-doors               : Обеспечивает авторизацию пользователя/сервиса внутри ядра (внутреннее взаимод.)
# eltex-bruce               : Сервис-планировщик задач, обеспечивает запуск задач по установленному расписанию
# eltex-jobs                : Сервис-исполнитель задач, выполняет задачи, запущенные eltex-bruce
# eltex-disconnect-service  : Сервис обеспечивает немедленное прерывание сессии пользователя
# eltex-johnny              : REST API к Mercury для управления Enterprise и Hotspot пользователями
# eltex-logging-service     : Микросервис журналирования операций
# eltex-airtune             : Сервис обеспечивает RRM (Radio Resource Management) и Client Load Balancing
# eltex-wids                : Внутренний сервис точки доступа (ТД) по предотвращению и обнаружению вторжений в беспроводную сеть
# eltex-bob                 : Сервис предназначенный для выгрузки  информации о  ТД в формате JSON  в систему мониторинга
# eltex-mariadb             : Сервис, включающий в себя как саму базу данных, так и параметры и скрипты конфигурации БД
# eltex-nginx               : Сервер nginx
# eltex-syslog-ng           : Сервис логирования
# eltex-cron                : Сервис предназначен для выполнения cron-скриптов создания бэкапа, проверки резервирования и т.д.
# eltex-proxysql *          : Сервис обеспечивает связь с базой данных (eltex-mariadb)
# * - устанавливаются по умолчанию при выборе многохостовой установки (reservation/replication)

# Версия: SoftWLC 1.37, EMS 3.41
# Целевая ОС: Ubuntu (jammy 22.04, focal 20.04), AstraLinux (1.7.3, 1.7.4, 1.7.5), RedOS (7.3.1 7.3.4 7.3.5)
# Автор: Столетний СО
# ООО Предприятие Элтекс
# Новосибирск, 2025

# Модификаторы запуска скрипта:
# --default-install               default SoftWLC install (1 host with SoftWLC)
# --reservation-install           reservation SoftWLC install (2 hosts with SoftWLC)
# --replication-galera-install    replication SoftWLC install (2 hosts with SoftWLC and 3 hosts with databases)

# --migrate-from-debs             migrate configs from debs to docker
# --migrate-db-backup             migrate databse from debs to docker (take backup)
# --migrate-db-restore            migrate databse from debs to docker (restore backup)

# --install-docker                install Docker to localhost
# --start-ansible                 run Ansible container

# --test-ports                    check if the application ports are open
# --backup                        backup all files of SoftWLC to archive
# --restore                       extract backup archive
# --run                           run containers
# --stop                          stop containers
# --delete                        delete containers
# --recreate-service <SERVICE>    recreates specified service
# --restore-galera                recover a Galera cluster after all nodes have crashed
# --generate-keys                 generate new keys for services and restart them
# --download, -d                  download, export and extract application archive with project
# --update-ems-licence            update ems/airtune licence
# --update-wifi-cab-licence       update wifi-cab licence and incremental licence
# --update-radius-certs           update eltex-radius certificate
# --change-password               change service users passwords in configuration files
# --reconfigure                   reconfiguring softwlc services using new ips
# --configure-radius-proxy        setup radius proxy configuration

# --install-peeper-client         install peeper client

# --diff                          show configuration changes between Ansible and SoftWLC
# --update, -u                    update SoftWLC system
# --help, -h                      show help
# --version, -v                   show current version
#
# Запросы при установке укажите согласно требованиям документации, либо по необходимости


red=$(tput setaf 1)
green=$(tput setaf 2)
yellow=$(tput setaf 3)
reset=$(tput sgr 0)

# Текущая директория скрипта
current_dir=$(pwd)
# Директория с бэкапом конфигурации Ansible
backup_dir="$current_dir/backup"
is_already_backuped=false

# Весрии
SOFTWLC_VERSION="1.37"
EMS_VERSION="3.41"
AIRTUNE_VERSION="1.8.0"

ansible_script_version="
SoftWLC Ansible Script
От 15 апреля 2026 года
"

# Директория текущей версии softwlc ansible
CURR_FILE_VERSION_NAME="softwlc-ansible-${SOFTWLC_VERSION}"
# Путь до директории с наcтройками пользователя (my_softwlc)
INVENTORY_PATH="${current_dir}/${CURR_FILE_VERSION_NAME}/inventory"
# Путь до директории с логами установки и запуска
LOG_DIR_PATH="${current_dir}/softwlc_ansible_logs"


# Запрет работы не от root
if [[ $(id -u) -ne 0 ]]; then
    echo -e "${red}This script can only be run as root${reset}"
    exit 1
fi

# ================================= Трап обработчик завершения скрипта ================================
cleanup() {
    recover_update_flags
    recover_close_loop_flags
    recover_softwlc_installation
    exit 1
}
trap cleanup EXIT

# ======================================== Логирование скрипта ========================================
# Включение дебаг логов скрипта (bash -x)
SCRIPT_LOG_LEVEL_DEBUG=false

# Создание директории с логами
mkdir -p $LOG_DIR_PATH
# Лог файл
LOGFILE_SCRIPT="${LOG_DIR_PATH}/eltex_softwlc_helper_ansible_$(date +%Y%m%d%H%M%S).log"
# Текущая дата для логирования
date_local=$(date '+%Y-%m-%d_%H:%M:%S')

# Логирование действий скрипта (скрипт + ансибл)
exec 3>&1
exec > >(tee >(sed -r "s/\x1B(\[[0-9;]*[A-Za-z]|\(B|=|>|\][^\a]*\a)//g" >> "${LOGFILE_SCRIPT}") >&3) 2>&1
exec 19>>"${LOGFILE_SCRIPT}"
BASH_XTRACEFD=19
if ${SCRIPT_LOG_LEVEL_DEBUG}; then
    set -x
fi

# Добавление параметров запуска в начало лог файла
args="$*"
echo "[$date_local] eltex-softwlc-helper-ansible run with: $args" >> "${LOGFILE_SCRIPT}"
# ======================================================================================================


# Public docker image registry
ELTEX_PUBLIC_REGISTRY="hub.eltex-co.ru/softwlc"
# Private docker image registry
ELTEX_PRIVATE_REGISTRY_RELEASE="secret"


# Public apt repository (need for download ansible playbook archive)
ELTEX_PUBLIC_REPO="https://archive.eltex-co.ru/wireless"
# Private apt repository (need for download ansible playbook archive)
ELTEX_PRIVATE_REPO="secret"


# Переменная, которая является рабочей. Внутри скрипта работа идёт с ней в зависимости от параметров вызова скрипта
ELTEX_REPO=${ELTEX_PUBLIC_REPO}
# Переменная, которая является рабочей. Внутри скрипта работа идёт с ней в зависимости от параметров вызова скрипта
ELTEX_DOCKER_REGISTRY=${ELTEX_PUBLIC_REGISTRY}


help="
Options:
--default-install               default SoftWLC install (1 host with SoftWLC)
--reservation-install           reservation SoftWLC install (2 hosts with SoftWLC)
--replication-galera-install    replication SoftWLC install (2 hosts with SoftWLC and 3 hosts with databases)

--migrate-from-debs             migrate configs from debs to docker
--migrate-db-backup             migrate databse from debs to docker (take backup)
--migrate-db-restore            migrate databse from debs to docker (restore backup)

--install-docker                install Docker to localhost
--start-ansible                 run Ansible container

--test-ports                    check if the application ports are open
--backup                        backup all files of SoftWLC to archive
--restore                       extract backup archive
--run                           run containers
--stop                          stop containers
--delete                        delete containers
--recreate-service <SERVICE>    recreates specified service
--restore-galera                recover a Galera cluster after all nodes have crashed
--generate-keys                 generate new keys for services and restart them
--download, -d                  download, export and extract application archive with project
--update-ems-licence            update ems/airtune licence
--update-wifi-cab-licence       update wifi-cab licence and incremental licence
--update-radius-certs           update eltex-radius certificate
--change-password               change service users passwords in configuration files
--reconfigure                   reconfiguring softwlc services using new ips
--configure-radius-proxy        setup radius proxy configuration

--install-peeper-client         install peeper client

--diff                          show configuration changes between Ansible and SoftWLC
--update, -u                    update SoftWLC system
--close-loop, -cl               setup config to use in close loop
--help, -h                      show help
--version, -v                   show current version

"
# -------------------------------------------- Close loop check ---------------------------------------------

function close_loop_check_packages() {
    local packages_info=$1
    if ${need_close_loop_setup}; then
        echo -e "${red}Using a closed-loop installation without pre-installed packages:
        \r${packages_info}${reset}"
        exit 1
    fi
}

# ----------------------------------- Check and install required packages -----------------------------------

function require_package() {
    pkgs=("$@")
    if ${need_close_loop_setup}; then
        close_loop_check_packages $pkgs
    fi
    if [[ ${DISTR_ID} == "RED SOFT" ]]; then
        dnf check-update
        for pkg in "${pkgs[@]}"; do
            if ! rpm -q $pkg; then
                echo -e "${green}Installing ${pkg}...${reset}"
                dnf install -y $pkg
            else
                echo -e "${green}${pkg} is already installed${reset}"
            fi
        done
    else
        apt update
        for pkg in "${pkgs[@]}"; do
            if ! dpkg -s "$pkg" | grep "install ok installed"; then
                echo -e "${green}Installing ${pkg}...${reset}"
                apt install -y $pkg
            else
                echo "${green}${pkg} is already installed${reset}"
            fi
        done
    fi
}

# ----------------------------------------- Check Docker on host --------------------------------------------

function check_docker() {
    if ! command -v docker &>/dev/null || ! command -v docker compose &>/dev/null; then
        echo -e "${yellow}docker/docker-compose is not installed${reset}"
        need_install_docker_host=true
        return
    fi

    if command -v whereis &>/dev/null; then
        if [[ $(whereis docker 2>/dev/null) =~ "snap" ]]; then
            echo -e "${red}docker/docker-compose is installed in the snap version. Please remove it manually${reset}"
            exit 1
        fi
    fi
    # Last docker compose for:
    # RedOS 8           : 5.0.2
    # RedOS 7           : 5.0.2
    # Ubuntu 22.04      : 5.1.0
    # AstraLinux 1.8    : 5.1.0
    # AstraLinux 1.7    : 2.39.2
    REQUIRED_COMPOSE_VERSION="5.0.0"
    local DISTR_ID=$(lsb_release -i --short 2>/dev/null | tail -n 1)
    if [[ "$DISTR_ID" =~ "Astra" ]]; then
        local DISTR_VER=$(lsb_release -r --short 2>/dev/null | tail -n 1)
        if [[ "$DISTR_VER" =~ "1.7" ]]; then
            REQUIRED_COMPOSE_VERSION="2.38.0"
        fi
    fi
    INSTALLED_COMPOSE_VERSION=$(docker compose version | awk '{print $4}' | tr -d ',abcdefghijklmnopqrstuvwxyz')
    if ! [[ "$(printf '%s\n' "$REQUIRED_COMPOSE_VERSION" "$INSTALLED_COMPOSE_VERSION" | sort -V | head -n1)" == "$REQUIRED_COMPOSE_VERSION" ]]; then
        echo -e "${yellow}docker-compose version ${INSTALLED_COMPOSE_VERSION} does not match required version ${REQUIRED_COMPOSE_VERSION}${reset}"
        need_install_docker_host=true
        return
    fi
}

# -------------------------------------- Install Docker on host by OS ---------------------------------------

function install_docker_ubuntu() {
    echo -e "${green}Install docker for Ubuntu${reset}"

    echo -e "${yellow}Warning!${reset}"
    echo -n "This step will stop the docker service during the upgrade, including all running SoftWLC services. Continue? [y/N] (default=\"N\"): "
    read ans
    if [[ "$ans" =~ ^(y|Y|д|Д)$ ]]; then
        echo "Remove unofficial docker versions..."
        apt-get remove -y docker.io docker-compose docker-compose-v2 docker-doc podman-docker containerd runc
        DOCKER_CONFIG="${DOCKER_CONFIG:-$HOME/.docker}"
        rm -f "$DOCKER_CONFIG"/cli-plugins/docker-compose*
        rm -f /usr/local/lib/docker/cli-plugins/docker-compose*

        echo -e "\nInstalling required packages"
        require_package curl

        echo -e "\nInstall official docker and docker compose..."
        curl -fsSL https://get.docker.com -o /tmp/get-docker.sh
        sh /tmp/get-docker.sh

        systemctl enable docker
        systemctl restart docker

        echo -e "${green}\nInstallation complete${reset}"
    else
        echo -e "${yellow}\nInstallation aborted${reset}"
        exit 1
    fi
}

function install_docker_redos() {
    echo -e "${green}Install docker for RedOS${reset}"

    echo -e "${yellow}Warning!${reset}"
    echo -n "This step will stop the docker service during the upgrade, including all running SoftWLC services. Continue? [y/N] (default=\"N\"): "
    read ans
    if [[ "$ans" =~ ^(y|Y|д|Д)$ ]]; then
        echo "Remove unofficial docker versions..."
        dnf remove -y docker docker-client docker-client-latest docker-common docker-latest docker-latest-logrotate docker-logrotate docker-engine podman
        DOCKER_CONFIG="${DOCKER_CONFIG:-$HOME/.docker}"
        rm -f "$DOCKER_CONFIG"/cli-plugins/docker-compose*
        rm -f /usr/local/lib/docker/cli-plugins/docker-compose*

# ------------------ Install Docker from Docker repo ------------------
#        if rpm -q docker-compose &>/dev/null; then
#            dnf remove -y docker-ce docker-ce-cli docker-compose
#        fi

#        echo -e "\nInstalling required packages"
#        require_package curl dnf-plugins-core
#        dnf config-manager --add-repo https://download.docker.com/linux/rhel/docker-ce.repo

#        echo -e "\nInstall official docker and docker compose..."
#        dnf install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin --disablerepo=* --enablerepo=docker-ce-stable --allowerasing
# ---------------------------------------------------------------------

        echo -e "\nInstalling required packages"
        require_package curl

        echo -e "\nInstall official docker and docker compose..."
        require_package docker-ce docker-ce-cli docker-compose

        systemctl enable docker
        systemctl restart docker

        echo -e "${green}\nInstallation complete${reset}"
    else
        echo -e "${yellow}\nInstallation aborted${reset}"
        exit 1
    fi
}

function install_docker_astralinux() {
    echo -e "${green}Install docker for Astra Linux${reset}"

    echo -e "${yellow}Warning!${reset}"
    echo -n "This step will stop the docker service during the upgrade, including all running SoftWLC services. Continue? [y/N] (default=\"N\"): "
    read ans
    if [[ "$ans" =~ ^(y|Y|д|Д)$ ]]; then
        echo "Remove unofficial docker versions..."
        local DISTR_VER=$(lsb_release -r --short 2>/dev/null | tail -n 1)
        if [[ "$DISTR_VER" =~ "1.7" ]]; then
            apt-get remove -y docker-doc docker-compose podman-docker
        elif [[ "$DISTR_VER" =~ "1.8" ]]; then
            DOCKER_CONFIG="${DOCKER_CONFIG:-$HOME/.docker}"
            rm -f "$DOCKER_CONFIG"/cli-plugins/docker-compose*
            rm -f /usr/local/lib/docker/cli-plugins/docker-compose*
            apt-get remove -y docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc
        else
            echo -e "${red}\nUnsupported version of Astra Linux${reset}"
            exit 1
        fi

        echo -e "\nInstalling required packages"
        require_package curl

        echo -e "\nInstall official docker and docker compose..."
        if [[ "$DISTR_VER" =~ "1.7" ]]; then
            echo "Configure AstraLinux deb repositories"
            repos=(
            "deb https://download.astralinux.ru/astra/stable/1.7_x86-64/repository-main/ 1.7_x86-64 main contrib non-free"
            "deb https://download.astralinux.ru/astra/stable/1.7_x86-64/repository-update/ 1.7_x86-64 main contrib non-free"
            "deb https://download.astralinux.ru/astra/stable/1.7_x86-64/repository-base/ 1.7_x86-64 main contrib non-free"
            "deb https://download.astralinux.ru/astra/stable/1.7_x86-64/repository-extended/ 1.7_x86-64 main contrib non-free"
            )

            if [ -f /etc/apt/sources.list.d/ansible.list ]; then
                for repo_index in "${!repos[@]}"; do
                    if grep -Fxq "${repos[$repo_index]}" /etc/apt/sources.list.d/ansible.list; then
                        unset repos["$repo_index"]
                    fi
                done
            fi

            if [ ${#repos[@]} -ne 0 ]; then
                for repo in "${repos[@]}"; do
                    echo "$repo" >>/etc/apt/sources.list.d/ansible.list
                done
                echo "SourceList was updated"
            fi
            require_package docker.io docker-compose-v2
        elif [[ "$DISTR_VER" =~ "1.8" ]]; then
            curl -fsSL https://get.docker.com -o get-docker.sh
            sh get-docker.sh
        else
            echo -e "${red}\nUnsupported version of Astra Linux${reset}"
            exit 1
        fi

        systemctl enable docker
        systemctl restart docker

        echo -e "${green}\nInstallation complete${reset}"
    else
        echo -e "${yellow}\nInstallation aborted${reset}"
        exit 1
    fi
}

# ----------------------------------------- Install Docker on host ------------------------------------------

function install_docker() {
    if ! ${need_install_docker_host}; then
        return
    fi
    if ${need_close_loop_setup}; then
        close_loop_check_packages "docker, docker compose"
    fi
    DISTR_ID=$(lsb_release -i --short 2>/dev/null | tail -n 1)

    case ${DISTR_ID} in
    "RED SOFT")
        install_docker_redos
        ;;
    "AstraLinux" | "Astra")
        install_docker_astralinux
        ;;
    "Ubuntu")
        install_docker_ubuntu
        ;;
    esac
}

# -------------------------------------- Check ansible playbook status --------------------------------------

check_ansible_status() {
    local exit_code=$1
    local task_name="$2"

    if [[ $exit_code -ne 0 ]]; then
        echo -e "${red}\nERROR: Task \"${task_name}\" throw error code. Check Ansible output for details. Abort...\n${reset}"
        exit 1
    fi
}

# --------------------------------------- Download Ansible archive ------------------------------------------

function download_softwlc_ansible(){
    if ! ${need_download_ansible} && ! ${need_default_install} && ! ${need_reservation_install} && ! ${need_replication_galera_install}; then
        return
    fi
    if [ ! -f "softwlc-ansible-${SOFTWLC_VERSION}.tar.gz" -a ! -f "softwlc-ansible-latest.tar.gz" ]; then
        if ${need_close_loop_setup}; then
            close_loop_check_packages "softwlc-ansible-${SOFTWLC_VERSION}.tar.gz"
        fi
    	echo -e "\n${green}Downloading SoftWLC Ansible playbook${reset}"
    	require_package wget
    	wget $ELTEX_REPO/help/softwlc-$SOFTWLC_VERSION/softwlc-ansible-$SOFTWLC_VERSION.tar.gz
    	if [ $? -ne 0 ]; then
    	    echo -e "${red}Error while trying to load playbooks. Check the site availability or your internet connection.${reset}"
    	    exit 1
        fi
    fi
    echo -e "\n${green}Extract SoftWLC installer...${reset}"
    # Поиск старой директории и переименовние в версию текущего скрипта для замены параметров (update)
    CURR_INSTALLED_FILE_VERSION_NAME=$(find . -maxdepth 1 -type d -name "softwlc-ansible-*" | sed 's|^\./||' | sort -V | tail -n 1)
    if [[ "$CURR_INSTALLED_FILE_VERSION_NAME" =~ "softwlc-ansible" && "$CURR_INSTALLED_FILE_VERSION_NAME" != "$CURR_FILE_VERSION_NAME" ]]; then
        mv ${CURR_INSTALLED_FILE_VERSION_NAME} ${CURR_FILE_VERSION_NAME}
    fi
    if [ ! -d $current_dir/$CURR_FILE_VERSION_NAME/ ]; then
        tar -xzf $CURR_FILE_VERSION_NAME.tar.gz
        mv $current_dir/$CURR_FILE_VERSION_NAME/docker-compose.yml $current_dir/docker-compose.yml
    else
        echo -n "You already have a softwlc-ansible configuration directory. Do you want to replace this configuration with default parameters from the archive? [y/N] (default=\"N\"): "
        read ans
        if [[ "$ans" =~ ^(y|Y|д|Д)$ ]]; then
            backup_softwlc_ansible_files
            find $CURR_FILE_VERSION_NAME \
                ! -path "${CURR_FILE_VERSION_NAME}" \
                ! -path "${CURR_FILE_VERSION_NAME}/inventory" \
                ! -path "${CURR_FILE_VERSION_NAME}/inventory/my_softwlc" \
                ! -path "${CURR_FILE_VERSION_NAME}/inventory/my_softwlc/*" \
                ! -path "${CURR_FILE_VERSION_NAME}/eltex_images.tar" \
                -delete
            tar -xzf $CURR_FILE_VERSION_NAME.tar.gz
            mv $current_dir/$CURR_FILE_VERSION_NAME/docker-compose.yml $current_dir/docker-compose.yml
        fi
    fi
    echo -e "\n${green}Prepare my_softwlc directory...${reset}"
    # Копирование inventory/sample => inventory/my_softwlc
    if [ ! -d $INVENTORY_PATH/my_softwlc ]; then
        cp -rfp $INVENTORY_PATH/sample $INVENTORY_PATH/my_softwlc
    else
        echo -n "You already have my_softwlc configurations. Do you want to update this configuration with default parameters from inventory/sample ? [y/N] (default=\"N\"): "
        read ans
        if [[ "$ans" =~ ^(y|Y|д|Д)$ ]]; then
            backup_softwlc_ansible_files
            # Если ответ да - делаем бэкапы и переходим к меррджу конфигурации
            cp $INVENTORY_PATH/my_softwlc/group_vars/all.yml $INVENTORY_PATH/my_softwlc/group_vars/all.yml.backup
            cp $INVENTORY_PATH/my_softwlc/inventory.yml $INVENTORY_PATH/my_softwlc/inventory.yml.backup
            # Спрашиваем пользователя, что делаем с конфигой - тупая замена без сохранения параметров через cp или sdiff - ручная, почти построчная, замена
            echo -e "Use sdiff to replace the configuration ?
            \r 'Y' - Using 'sdiff' to manually replace parameters. (Allows you to choose which parameters to replace with defaults and which to keep)
            \r 'N' - Using 'cp' to copy default settings. (Overwriting the current configuration with default settings)"
            echo -n "[Y/n] (default=\"Y\"): "
            read ans
            if [[ "$ans" =~ ^(n|N|н|Н)$ ]]; then
                cp -rfp $INVENTORY_PATH/sample/. $INVENTORY_PATH/my_softwlc
            else
                echo -e "${yellow}Use the 'L' key to select the old configuration or the 'R' key to select the new configuration:${reset}"
                echo -e "${green}#====================== Users all.yml ======================#   #===================== Default all.yml =====================#${reset}"
                sdiff --suppress-common-lines --minimal -o $INVENTORY_PATH/my_softwlc/group_vars/all.yml.new $INVENTORY_PATH/my_softwlc/group_vars/all.yml $INVENTORY_PATH/sample/group_vars/all.yml
                local exit_code=$?
                # Код возврата 0 - файлы идентичны, 1 - различаются, 2 - разлиные ошибки (включая прерывание со стороны пользователя)
                if [[ $exit_code -eq 0 || $exit_code -eq 1 ]]; then
                    mv $INVENTORY_PATH/my_softwlc/group_vars/all.yml.new $INVENTORY_PATH/my_softwlc/group_vars/all.yml
                fi
                echo -e "${green}#=================== Users inventory.yml ===================#   #================== Default inventory.yml ==================#${reset}"
                sdiff --suppress-common-lines --minimal -o $INVENTORY_PATH/my_softwlc/inventory.yml.new $INVENTORY_PATH/my_softwlc/inventory.yml $INVENTORY_PATH/sample/inventory.yml
                local exit_code=$?
                # Код возврата 0 - файлы идентичны, 1 - различаются, 2 - разлиные ошибки (включая прерывание со стороны пользователя)
                if [[ $exit_code -eq 0 || $exit_code -eq 1 ]]; then
                    mv $INVENTORY_PATH/my_softwlc/inventory.yml.new $INVENTORY_PATH/my_softwlc/inventory.yml
                fi
            fi
        else
            if [ ! -f $INVENTORY_PATH/my_softwlc/inventory.yml ]; then
                cp -fp $INVENTORY_PATH/sample/inventory.yml $INVENTORY_PATH/my_softwlc/inventory.yml
            fi
            if [ ! -f $INVENTORY_PATH/my_softwlc/group_vars/all.yml ]; then
                cp -fp $INVENTORY_PATH/sample/group_vars/all.yml $INVENTORY_PATH/my_softwlc/group_vars/all.yml
            fi
        fi
    fi
    # Изменение пути к файлу inventory в конфиге ansible
    sed -i "s|^inventory =.*|inventory = inventory\/my_softwlc\/inventory.yml|" $current_dir/$CURR_FILE_VERSION_NAME/ansible.cfg
    
    echo -e "\n${green}Ansible playbooks preparation is complete${reset}"

    if ${need_close_loop_setup}; then
        if [[ ! -f "eltex_images.tar" && ! -f "./softwlc-ansible-${SOFTWLC_VERSION}/eltex_images.tar" ]]; then
            close_loop_check_packages "eltex_images.tar"
        fi
        if [ -f "eltex_images.tar" ]; then
            mv eltex_images.tar ./softwlc-ansible-${SOFTWLC_VERSION}/eltex_images.tar
        fi
        if [[ "${need_update}" == "true" || ! -f "./softwlc-ansible-${SOFTWLC_VERSION}/.local_images_loaded" ]]; then
            echo -e "\n${green}Extract SoftWLC images...${reset}"
            docker load --input ./softwlc-ansible-${SOFTWLC_VERSION}/eltex_images.tar
            touch "./softwlc-ansible-${SOFTWLC_VERSION}/.local_images_loaded"
        fi
        echo -e "\n${green}The SoftWLC Docker images for the close loop have been loaded successfully${reset}"
    fi
}

# --------------------------------------- Backup host config files -------------------------------------------

function backup_softwlc_ansible_files() {
    if ${is_already_backuped}; then
        return
    fi
    echo -n "Do you want to backup your configuration files ? [Y/n] (default=\"Y\"): "
    read ans
    if [[ "$ans" =~ ^(n|N|н|Н)$ ]]; then
        return
    fi
    mkdir -p "${backup_dir}"
    tar -C $current_dir -czf $backup_dir/backup_$(date +"%Y%m%dT%H%M%S").tar.gz $CURR_FILE_VERSION_NAME/*
    is_already_backuped=true
    echo "${green}Backup has been completed!${reset}"
}

# ------------------------------------- Preparing the inventory file -----------------------------------------

# Валидация IP адреса через regex
function valid_ipv4() {
    local ip=$1
    local stat=1
    # Проверка пустого значения
    if [[ -z $ip ]]; then
        echo -e "${red}IP address cannot be empty${reset}"
        exit 1
    fi
    # Проверка, что это не localhost
    if [[ $ip == "127.0.0.1" || $ip == "::1" ]]; then
        echo -e "${red}Cannot assign localhost IP '$1' to server IP address${reset}"
        exit 1
    fi

    if [[ $ip =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then
        OIFS=$IFS
        IFS='.'
        ip=($ip)
        IFS=$OIFS
        [[ ${ip[0]} -le 255 && ${ip[1]} -le 255 && ${ip[2]} -le 255 && ${ip[3]} -le 255 ]]

        stat=$?
    fi
    return $stat
}

function setup_reservation_ip_addresses() {
    echo -n "Enter virtual IP address (blank for last used): "
    read vip_address
    if [ -n "$vip_address" ]; then
        # Проверка введенного адреса на корректность
        if valid_ipv4 $vip_address; then
            echo -e "${green}Vitual IP set to: ${vip_address}${reset}"
            # Изменение virtual_ip в файле group_vars/all.yml с дефолтного на введенный пользователем
            sed -i "s|virtual_ip: .*|virtual_ip: ${vip_address}|" $INVENTORY_PATH/my_softwlc/group_vars/all.yml
        else
            echo -e "${red}Incorrect ip${reset}"
            exit 1
        fi
    else
        current_vip_address=$(grep 'virtual_ip:' $INVENTORY_PATH/my_softwlc/group_vars/all.yml | awk '{print $2}')
        echo -e "${green}Vitual IP set to: ${current_vip_address}${reset}"
    fi

    echo -n "Enter gateway IP address (blank for last used): "
    read gw_address
    if [ -n "$gw_address" ]; then
        # Проверка введенного адреса на корректность
        if valid_ipv4 $gw_address; then
            echo -e "${green}Gateway IP set to: ${gw_address}${reset}"
            # Изменение gateway_ip в файле group_vars/all.yml с дефолтного на введенный пользователем
            sed -i "s|gateway_ip: .*|gateway_ip: ${gw_address}|" $INVENTORY_PATH/my_softwlc/group_vars/all.yml
        else
            echo -e "${red}Incorrect ip${reset}"
            exit 1
        fi
    else
        current_gw_address=$(grep 'gateway_ip:' $INVENTORY_PATH/my_softwlc/group_vars/all.yml | awk '{print $2}')
        echo -e "${green}Gateway IP set to: ${current_gw_address}${reset}"
    fi
}

function setup_auth_creds() {
    # Проверяем, существует ли my_softwlc/inventory.yml
    if [ ! -f "${INVENTORY_PATH}/my_softwlc/inventory.yml" ]; then
        echo -e "${red}Ansible playbook for SoftWLC are not installed on the host. Run script with -d option (The inventory.yml file is missing)${reset}"
        exit 1
    fi

    # Определяем кол-во хостов в зависимоти от установки
    if ${need_default_install}; then
        echo -e "\n${green}Setup HOST for default installation${reset}"
        HOSTS=("softwlc_main")
    elif ${need_reservation_install}; then
        echo -e "\n${green}Setup HOSTS for reservation installation${reset}"
        HOSTS=("softwlc_reserv_1" "softwlc_reserv_2")
        setup_reservation_ip_addresses
    elif ${need_replication_galera_install}; then
        echo -e "\n${green}Setup HOSTS for replication Galera installation${reset}"
        HOSTS=("softwlc_1" "softwlc_2" "database_1" "database_2" "database_3")
        setup_reservation_ip_addresses
    fi

    echo -e "List of prepared hosts: "
    for i in "${!HOSTS[@]}"; do
        HOST="${HOSTS[$i]}"
        echo -e "${HOST}"
    done

    # Если не настроенно использование приватных ключей - уведомляем пользователя
    if ! grep "private_key_file" $current_dir/$CURR_FILE_VERSION_NAME/ansible.cfg > /dev/null 2>&1 && \
       ! grep "ansible_ssh_private_key_file" $INVENTORY_PATH/my_softwlc/inventory.yml > /dev/null 2>&1; then
        echo -e "
${green}Information note${reset}
Ansible uses SSH with existing OS credentials to access machines.
Next, you will be asked to provide details for connecting via SSH to the hosts you specified.
In the future, it is recommended to add the generated ssh key and use it
(via \"private_key_file\" in ansible.cfg or ansible_ssh_private_key_file in inventory.yml for example) to increase the security of the connection.
More information (Official Ansible Web site): https://docs.ansible.com/ansible/latest/reference_appendices/config.html"
    fi

    # Цикл настройки хостов - в зависимости от выбранной конфигурации будут запрошены IP адреса и данные для входа
    for i in "${!HOSTS[@]}"; do
        HOST="${HOSTS[$i]}"
        echo -e "\n${green}Setup credentials for HOST: ${HOST}${reset}"

        # Установка IP адреса для хоста ${HOST}
        echo -n "Enter address for HOST: ${HOST} (blank for last used): "
        read swlc_address
        if [ -n "$swlc_address" ]; then
            # Проверка введенного адреса на корректность
            if valid_ipv4 $swlc_address; then
                echo -e "${green}${HOST} will be configured at the: ${swlc_address}${reset}"
                # Изменение ansible_host в файле inventory с дефолтного на введенный пользователем для хоста ${HOST}
                sed -i "/${HOST}:/,/ansible_host:/ { /ansible_host:/ s/: .*/: ${swlc_address}/ }" $INVENTORY_PATH/my_softwlc/inventory.yml
            else
                echo -e "${red}Incorrect ip${reset}"
                exit 1
            fi
        else
            current_swlc_address=$(grep -A 3 $HOST $INVENTORY_PATH/my_softwlc/inventory.yml | grep 'ansible_host:' | awk '{print $2}')
            echo -e "${green}${HOST} will be configured at the: ${current_swlc_address}${reset}"
        fi

        echo -n "Enter SSH user login for HOST: ${HOST} (blank for last used): "
        read ans
        if [ -n "$ans" ]; then
            sed -i "/${HOST}:/,/ansible_user:/ { /ansible_user:/ s/: .*/: '${ans}'/ }" $INVENTORY_PATH/my_softwlc/inventory.yml
        fi
        
        # Если не настроенно использование приватных ключей - запрашиваем данные для входа для хоста ${HOST}
        if ! grep "private_key_file" $current_dir/$CURR_FILE_VERSION_NAME/ansible.cfg > /dev/null 2>&1 && \
           ! grep "ansible_ssh_private_key_file" $INVENTORY_PATH/my_softwlc/inventory.yml > /dev/null 2>&1; then

            echo -n "Enter SSH user password for HOST: ${HOST} (blank for last used): "
            read -s ans
            if [ -n "$ans" ]; then
                sed -i "/${HOST}:/,/ansible_password:/ { /ansible_password:/ s/: .*/: '${ans}'/ }" $INVENTORY_PATH/my_softwlc/inventory.yml
            fi
            echo " "

            echo -n "Enter sudo password for HOST: ${HOST} (blank for last used): "
            read -s ans
            if [ -n "$ans" ]; then
                sed -i "/${HOST}:/,/ansible_become_password:/ { /ansible_become_password:/ s/: .*/: '${ans}'/ }" $INVENTORY_PATH/my_softwlc/inventory.yml
            fi
            echo " "
        fi
    done
}

# Подготовка файла inventory
function prepare_inventory_file() {
    if ! ${need_prepare_inventory_file} && ! ${need_default_install} && ! ${need_reservation_install} && ! ${need_replication_galera_install}; then
        return
    fi
    if [ ! -f "${INVENTORY_PATH}/my_softwlc/group_vars/all.yml" ]; then
        echo -e "${red}Ansible playbook for SoftWLC are not installed on the host. Run script with -d option (The all.yml file is missing)${reset}"
        exit 1
    fi
    # Если указан флаг need_prepare_inventory_file, но не указаны need_*_install -> решаем, что используются tools -> определяем для них текущую установку
    if ${need_prepare_inventory_file} && ! ${need_default_install} && ! ${need_reservation_install} && ! ${need_replication_galera_install}; then
        set_softwlc_installation
    fi

    setup_auth_creds

    # Установка версий и доп параметров для Ansible и SoftWLC
    sed -i "s|ansible_playbook_dir: .*|ansible_playbook_dir: \"${current_dir}/${CURR_FILE_VERSION_NAME}\"|" $INVENTORY_PATH/my_softwlc/group_vars/all.yml

    sed -i "s|eltex_hub: .*|eltex_hub: ${ELTEX_DOCKER_REGISTRY}|" $INVENTORY_PATH/my_softwlc/group_vars/all.yml

    sed -i "s|swlc_version: .*|swlc_version: \"${SOFTWLC_VERSION}\"|" $INVENTORY_PATH/my_softwlc/group_vars/all.yml
    sed -i "s|ems_version: .*|ems_version: \"${EMS_VERSION}\"|" $INVENTORY_PATH/my_softwlc/group_vars/all.yml
    sed -i "s|airtune_version: .*|airtune_version: \"${AIRTUNE_VERSION}\"|" $INVENTORY_PATH/my_softwlc/group_vars/all.yml

    if ${need_default_install}; then
        # Включение параметра is_default_install глобально (отключение других GLOBAL переменных)
        sed -i "s|is_default_install: .*|is_default_install: true|" $INVENTORY_PATH/my_softwlc/group_vars/all.yml
        sed -i "s|is_reservation_install: .*|is_reservation_install: false|" $INVENTORY_PATH/my_softwlc/group_vars/all.yml
        sed -i "s|is_replication_galera_install: .*|is_replication_galera_install: false|" $INVENTORY_PATH/my_softwlc/group_vars/all.yml

    elif ${need_reservation_install}; then
        # Включение параметра is_reservation_install глобально (отключение других GLOBAL переменных)
        sed -i "s|is_default_install: .*|is_default_install: false|" $INVENTORY_PATH/my_softwlc/group_vars/all.yml
        sed -i "s|is_reservation_install: .*|is_reservation_install: true|" $INVENTORY_PATH/my_softwlc/group_vars/all.yml
        sed -i "s|is_replication_galera_install: .*|is_replication_galera_install: false|" $INVENTORY_PATH/my_softwlc/group_vars/all.yml
        # Установка подключения к БД через proxysql, установка ems_ip, external_ip через VIP
        sed -i "s|mysql_server_host: .*|mysql_server_host: eltex-proxysql|" $INVENTORY_PATH/my_softwlc/group_vars/all.yml
        sed -i "s|mysql_server_port: .*|mysql_server_port: 6033|" $INVENTORY_PATH/my_softwlc/group_vars/all.yml
        sed -i "s|ems_ip: .*|ems_ip: \"{{ virtual_ip }}\"|" $INVENTORY_PATH/my_softwlc/group_vars/all.yml
        sed -i "s|external_ip: .*|external_ip: \"{{ virtual_ip }}\"|" $INVENTORY_PATH/my_softwlc/group_vars/all.yml
        # Включение и конфигурация кластера pcrf
        softwlc_reserv_1_address=$(grep -A 3 "softwlc_reserv_1" $INVENTORY_PATH/my_softwlc/inventory.yml | grep 'ansible_host' | awk '{print $2}')
        softwlc_reserv_2_address=$(grep -A 3 "softwlc_reserv_2" $INVENTORY_PATH/my_softwlc/inventory.yml | grep 'ansible_host' | awk '{print $2}')
        sed -i "s|pcrf_cluster_enable: .*|pcrf_cluster_enable: true|" $INVENTORY_PATH/my_softwlc/group_vars/all.yml
        sed -i "s|pcrf_cluster_event_bus_public_host: .*|pcrf_cluster_event_bus_public_host: \"{{ ansible_host }}\"|" $INVENTORY_PATH/my_softwlc/group_vars/all.yml
        sed -i "s|pcrf_hz_network_public_address: .*|pcrf_hz_network_public_address: \"{{ ansible_host }}\"|" $INVENTORY_PATH/my_softwlc/group_vars/all.yml
        sed -i "s|pcrf_hz_network_join_tcp_ip_members: .*|pcrf_hz_network_join_tcp_ip_members: $softwlc_reserv_1_address,$softwlc_reserv_2_address|" $INVENTORY_PATH/my_softwlc/group_vars/all.yml
        # Включение и конфигурация кластера apb
        sed -i "s|apb_cluster_enabled: .*|apb_cluster_enabled: true|" $INVENTORY_PATH/my_softwlc/group_vars/all.yml
        sed -i "s|apb_cluster_public_node_address: .*|apb_cluster_public_node_address: \"{{ ansible_host }}\"|" $INVENTORY_PATH/my_softwlc/group_vars/all.yml
        sed -i "s|apb_cluster_members_0: .*|apb_cluster_members_0: $softwlc_reserv_1_address|" $INVENTORY_PATH/my_softwlc/group_vars/all.yml
        sed -i "s|apb_cluster_members_1: .*|apb_cluster_members_1: $softwlc_reserv_2_address|" $INVENTORY_PATH/my_softwlc/group_vars/all.yml

    elif ${need_replication_galera_install}; then
        # Включение параметра is_replication_galera_install глобально (отключение других GLOBAL переменных)
        sed -i "s|is_default_install: .*|is_default_install: false|" $INVENTORY_PATH/my_softwlc/group_vars/all.yml
        sed -i "s|is_reservation_install: .*|is_reservation_install: false|" $INVENTORY_PATH/my_softwlc/group_vars/all.yml
        sed -i "s|is_replication_galera_install: .*|is_replication_galera_install: true|" $INVENTORY_PATH/my_softwlc/group_vars/all.yml
        # Установка подключения к БД через proxysql, установка ems_ip, external_ip через VIP
        sed -i "s|mysql_server_host: .*|mysql_server_host: eltex-proxysql|" $INVENTORY_PATH/my_softwlc/group_vars/all.yml
        sed -i "s|mysql_server_port: .*|mysql_server_port: 6033|" $INVENTORY_PATH/my_softwlc/group_vars/all.yml
        sed -i "s|ems_ip: .*|ems_ip: \"{{ virtual_ip }}\"|" $INVENTORY_PATH/my_softwlc/group_vars/all.yml
        sed -i "s|external_ip: .*|external_ip: \"{{ virtual_ip }}\"|" $INVENTORY_PATH/my_softwlc/group_vars/all.yml
        # Включение и конфигурация кластера pcrf
        softwlc_1_address=$(grep -A 3 "softwlc_1" $INVENTORY_PATH/my_softwlc/inventory.yml | grep 'ansible_host' | awk '{print $2}')
        softwlc_2_address=$(grep -A 3 "softwlc_2" $INVENTORY_PATH/my_softwlc/inventory.yml | grep 'ansible_host' | awk '{print $2}')
        sed -i "s|pcrf_cluster_enable: .*|pcrf_cluster_enable: true|" $INVENTORY_PATH/my_softwlc/group_vars/all.yml
        sed -i "s|pcrf_cluster_event_bus_public_host: .*|pcrf_cluster_event_bus_public_host: \"{{ ansible_host }}\"|" $INVENTORY_PATH/my_softwlc/group_vars/all.yml
        sed -i "s|pcrf_hz_network_public_address: .*|pcrf_hz_network_public_address: \"{{ ansible_host }}\"|" $INVENTORY_PATH/my_softwlc/group_vars/all.yml
        sed -i "s|pcrf_hz_network_join_tcp_ip_members: .*|pcrf_hz_network_join_tcp_ip_members: $softwlc_1_address,$softwlc_2_address|" $INVENTORY_PATH/my_softwlc/group_vars/all.yml
        # Включение и конфигурация кластера apb
        sed -i "s|apb_cluster_enabled: .*|apb_cluster_enabled: true|" $INVENTORY_PATH/my_softwlc/group_vars/all.yml
        sed -i "s|apb_cluster_public_node_address: .*|apb_cluster_public_node_address: \"{{ ansible_host }}\"|" $INVENTORY_PATH/my_softwlc/group_vars/all.yml
        sed -i "s|apb_cluster_members_0: .*|apb_cluster_members_0: $softwlc_1_address|" $INVENTORY_PATH/my_softwlc/group_vars/all.yml
        sed -i "s|apb_cluster_members_1: .*|apb_cluster_members_1: $softwlc_2_address|" $INVENTORY_PATH/my_softwlc/group_vars/all.yml
    fi
}

# ------------------------------------- Start Ansible Docker container --------------------------------------

function start_ansible_container() {
    if ! ${need_start_ansible_container} && ! ${need_default_install} && ! ${need_reservation_install} && ! ${need_replication_galera_install}; then
        return
    fi
    if [ -f $current_dir/.env ]; then
        rm .env
    fi
    echo -e "${green}Starting Ansible docker container${reset}"

    echo "SWLC_VERSION=${SOFTWLC_VERSION}" >> .env
    echo "CURR_FILE_VERSION_NAME=${CURR_FILE_VERSION_NAME}" >> .env
    echo "ELTEX_HUB=${ELTEX_DOCKER_REGISTRY}" >> .env

    source .env
    if ${need_update}; then
        docker compose up -d --remove-orphans --force-recreate
    else
        docker compose up -d --remove-orphans
    fi

}

# ----------------------------------- Migrate configs from debs to docker -----------------------------------

# Миграция из установки в deb в docker
function ansible_migrate_from_debs(){
    if ! ${need_migrate_from_debs}; then
        return
    fi
    sed -i "s|ROOT_DIR_PATH = .*|ROOT_DIR_PATH = ${current_dir}/${CURR_FILE_VERSION_NAME}|" ${current_dir}/${CURR_FILE_VERSION_NAME}/roles/migrate_config_files/files/config.hocon
    echo -e "${green}Migrate configs from debs to docker${reset}"
    docker exec -w /ansible -it eltex-ansible ansible-playbook /ansible/softwlc_default_tools.yml -t "migrate-debs"
    check_ansible_status $? "Migrate configs from debs to docker"
}

# -------------------------------------- Migrate database create backup -------------------------------------

# Миграция из установки в deb в docker создание бэкапа бд по пути /tmp/mysql_backup.zip
function ansible_migrate_db_backup(){
    if ! ${need_migrate_backup_db}; then
        return
    fi
    echo -e "${green}Migrate database create backup${reset}"
    docker exec -w /ansible -it eltex-ansible ansible-playbook /ansible/softwlc_default_tools.yml -t "migration-backup-db"
    check_ansible_status $? "Migrate database create backup"
}

# ------------------------------------- Migrate database restore backup -------------------------------------

# Миграция из установки в deb в docker восстановление из бэкапа бд
function ansible_migrate_db_restore(){
    if ! ${need_migrate_restore_db}; then
        return
    fi
    echo -e "${green}Migrate database restore backup${reset}"
    docker exec -w /ansible -it eltex-ansible ansible-playbook /ansible/softwlc_${current_swlc_installation}_tools.yml -t "migration-restore-db"
    check_ansible_status $? "Migrate database restore backup"
}

# ---------------------------------- Install Docker and SoftWLC (default) -----------------------------------

# Установка Docker и SoftWLC (default)
function ansible_default_install(){
    if ! ${need_default_install} || [[ "$current_swlc_installation" != "none" ]]; then
        return
    fi

    # Запуск плейбука с тегом install-docker
    echo -e "${green}Install Docker${reset}"
    docker exec -w /ansible -it eltex-ansible ansible-playbook /ansible/softwlc_default_setup.yml -t "install-docker" 2>&1 | tee ${LOG_DIR_PATH}/eltex_ansible_install_docker.log
    status_code=${PIPESTATUS[0]}
    output=$(< ${LOG_DIR_PATH}/eltex_ansible_install_docker.log)

    if [[ $status_code -ne 0 && ! "$output" =~ "No such exec instance" ]]; then
        check_ansible_status $status_code "Install Docker"
    fi

    echo -e "${green}Docker installation and configuration complete. Waiting for eltex-ansible to restart...${reset}"
    # Ждём 30 секунд до запуска eltex-ansible
    for ((i=1; i < 7; i++)); do
        if [[ $(docker inspect -f '{{.State.Status}}' "eltex-ansible" 2>/dev/null) == "running" ]]; then
            break
        fi
        sleep 5
    done
    # Если контейнер не запустился - запускаем принудительно
    if [[ $(docker inspect -f '{{.State.Status}}' "eltex-ansible" 2>/dev/null) != "running" ]]; then
        docker compose up -d --remove-orphans
    fi
    # Ждём до запуска eltex-ansible
    while [[ $(docker inspect -f '{{.State.Status}}' "eltex-ansible" 2>/dev/null) != "running" ]]; do
        sleep 5
    done
    echo -e "${green}Install SoftWLC (default)${reset}"
    docker exec -w /ansible -it eltex-ansible ansible-playbook /ansible/softwlc_default_setup.yml -t "install-softwlc-default"
    check_ansible_status $? "Install SoftWLC (default)"
}

# ---------------------------------- Install Docker and SoftWLC 1+1 (reservation) -----------------------------------

# Установка Docker и SoftWLC 1+1 (reservation)
function ansible_reservation_install(){
    if ! ${need_reservation_install} || [[ "$current_swlc_installation" != "none" ]]; then
        return
    fi

    # Запуск плейбука с тегом install-docker
    echo -e "${green}Install Docker${reset}"
    docker exec -w /ansible -it eltex-ansible ansible-playbook /ansible/softwlc_reservation_setup.yml -t "install-docker" 2>&1 | tee ${LOG_DIR_PATH}/eltex_ansible_install_docker.log
    status_code=${PIPESTATUS[0]}
    output=$(< ${LOG_DIR_PATH}/eltex_ansible_install_docker.log)

    if [[ $status_code -ne 0 && ! "$output" =~ "No such exec instance" ]]; then
        check_ansible_status $status_code "Install Docker"
    fi

    echo -e "${green}Docker installation and configuration complete. Waiting for eltex-ansible to restart...${reset}"
    # Ждём 30 секунд до запуска eltex-ansible
    for ((i=1; i < 7; i++)); do
        if [[ $(docker inspect -f '{{.State.Status}}' "eltex-ansible" 2>/dev/null) == "running" ]]; then
            break
        fi
        sleep 5
    done
    # Если контейнер не запустился - запускаем принудительно
    if [[ $(docker inspect -f '{{.State.Status}}' "eltex-ansible" 2>/dev/null) != "running" ]]; then
        docker compose up -d --remove-orphans
    fi
    # Ждём до запуска eltex-ansible
    while [[ $(docker inspect -f '{{.State.Status}}' "eltex-ansible" 2>/dev/null) != "running" ]]; do
        sleep 5
    done

    echo -e "${green}Install SoftWLC (reservation)${reset}"
    docker exec -w /ansible -it eltex-ansible ansible-playbook /ansible/softwlc_reservation_setup.yml -t "install-softwlc-reservation"
    check_ansible_status $? "Install SoftWLC (reservation)"
}


# ---------------------------------- Install Docker, SoftWLC and Galera Clater (replication galera) -----------------------------------

# Установка Docker, SoftWLC и Galera Clater (replication galera)
function ansible_replication_galera_install(){
    if ! ${need_replication_galera_install} || [[ "$current_swlc_installation" != "none" ]]; then
        return
    fi

    # Запуск плейбука с тегом install-docker
    echo -e "${green}Install Docker${reset}"
    docker exec -w /ansible -it eltex-ansible ansible-playbook /ansible/softwlc_replication_setup.yml -t "install-docker" 2>&1 | tee ${LOG_DIR_PATH}/eltex_ansible_install_docker.log
    status_code=${PIPESTATUS[0]}
    output=$(< ${LOG_DIR_PATH}/eltex_ansible_install_docker.log)

    if [[ $status_code -ne 0 && ! "$output" =~ "No such exec instance" ]]; then
        check_ansible_status $status_code "Install Docker"
    fi

    echo -e "${green}Docker installation and configuration complete. Waiting for eltex-ansible to restart...${reset}"
    # Ждём 30 секунд до запуска eltex-ansible
    for ((i=1; i < 7; i++)); do
        if [[ $(docker inspect -f '{{.State.Status}}' "eltex-ansible" 2>/dev/null) == "running" ]]; then
            break
        fi
        sleep 5
    done
    # Если контейнер не запустился - запускаем принудительно
    if [[ $(docker inspect -f '{{.State.Status}}' "eltex-ansible" 2>/dev/null) != "running" ]]; then
        docker compose up -d --remove-orphans
    fi
    # Ждём до запуска eltex-ansible
    while [[ $(docker inspect -f '{{.State.Status}}' "eltex-ansible" 2>/dev/null) != "running" ]]; do
        sleep 5
    done

    echo -e "${green}Install SoftWLC (replication)${reset}"
    docker exec -w /ansible -it eltex-ansible ansible-playbook /ansible/softwlc_replication_setup.yml -t "install-softwlc-replication-galera"
    check_ansible_status $? "Install SoftWLC (replication)"
}

# ---------------------------------------------- Ports test --------------------------------------------------

function test_ports(){
    if ! ${need_test_ports}; then
        return
    fi
    echo -e "${green}Test ports${reset}"
    # Запуск плейбука с тегом test-ports
    docker exec -w /ansible -it eltex-ansible ansible-playbook /ansible/softwlc_${current_swlc_installation}_setup.yml -t "test-ports"
    check_ansible_status $? "Test ports"
}

# ------------------------------------------------ Backup ----------------------------------------------------

function create_backup(){
    if ! ${need_backup}; then
        return
    fi
    echo -e "${green}Create backup SoftWLC${reset}"
    # Запуск плейбука с тегом backup
    docker exec -w /ansible -it eltex-ansible ansible-playbook /ansible/softwlc_${current_swlc_installation}_tools.yml -t "backup"
    check_ansible_status $? "Create backup SoftWLC"
}

# ------------------------------------------------ Restore ---------------------------------------------------

function restore_backup(){
    if ! ${need_restore}; then
        return
    fi
    echo -e "${green}Restore backup SoftWLC${reset}"
    # Запуск плейбука с тегом restore
    docker exec -w /ansible -it eltex-ansible ansible-playbook /ansible/softwlc_${current_swlc_installation}_tools.yml -t "restore"
    check_ansible_status $? "Restore backup SoftWLC"
}

# ----------------------------------------- Run SoftWLC containers -------------------------------------------

function run_containers(){
    if ! ${need_run}; then
        return
    fi
    echo -e "${green}Run SoftWLC${reset}"
    # Запуск плейбука с тегом run
    docker exec -w /ansible -it eltex-ansible ansible-playbook /ansible/softwlc_${current_swlc_installation}_tools.yml -t "run"
    check_ansible_status $? "Run SoftWLC"
}

# ---------------------------------------- Stop SoftWLC containers -------------------------------------------

function stop_containers(){
    if ! ${need_stop}; then
        return
    fi
    echo -e "${green}Stop SoftWLC${reset}"
    # Запуск плейбука с тегом stop
    docker exec -w /ansible -it eltex-ansible ansible-playbook /ansible/softwlc_${current_swlc_installation}_tools.yml -t "stop"
    check_ansible_status $? "Stop SoftWLC"
}

# --------------------------------------- Remove SoftWLC containers ------------------------------------------

function delete_containers(){
    if ! ${need_delete}; then
        return
    fi
    echo -e "${green}Delete SoftWLC containers${reset}"
    # Запуск плейбука с тегом delete
    docker exec -w /ansible -it eltex-ansible ansible-playbook /ansible/softwlc_${current_swlc_installation}_tools.yml -t "delete"
    check_ansible_status $? "Delete SoftWLC containers"
}

# -------------------------------------- Recreate SoftWLC containers -----------------------------------------

function recreate_service(){
    local standard=() mariadb=() proxysql=()
    for svc in "$@"; do
        if [[ ("$current_swlc_installation" == "reservation" || "$current_swlc_installation" == "replication") && "$svc" == "eltex-mariadb" ]]; then
            mariadb+=("'$svc'")
        elif [[ "$current_swlc_installation" == "replication" && "$svc" == "eltex-proxysql" ]]; then
            proxysql+=("'$svc'")
        else
            standard+=("'$svc'")
        fi
    done
    if [[ ${#mariadb[@]} -le 0 && ${#proxysql[@]} -le 0 && ${#standard[@]} -le 0 ]]; then
        echo -e "${yellow}Services not specified. Ignoring...\n${reset}"
        exit 1
    fi
    echo -e "${green}Recreate SoftWLC containers${reset}"
    # Запуск плейбука с тегом recreate-service и передача ему переменной с названием сервиса
    if [[ ${#mariadb[@]} -gt 0 ]]; then
        echo -e "${green}Recreate eltex-mariadb${reset}"
        docker exec -w /ansible -it eltex-ansible ansible-playbook /ansible/softwlc_${current_swlc_installation}_tools.yml -t "recreate-database" --extra-vars "containers=['eltex-mariadb']"
    fi
    if [[ ${#proxysql[@]} -gt 0 ]]; then
        echo -e "${green}Recreate eltex-proxysql${reset}"
        docker exec -w /ansible -it eltex-ansible ansible-playbook /ansible/softwlc_replication_tools.yml -t "recreate-service" --extra-vars "containers=['eltex-proxysql'] target_group='galera_claster_replication'"
    fi
    if [[ ${#standard[@]} -gt 0 ]]; then
        list_string=$(IFS=,; echo "${standard[*]}")
        echo -e "${green}Recreate ${list_string}${reset}"
        docker exec -w /ansible -it eltex-ansible ansible-playbook /ansible/softwlc_${current_swlc_installation}_tools.yml -t "recreate-service" --extra-vars "containers=[$list_string]"
    fi
    check_ansible_status $? "Recreate SoftWLC containers"
}

# -------------------------------------------- Key generation ------------------------------------------------

function generate_keys(){
    if ! ${need_generate_keys}; then
        return
    fi
    echo -e "${green}Generate keys${reset}"
    # Запуск плейбука с тегом generate-keys
    docker exec -w /ansible -it eltex-ansible ansible-playbook /ansible/softwlc_${current_swlc_installation}_tools.yml -t "generate-keys"
    check_ansible_status $? "Generate keys"
}

# -------------------------------------------- Password change ------------------------------------------------

function change_password(){
    if ! ${need_change_password}; then
        return
    fi
    echo -e "${green}Change passwords in SoftWLC${reset}"
    # Запуск плейбука с тегом change-password
    docker exec -w /ansible -it eltex-ansible ansible-playbook /ansible/softwlc_${current_swlc_installation}_tools.yml -t "change-password"
    check_ansible_status $? "Change passwords in SoftWLC"
}

# ------------------------------------- Update EMS and Airtune licence ----------------------------------------

function update_ems_airtune_licence(){
    if ! ${need_update_ems_airtune_licence}; then
        return
    fi
    echo -e "${green}Update EMS and Airtune licence${reset}"
    # Запуск плейбука с тегом update-ems-airtune-licence
    docker exec -w /ansible -it eltex-ansible ansible-playbook /ansible/softwlc_${current_swlc_installation}_tools.yml -t "update-ems-airtune-licence"
    check_ansible_status $? "Update EMS and Airtune licence"
}

# ----------------------------------------- Update Wifi-Cab licence --------------------------------------------

function update_wifi_cab_licence(){
    if ! ${need_update_wifi_cab_licence}; then
        return
    fi
    echo -e "${green}Update Wifi-Cab licence${reset}"
    # Запуск плейбука с тегом update-wifi-cab-licence
    docker exec -w /ansible -it eltex-ansible ansible-playbook /ansible/softwlc_${current_swlc_installation}_tools.yml -t "update-wifi-cab-licence"
    check_ansible_status $? "Update Wifi-Cab licence"
}

# ------------------------------------------- Update Radius certs ----------------------------------------------

function update_radius_certs(){
    if ! ${need_update_radius_certs}; then
        return
    fi
    echo -e "${green}Update Radius certs${reset}"
    # Запуск плейбука с тегом update-radius-certs
    docker exec -w /ansible -it eltex-ansible ansible-playbook /ansible/softwlc_${current_swlc_installation}_tools.yml -t "update-radius-certs"
    check_ansible_status $? "Update Radius certs"
}

# ---------------------------------------- Reconfigure SoftWLC services ----------------------------------------

function reconfigure_softwlc(){
    if ! ${need_reconfigure_softwlc}; then
        return
    fi
    echo -e "${green}Reconfiguring SoftWLC addresses${reset}"
    # Запуск плейбука с тегом reconfigure
    docker exec -w /ansible -it eltex-ansible ansible-playbook /ansible/softwlc_${current_swlc_installation}_tools.yml -t "reconfigure"
    check_ansible_status $? "Reconfiguring SoftWLC addresses"
}

# ------------------------------------------- Configure Radius proxy -------------------------------------------

function configure_radius_proxy(){
    if ! ${need_configure_radius_proxy}; then
        return
    fi
    echo -e "${green}Configure radius proxy${reset}"
    # Запуск плейбука с тегом configure-radius-proxy
    docker exec -w /ansible -it eltex-ansible ansible-playbook /ansible/softwlc_${current_swlc_installation}_tools.yml -t "configure-radius-proxy"
    check_ansible_status $? "Configure radius proxy"
}

# ------------------------------------------- Restore Galera cluster -------------------------------------------

function restore_galera_cluster(){
    if ! ${need_restore_galera}; then
        return
    fi
    if [[ ! "$current_swlc_installation" =~ "reservation" && ! "$current_swlc_installation" =~ "replication" ]]; then
        echo -e "${red}Galera cluster recovery is not available for single-host installation${reset}"
        exit 1
    fi
    echo -e "${green}Restore Galera cluster${reset}"
    # Запуск плейбука с тегом restore-galera
    docker exec -w /ansible -it eltex-ansible ansible-playbook /ansible/softwlc_${current_swlc_installation}_tools.yml -t "restore-galera"
    check_ansible_status $? "Restore Galera cluster"
}

# ------------------------------------------- Install Peeper Client -------------------------------------------

function install_peeper_client(){
    if ! ${need_install_peeper_client}; then
        return
    fi
    echo -e "${green}Install Peeper Client${reset}"
    # Запуск плейбука с тегом install-peeper-client
    docker exec -w /ansible -it eltex-ansible ansible-playbook /ansible/softwlc_${current_swlc_installation}_tools.yml -t "install-peeper-client"
    check_ansible_status $? "Install Peeper Client"
}

# -------------------------------------- Show configuration differences ---------------------------------------

function show_configuration_differences(){
    if ! ${need_show_configuration_differences}; then
        return
    fi
    echo -e "${green}Show configuration differences${reset}"
    # Запуск плейбука с тегом show-configuration-differences и флагами --check --diff
    docker exec -w /ansible -it eltex-ansible ansible-playbook /ansible/softwlc_${current_swlc_installation}_tools.yml -t "show-configuration-differences" --check --diff
    check_ansible_status $? "Show configuration differences"
}

# ---------------------------------- Setup update params in Ansible all.yml -----------------------------------

function set_update_flags(){
    if ! ${need_update}; then
        return
    fi
    if [ ! -f "${INVENTORY_PATH}/my_softwlc/group_vars/all.yml" ]; then
        echo -e "${red}Ansible playbook for SoftWLC are not installed on the host. Run script with -d option (The all.yml file is missing)${reset}"
        exit 1
    fi
    sed -i "s|is_need_update_system: .*|is_need_update_system: true|" $INVENTORY_PATH/my_softwlc/group_vars/all.yml
}

function recover_update_flags(){
    if ! ${need_update}; then
        return
    fi
    if [ ! -f "${INVENTORY_PATH}/my_softwlc/group_vars/all.yml" ]; then
        echo -e "${red}Ansible playbook for SoftWLC are not installed on the host. Run script with -d option (The all.yml file is missing)${reset}"
        return
    fi
    sed -i "s|is_need_update_system: .*|is_need_update_system: false|" $INVENTORY_PATH/my_softwlc/group_vars/all.yml
}

# -------------------------------- Setup close-loop params in Ansible all.yml ---------------------------------

function set_close_loop_flags(){
    if ! ${need_close_loop_setup}; then
        return
    fi
    if [ ! -f "${INVENTORY_PATH}/my_softwlc/group_vars/all.yml" ]; then
        echo -e "${red}Ansible playbook for SoftWLC are not installed on the host. Run script with -d option (The all.yml file is missing)${reset}"
        exit 1
    fi
    sed -i "s|is_close_loop_setup: .*|is_close_loop_setup: true|" $INVENTORY_PATH/my_softwlc/group_vars/all.yml
}

function recover_close_loop_flags(){
    if ! ${need_close_loop_setup}; then
        return
    fi
    if [ ! -f "${INVENTORY_PATH}/my_softwlc/group_vars/all.yml" ]; then
        echo -e "${red}Ansible playbook for SoftWLC are not installed on the host. Run script with -d option (The all.yml file is missing)${reset}"
        return
    fi
    sed -i "s|is_close_loop_setup: .*|is_close_loop_setup: false|" $INVENTORY_PATH/my_softwlc/group_vars/all.yml
}

# --------------------------- Configure SoftWLC version and repo (For QA test only) ---------------------------

function configure_versions(){
    if ! ${need_configure_versions}; then
        return
    fi
    echo -e "${green}Set up SoftWLC versions${reset}"
    
    echo -e "SoftWLC version:
        \r1. latest (equal ${SOFTWLC_VERSION})
        \r2. other \n"
    echo -n ": "
    
    read ans
    echo ""
    case ${ans} in
    1)
        echo -e "${green}Set to latest ${SOFTWLC_VERSION}${reset}"
        ;;
    2)
        echo -n "Input SoftWLC version (blank for ${SOFTWLC_VERSION}): "
        read ver
        if [ -n "$ver" ]; then
            SOFTWLC_VERSION=${ver}
        fi
        echo -n "Input EMS version (blank for ${EMS_VERSION}): "
        read ver
        if [ -n "$ver" ]; then
            EMS_VERSION=${ver}
        fi
        echo -n "Input Airtune version (blank for ${AIRTUNE_VERSION}): "
        read ver
        echo ""
        if [ -n "$ver" ]; then
            AIRTUNE_VERSION=${ver}
        fi
        echo -e "${green}SoftWLC version: ${SOFTWLC_VERSION}
            \rEMS version: ${EMS_VERSION}
            \rAirtune version: ${AIRTUNE_VERSION}${reset}"
        ;;
    *)
        echo -e "${green}Set to latest ${SOFTWLC_VERSION}${reset}"
        ;;
    esac
    
    echo -e "Docker registry:
        \r1. ${ELTEX_PUBLIC_REGISTRY}
        \r2. ${ELTEX_PRIVATE_REGISTRY_RELEASE} \n"
    echo -n ": "
    
    read ans
    echo ""
    case ${ans} in
    1)
        echo -e "${green}Set to default ${ELTEX_PUBLIC_REGISTRY}${reset}"
        ;;
    2)
        echo -e "${green}Set to release ${ELTEX_PRIVATE_REGISTRY_RELEASE}${reset}"
        ELTEX_DOCKER_REGISTRY=${ELTEX_PRIVATE_REGISTRY_RELEASE}
        ;;
    *)
        echo -e "${green}Set to default ${ELTEX_PUBLIC_REGISTRY}${reset}"
        ;;
    esac
    
    
    echo -e "Repository:
        \r1. ${ELTEX_PUBLIC_REPO}
        \r2. ${ELTEX_PRIVATE_REPO} \n"
    echo -n ": "
    
    read ans
    echo ""
    case ${ans} in
    1)
        echo -e "${green}Set to default ${ELTEX_PUBLIC_REPO}${reset}"
        ;;
    2)
        echo -e "${green}Set to private ${ELTEX_PRIVATE_REPO}${reset}"
        ELTEX_REPO=${ELTEX_PRIVATE_REPO}
        ;;
    *)
        echo -e "${green}Set to default ${ELTEX_PUBLIC_REPO}${reset}"
        ;;
    esac
    echo ""
}

# ---------------------------------------------- Show HELP ---------------------------------------------------

function print_help() {
    echo "${help}"
}

function print_version() {
    echo ""
    echo "SoftWLC version: $SOFTWLC_VERSION"
    echo "EMS version: $EMS_VERSION"
    echo "Airtune version: $AIRTUNE_VERSION"
    echo "${ansible_script_version}"
    if [ ! -f "${current_dir}/${CURR_FILE_VERSION_NAME}/version" ]; then
        echo -e "${red}Ansible playbook for SoftWLC are not installed on the host. Run script with -d option (The version file is missing)${reset}"
    else
        cat "${current_dir}/${CURR_FILE_VERSION_NAME}/version" && echo -e ""
    fi
}

# ------------------------------- Check current installation (TOOLS FUNCTIONS) -------------------------------

# Функция чтения параметров is_default_install/is_reservation_install/is_replication_galera_install из файла all.yml для последующей обработки
# Private функция запускающаяся ТОЛЬКО из 3 функций ниже
function read_softwlc_installation() {
    if [ ! -f "${INVENTORY_PATH}/my_softwlc/group_vars/all.yml" ]; then
        echo -e "${red}Ansible playbook for SoftWLC are not installed on the host. Run script with -d option (The all.yml file is missing)${reset}"
        exit 1
    fi
    need_default_install=$(grep 'is_default_install:' $INVENTORY_PATH/my_softwlc/group_vars/all.yml | awk '{print $2}')
    need_reservation_install=$(grep 'is_reservation_install:' $INVENTORY_PATH/my_softwlc/group_vars/all.yml | awk '{print $2}')
    need_replication_galera_install=$(grep 'is_replication_galera_install:' $INVENTORY_PATH/my_softwlc/group_vars/all.yml | awk '{print $2}')
    true_count=$(
        printf "%s\n" "$need_default_install" \
                "$need_reservation_install" \
                "$need_replication_galera_install" |
        grep -c '^true$'
    )
    if (( true_count != 1 )); then
        echo -e "${red}Warning! More than one parameter is set to 'true' in the selected setup options. Default installation selected (softwlc_default_* playbooks)!${reset}"
        need_replication_galera_install=false
        need_reservation_install=false
        need_default_install=true
    fi
}

# Функция СОХРАНЕНИЯ параметров ТЕКУЩЕЙ установки
# Необходима ТОЛЬКО в случае принудительного переключения типов установки в некоторых флагах
# Используется СОВМЕСТНО с функцией recover_softwlc_installation для восстановления параметра в all.yml
# Запускается при выбранном флаге need_save_state=true
function save_softwlc_installation() {
    if ! ${need_save_state}; then
        return
    fi
    read_softwlc_installation
    if ${need_default_install}; then
        saved_installation_state='default'
    elif ${need_reservation_install}; then
        saved_installation_state='reservation'
    elif ${need_replication_galera_install}; then
        saved_installation_state='replication'
    fi
}

# Функция ВОССТАНОВЛЕНИЯ параметров ТЕКУЩЕЙ установки
# Необходима ТОЛЬКО в случае принудительного переключения типов установки в некоторых флагах и необходимости ПОСЛЕ отработки данного флага восстановить параметры в all.yml
# Используется СОВМЕСТНО с функцией save_softwlc_installation для первоначального сохранения параметра
# Запускается при выбранном флаге need_save_state=true
function recover_softwlc_installation() {
    if ! ${need_save_state}; then
        return
    fi
    case ${saved_installation_state} in
    default)
        # Включение параметра is_default_install глобально (отключение других GLOBAL переменных)
        sed -i "s|is_default_install: .*|is_default_install: true|" $INVENTORY_PATH/my_softwlc/group_vars/all.yml
        sed -i "s|is_reservation_install: .*|is_reservation_install: false|" $INVENTORY_PATH/my_softwlc/group_vars/all.yml
        sed -i "s|is_replication_galera_install: .*|is_replication_galera_install: false|" $INVENTORY_PATH/my_softwlc/group_vars/all.yml
        ;;
    reservation)
        # Включение параметра is_reservation_install глобально (отключение других GLOBAL переменных)
        sed -i "s|is_default_install: .*|is_default_install: false|" $INVENTORY_PATH/my_softwlc/group_vars/all.yml
        sed -i "s|is_reservation_install: .*|is_reservation_install: true|" $INVENTORY_PATH/my_softwlc/group_vars/all.yml
        sed -i "s|is_replication_galera_install: .*|is_replication_galera_install: false|" $INVENTORY_PATH/my_softwlc/group_vars/all.yml
        ;;
    replication)
        # Включение параметра is_replication_galera_install глобально (отключение других GLOBAL переменных)
        sed -i "s|is_default_install: .*|is_default_install: false|" $INVENTORY_PATH/my_softwlc/group_vars/all.yml
        sed -i "s|is_reservation_install: .*|is_reservation_install: false|" $INVENTORY_PATH/my_softwlc/group_vars/all.yml
        sed -i "s|is_replication_galera_install: .*|is_replication_galera_install: true|" $INVENTORY_PATH/my_softwlc/group_vars/all.yml
        ;;
    *)
        echo -e "${red}Unknown version${reset}"
        exit 1
        ;;
    esac
}

# Функция УСТАНОВКИ параметров ТЕКУЩЕЙ установки
# Запускается ТОЛЬКО из функции prepare_inventory_file при следующем условии:
# ${need_prepare_inventory_file} && ! ${need_default_install} && ! ${need_reservation_install} && ! ${need_replication_galera_install}
# Необходима ТОЛЬКО в случае отсутствия флагов на установку (т.е. используется только в tools плейбуках)
# для установки соответствующих параметров и выбора хостов в некоторых флагах скрипта (tools), а также ДЛЯ ВЫБОРА СООТВЕТСТВУЮЩЕГО ФАЙЛА TOOLS:
# current_swlc_installation='reservation' => softwlc_reservation_tools.yml
# Используется ТОЛЬКО при отсутствии ручного включения соответствующего параметра во флагах
function set_softwlc_installation() {
    read_softwlc_installation
    if ${need_default_install}; then
        current_swlc_installation='default'
    elif ${need_reservation_install}; then
        current_swlc_installation='reservation'
    elif ${need_replication_galera_install}; then
        current_swlc_installation='replication'
    fi
}

# -------------------------------------------- SCRIPT START POINT --------------------------------------------

# Флаги для запуска функций
# Параметры, необходимые для проверки/сохранения/восстановления текущего состояния установки
current_swlc_installation="none"
saved_installation_state="none"
need_save_state=false

# Параметры, запускаемые по флагам. При указании флага после скрипта соответствующее значение переводится в true
need_install_docker_host=false
need_start_ansible_container=false
need_prepare_inventory_file=false
need_migrate_from_debs=false
need_migrate_backup_db=false
need_migrate_restore_db=false
need_test_ports=false
need_backup=false
need_restore=false
need_run=false
need_stop=false
need_delete=false
need_generate_keys=false
need_change_password=false
need_restore_galera=false
need_update_ems_airtune_licence=false
need_update_wifi_cab_licence=false
need_reconfigure_softwlc=false
need_configure_radius_proxy=false
need_download_ansible=false
need_update_radius_certs=false
need_show_configuration_differences=false

# Глобальные параметры, отвечающие за установку в закрытом контуре
need_close_loop_setup=false

# Глобальные параметры, отвечающие за обновление конфигураций, версий, патчей SoftWLC и Ansible
need_update=false

# Глобальный параметр установки и настройки Peeper Client
need_install_peeper_client=false

# QA Param
need_configure_versions=false

# Параметры установки. ГЛОБАЛЬНЫЕ И ВАЖНЫЕ ПАРАМЕТРЫ. От них зависит настройка плейбука (all.yml) и адресов (inventory.yml)
need_replication_galera_install=false
need_reservation_install=false
need_default_install=false

# Главный цикл проверки флагов скрипта
if [[ -n "$1" ]]; then
    while [[ $# -gt 0 ]]; do
        key=$1
        case ${key} in
        --install-docker)
            need_install_docker_host=true
            ;;
        -d | --download)
            need_download_ansible=true
            ;;
        --start-ansible)
            need_start_ansible_container=true
            ;;
        --migrate-from-debs)
            need_download_ansible=true

            need_save_state=true
            save_softwlc_installation

            current_swlc_installation="default" # Мигрируем параметры из Deb установки => нужен адрес одного хоста - хоста с дебками
            need_default_install=true
            need_replication_galera_install=false
            need_reservation_install=false

            need_prepare_inventory_file=true

            need_start_ansible_container=true
            need_migrate_from_debs=true
            ;;
        --reservation-install)		# Запускаемые функции: check_docker, install_docker, download_softwlc_ansible, prepare_inventory_file, start_ansible_container, ansible_reservation_install
            need_default_install=false
            need_reservation_install=true
            need_replication_galera_install=false
            ;;
        --replication-galera-install)	# Запускаемые функции: check_docker, install_docker, download_softwlc_ansible, prepare_inventory_file, start_ansible_container, ansible_replication_galera_install
            need_default_install=false
            need_reservation_install=false
            need_replication_galera_install=true
            ;;
        --default-install) 		# Запускаемые функции: check_docker, install_docker, download_softwlc_ansible, prepare_inventory_file, start_ansible_container, ansible_default_install
            need_default_install=true
            need_reservation_install=false
            need_replication_galera_install=false
            ;;
        --migrate-db-backup)
            need_save_state=true
            save_softwlc_installation

            current_swlc_installation="default" # Делаем бэкап базы из Deb установки => нужен адрес одного хоста - хоста с дебками, откуда тянем БД
            need_default_install=true
            need_replication_galera_install=false
            need_reservation_install=false

            need_prepare_inventory_file=true

            need_start_ansible_container=true
            need_migrate_backup_db=true
            ;;
        --migrate-db-restore)
            need_prepare_inventory_file=true
            need_start_ansible_container=true
            need_migrate_restore_db=true
            ;;
        --test-ports)
            need_prepare_inventory_file=true
            need_test_ports=true
            ;;
        --backup)
            need_prepare_inventory_file=true
            need_backup=true
            ;;
        --restore)
            need_prepare_inventory_file=true
            need_restore=true
            ;;
        --run)
            need_prepare_inventory_file=true
            need_run=true
            ;;
        --stop)
            need_prepare_inventory_file=true
            need_stop=true
            ;;
        --delete)
            need_prepare_inventory_file=true
            need_delete=true
            ;;
        --generate-keys)
            need_prepare_inventory_file=true
            need_generate_keys=true
            ;;
        --update-ems-licence)
            need_prepare_inventory_file=true
            need_update_ems_airtune_licence=true
            ;;
        --update-wifi-cab-licence)
            need_prepare_inventory_file=true
            need_update_wifi_cab_licence=true
            ;;
        --update-radius-certs)
            need_prepare_inventory_file=true
            need_update_radius_certs=true
            ;;
        --recreate-service)
            need_prepare_inventory_file=true
            prepare_inventory_file
            shift
            recreate_service "$@"
            exit 0
            ;;
        --restore-galera)
            need_prepare_inventory_file=true
            need_restore_galera=true
            ;;
        --change-password)
            need_prepare_inventory_file=true
            need_change_password=true
            ;;
        --reconfigure)
            need_prepare_inventory_file=true
            need_reconfigure_softwlc=true
            ;;
        --configure-radius-proxy)
            need_prepare_inventory_file=true
            need_configure_radius_proxy=true
            ;;
        --install-peeper-client)
            need_prepare_inventory_file=true
            need_install_peeper_client=true
            ;;
        --diff)
            need_prepare_inventory_file=true
            need_show_configuration_differences=true
            ;;
        -u | --update)
            need_update=true
            ;;
        -cl | --close-loop)
            need_close_loop_setup=true
            echo -e "${yellow}Using a closed loop does not automatically download packages.
            \rPlease ensure you have all required packages listed in the closed loop user manual before installation.${reset}"
            ;;
        -cv | --configure-versions)
            need_configure_versions=true
            ;;
        -h | --help)
            print_help
            exit 0
            ;;
        -v | --version)
            print_version
            exit 0
            ;;
        *)
            print_help
            exit 0
            ;;
        esac
        shift
    done
else
    print_help
    exit 0
fi

# ===============================================================================================================
# =========================================== Запуск функций скрипта ============================================
# ===============================================================================================================
# название_функции                  # ОПИСАНИЕ ФУНКЦИИ                                                      (используемый_флаг) (В рамках чего данная функция работает/какой файл ansible вызывает)


check_docker                        # Функция проверки наличия и версии докера на хосте со скриптом         (true) (script)

install_docker                      # Функция установки Docker на хост со скриптом                          (need_install_docker_host) (script)
download_softwlc_ansible            # Функция скачивания и распаковки плейбуков Ansible                     (need_download_ansible) (script)
set_close_loop_flags                # Функция установки флагов закрытого контура                            (need_close_loop_setup) (script)
set_update_flags                    # Функция установки флагов обновления конфигураций                      (need_update) (script)

prepare_inventory_file              # Функция подготовки all.yml и inventory.yml (адреса параметры)         (need_prepare_inventory_file) (script)
start_ansible_container             # Функция запуска контейнера Ansible (включая его .env параметры)       (need_start_ansible_container) (script)

show_configuration_differences      # Функция запуска проверки файлов конфигурации (Ansible --check --diff) (need_show_configuration_differences) (softwlc_*_tools.yml[default|reservation|replication])

ansible_migrate_from_debs           # Функция миграции параметров конфигурации из Deb в параметры Ansible   (need_migrate_from_debs) (softwlc_default_tools.yml)

ansible_replication_galera_install  # Функция установки. Вариант - replication 1+1+3 Galera Cluster nodes   (need_replication_galera_install) (softwlc_replication_setup.yml)
ansible_reservation_install         # Функция установки. Вариант - reservation 1+1                          (need_reservation_install) (softwlc_reservation_setup.yml)
ansible_default_install             # Функция установки. Вариант - standalone                               (need_default_install) (softwlc_default_setup.yml)

stop_containers                     # Функция остановки сервисов SoftWLC (docker stop)                      (need_stop) (softwlc_*_tools.yml[default|reservation|replication])

create_backup                       # Функция создания бэкапа сервисов SoftWLC                              (need_backup) (softwlc_*_tools.yml[default|reservation|replication])
restore_backup                      # Функция восстановления бэкапа сервисов SoftWLC                        (need_restore) (softwlc_*_tools.yml[default|reservation|replication])

delete_containers                   # Функция удаления сервисов SoftWLC (docker down)                       (need_delete) (softwlc_*_tools.yml[default|reservation|replication])
run_containers                      # Функция запуска сервисов SoftWLC (docker up)                          (need_run) (softwlc_*_tools.yml[default|reservation|replication])

ansible_migrate_db_backup           # Функция создания бэкапа базы данных Deb версии                        (need_migrate_backup_db) (softwlc_default_tools.yml)
ansible_migrate_db_restore          # Функция восстановления бэкапа базы данных в Docker версию             (need_migrate_restore_db) (softwlc_*_tools.yml[default|reservation|replication])

reconfigure_softwlc                 # Функция смены конфигурации IP адресов SoftWLC (включая VIP)           (need_reconfigure) (softwlc_*_tools.yml[default|reservation|replication])

generate_keys                       # Функция генерации и установки новых ключей                            (need_generate_keys) (softwlc_*_tools.yml[default|reservation|replication])
test_ports                          # Функция тестирования портов                                           (need_test_ports) (softwlc_*_tools.yml[default|reservation|replication])

configure_radius_proxy              # Функция настройки проксирования radius                                (need_test_ports) (softwlc_*_tools.yml[default|reservation|replication])
change_password                     # Функция смены паролей сервисных+root пользователей в БД               (need_change_password) (softwlc_*_tools.yml[default|reservation|replication])

restore_galera_cluster              # Функция восстановления кластера галеры после смерти всех нод          (need_restore_galera) (softwlc_*_tools.yml[reservation|replication])

update_ems_airtune_licence          # Функция обновления лицензии ems airtune                               (need_update_ems_airtune_licence) (softwlc_*_tools.yml[default|reservation|replication])
update_wifi_cab_licence             # Функция обновления лицензии wifi-cab                                  (need_update_wifi_cab_licence) (softwlc_*_tools.yml[default|reservation|replication])
update_radius_certs                 # Функция обновления сертификатов Radius                                (need_update_radius_certs) (softwlc_*_tools.yml[default|reservation|replication])

install_peeper_client               # Функция настройки, установки и запуска Peeper Client                  (need_install_peeper_client) (softwlc_*_tools.yml[default|reservation|replication])

# Функции ниже запускаются при ЛЮБОМ завершении скрипта через cleanup функцию из trap EXIT
# recover_update_flags              # Функция восстановления флагов обновления конфигураций                 (need_update) (script)
# recover_softwlc_installation      # Функция восстановления флага is_* текущей установки (for tools only)  (need_save_state) (script)
# recover_close_loop_flags          # Функция восстановления флагов close-loop                              (need_close_loop_setup) (script)
