#!/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
# --help, -h                      help
#
# Запросы при установке укажите согласно требованиям документации, либо по необходимости


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

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

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

ansible_script_version="
SoftWLC Ansible Script
От 13 февраля 2026 года
"

# Максимальное кол-во коннектов к базе данных при выборе многохостовой установки (reservation/replication)
DB_MAX_CONNECTIONS=3000

# Директория текущей версии 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

# ================================== Логирование скрипта ===============================================
# Включение дебаг логов скрипта (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

--help, -h                      help
--version, -v                   version

"

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

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

    REQUIRED_COMPOSE_VERSION="2.23.3" # last for redos - 2.23.3, ubuntu - 2.33.1, astra - 2.23.3
    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 "${red}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 "Remove unofficial docker versions..."
    # Нужно просто проверить, что их нет и удалить, необходимы поставит официальный скрипт докера
    for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do sudo apt-get remove -y $pkg; done

    echo -e "\nInstallation requires packages"
    require_package curl

    DOCKER_CONFIG=${DOCKER_CONFIG:-$HOME/.docker}
    rm -f $DOCKER_CONFIG/cli-plugins/docker-compose
    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}\nInstall is complete${reset}"
}

function install_docker_redos() {
    echo -e "${green}Install docker for REDOS${reset}"
    echo "Remove unofficial docker-compose versions..."
    dnf remove docker-compose-* -y

    echo -e "\nInstallation requires packages"
    require_package docker-ce curl

    echo -e "\nInstall official docker and docker compose..."
    export DOCKER_CONFIG=${DOCKER_CONFIG:-/usr/local/lib/docker}
    mkdir -p "${DOCKER_CONFIG}/cli-plugins"
    curl -SL https://github.com/docker/compose/releases/download/v2.23.3/docker-compose-linux-x86_64 -o $DOCKER_CONFIG/cli-plugins/docker-compose
    chmod +x $DOCKER_CONFIG/cli-plugins/docker-compose
    systemctl enable docker
    systemctl restart docker
    echo -e "${green}\nInstall is complete${reset}"
}

function install_docker_astralinux() {
    echo -e "${green}Install docker for Astra Linux${reset}"
    echo "Remove unofficial docker-compose versions..."
    apt remove docker-compose-* -y

    if [[ ${DISTR_ID} == "AstraLinux" ]]; 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
    fi

    echo -e "\nInstallation requires packages"
    require_package docker.io curl

    echo -e "\nInstall official docker compose..."
    export DOCKER_CONFIG=${DOCKER_CONFIG:-/usr/local/lib/docker}
    mkdir -p "${DOCKER_CONFIG}/cli-plugins"
    curl -SL https://github.com/docker/compose/releases/download/v2.23.3/docker-compose-linux-x86_64 -o $DOCKER_CONFIG/cli-plugins/docker-compose
    chmod +x $DOCKER_CONFIG/cli-plugins/docker-compose
    chmod +x /usr/local/lib/docker/cli-plugins/docker-compose
    systemctl enable docker
    systemctl restart docker
    echo -e "${green}\nInstall is complete${reset}"
}

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

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

    case ${DISTR_ID} in
    "RED SOFT") install_docker_redos ;;
    "AstraLinux") install_docker_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
    	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
        backup_softwlc_ansible_files
        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
            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/*" \
                -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 replace this configuration with default parameters from inventory/sample ? [y/N] (default=\"N\"): "
        read ans
        if [[ "$ans" =~ ^(y|Y|д|Д)$ ]]; then
            mv $INVENTORY_PATH/my_softwlc/group_vars/all.yml $INVENTORY_PATH/my_softwlc/group_vars/all.yml.backup
            cp -rfp $INVENTORY_PATH/sample/. $INVENTORY_PATH/my_softwlc
        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}"
}

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

function backup_softwlc_ansible_files() {
    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/*
    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; 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 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
        
        # Если не настроенно использование приватных ключей - запрашиваем данные для входа для хоста ${HOST}
        if ! grep "private_key_file" $current_dir/$CURR_FILE_VERSION_NAME/ansible.cfg; then
            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

            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
        # Установка коннектов к БД
        sed -i "s|mariadb_max_connections: .*|mariadb_max_connections: ${DB_MAX_CONNECTIONS}|" $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
        # Установка коннектов к БД
        sed -i "s|mariadb_max_connections: .*|mariadb_max_connections: ${DB_MAX_CONNECTIONS}|" $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
    docker compose up -d --remove-orphans --force-recreate
}

# ----------------------------------- 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 service=${1:?service name is missing}
    echo -e "${green}Recreate ${service} container${reset}"
    # Запуск плейбука с тегом recreate-service и передача ему переменной с названием сервиса
    if [[ ("$current_swlc_installation" == "reservation" || "$current_swlc_installation" == "replication") && "$service" == "eltex-mariadb" ]]; then
        docker exec -w /ansible -it eltex-ansible ansible-playbook /ansible/softwlc_${current_swlc_installation}_tools.yml -t "recreate-database" --extra-vars "containers=['eltex-mariadb']"
    elif [[ "$current_swlc_installation" == "replication" && "$service" == "eltex-proxysql" ]]; then
        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'"
    else
        docker exec -w /ansible -it eltex-ansible ansible-playbook /ansible/softwlc_${current_swlc_installation}_tools.yml -t "recreate-service" --extra-vars "containers=['${service}']"
    fi
    check_ansible_status $? "Recreate ${service} container"
}

# -------------------------------------------- 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"
}

# --------------------------------- 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:
1. latest (equal ${SOFTWLC_VERSION})
2. 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}
EMS version: ${EMS_VERSION}
Airtune version: ${AIRTUNE_VERSION}${reset}"
        ;;
    *)
        echo -e "${green}Set to latest ${SOFTWLC_VERSION}${reset}"
        ;;
    esac
    
    echo -e "
Docker registry:
1. ${ELTEX_PUBLIC_REGISTRY}
2. ${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:
1. ${ELTEX_PUBLIC_REPO}
2. ${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_install_docker_ansible=false
need_prepare_inventory_file=false
need_migrate_from_debs=false
need_migrate_backup_db=false
need_migrate_restore_db=false
need_install_softwlc_ansible=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_configure_versions=false
need_update_radius_certs=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 $1
            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
            ;;
        --configure-versions | -cv)
            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)
prepare_inventory_file              # Функция подготовки all.yml и inventory.yml (адреса параметры)         (need_prepare_inventory_file) (script)
start_ansible_container             # Функция запуска контейнера Ansible (включая его .env параметры)       (need_start_ansible_container) (script)

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])

recover_softwlc_installation        # Функция восстановления флага is_* текущей установки (for tools only)  (need_save_state) (script)
