#!/bin/bash
# This script will assist with configuring ProxySQL
# (currently only Percona XtraDB cluster in combination with ProxySQL is supported)
# Version 2.0
###############################################################################################

# This program is copyright 2016-2022 Percona LLC and/or its affiliates.
#
# THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
# WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free Software
# Foundation, version 2 or later
#
# You should have received a copy of the GNU General Public License version 2
# along with this program; if not, write to the Free Software Foundation, Inc.,
# 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA.

#-------------------------------------------------------------------------------
#
# Step 1 : Bash internal configuration
#

set -o nounset    # no undefined variables
set -o pipefail   # internal pipe failures cause an exit

# Include the common functions
. $(dirname ${BASH_SOURCE[0]})/proxysql-common
. $(dirname ${BASH_SOURCE[0]})/proxysql-admin-common


#-------------------------------------------------------------------------------
#
# Step 2 : Global variables
#

#
# Script parameters/constants
#

# default timeout is 10 seconds
declare  -i TIMEOUT=10

#
# Global variables used by the script
#
declare    CONFIG_FILE=""
declare    CONFIG_PATH
declare    CONFIG_FILENAME

#
# Location of executables
#
declare    MY_PRINT_DEFAULTS=""
declare    SCHEDULER_BINARY=""


# Variables declared in proxysql-common
# ----------------------------------------
# readonly    PROXYSQL_ADMIN_VERSION="2.0.12"
# readonly    REQUIRED_OPENSSL_VERSION="1.1.1"
# readonly    PROXYSQL_ADMIN_OPENSSL_NAME="proxysql-admin-openssl"
# declare  -i DEBUG=0
# declare     PROXYSQL_USERNAME=""
# declare     PROXYSQL_PASSWORD=""
# declare     PROXYSQL_PORT=""
# declare     PROXYSQL_HOSTNAME=""
# declare     CLUSTER_USERNAME=""
# declare     CLUSTER_PASSWORD=""
# declare     CLUSTER_HOSTNAME=""
# declare     CLUSTER_PORT=""
# declare     CLUSTER_APP_USERNAME=""
# declare     CLUSTER_APP_PASSWORD=""
# declare     MONITOR_USERNAME=""
# declare     MONITOR_PASSWORD=""

# The SAFE_XXX versions are the version that have
# been escaped.  The single quotes will have been
# SQL-escaped, which means ' -> ''
# So these are the versions that should be used in any SQL query.
declare    SAFE_MONITOR_USERNAME=""
declare    SAFE_MONITOR_PASSWORD=""

declare    LOCK_FILE_PATH=""
declare    WRITE_NODE=""
declare    UPDATE_READ_WEIGHT=""
declare    UPDATE_WRITE_WEIGHT=""

declare    MODE="singlewrite"
declare -i NODE_CHECK_INTERVAL=-1

declare -i WRITER_HOSTGROUP_ID=-1
declare -i WRITER_HOSTGROUP_COMMAND_LINE=0
declare -i READER_HOSTGROUP_ID=-1
declare -i BACKUP_WRITER_HOSTGROUP_ID=-1
declare -i OFFLINE_HOSTGROUP_ID=-1

declare -i WRITER_CONFIG_HOSTGROUP_ID=-1
declare -i READER_CONFIG_HOSTGROUP_ID=-1
declare -i WRITER_MAINT_HOSTGROUP_ID=-1
declare -i READER_MAINT_HOSTGROUP_ID=-1

declare -i AUTO_ASSIGN_WEIGHTS=0
declare -i QUICK_DEMO=0
declare -i ENABLE=0
declare -i DISABLE=0
declare -i ADDUSER=0
declare -i SYNCUSERS=0
declare -i SYNCMULTICLUSTERUSERS=0
declare -i ADD_QUERY_RULE=0
declare -i UPDATE_CLUSTER=0
declare -i UPDATE_MYSQL_VERSION=0
declare -i CHECK_IF_ENABLED=0
declare -i FORCE=0
declare -i REPORT_STATUS=0

# This can be specifed by the "--server=IP:PORT" option for
# a single address.  This is used by --syncusers to sync a
# single node (that does not need to be part of a cluster)
declare    SINGLE_SERVER=""

# For the scheduler-admin, we do not allow prompts, so just
# use the existing monitor password
declare -i USE_EXISTING_MONITOR_PASSWORD=1
declare -i WITH_CLUSTER_APP_USER=1

declare -i DISABLE_UPDATES=0

# If we have to disable updates, store the original values
# so that we can restore them back to these values.
# These variables have the value of 'true' | 'false'
declare    ADMIN_CHECKSUM_MYSQL_QUERY_RULES_ORIGINAL_VALUE=""
declare    ADMIN_CHECKSUM_MYSQL_SERVERS_ORIGINAL_VALUE=""
declare    ADMIN_CHECKSUM_MYSQL_USERS_ORIGINAL_VALUE=""
declare    ADMIN_CLUSTER_USERNAME_ORIGINAL_VALUE=""
declare    ADMIN_CLUSTER_CHECK_INTERVAL_ORIGINAL_VALUE=""

declare    MYSQL_CLIENT_VERSION=""

# SSL options
declare -i USE_SSL_OPTION=0
declare    SSL_CERTIFICATE_PATH=""

#
# Default value for max_connections in mysql_servers
#
declare    MAX_CONNECTIONS="1000"

declare -i WRITERS_ARE_READERS_OPTION=-1

declare -i REMOVE_ALL_SERVERS=0

#
# Set these values if a parameter is needed by the operation
# (For example, --disable only needs --writer-hg)
#
declare   NEEDS_WRITER_HOSTGROUP=0
declare   NEEDS_READER_HOSTGROUP=0
declare   NEEDS_BACKUP_WRITER_HOSTGROUP=0
declare   NEEDS_OFFLINE_HOSTGROUP=0

#
# If set to 1, then calls to MySQL/ProxySQL via the MySQL client
# will have credentials passed via stdin rather than using bash
# process substitution.
#
declare   USE_STDIN_FOR_CREDENTIALS=0

#-------------------------------------------------------------------------------
#
# Step 3 : Helper functions
#


#
# Dispay script usage details
#
function usage() {
  local path=$0
  cat << EOF
Usage: ${path##*/} [ options ]


You must include at least one option. Independent options do not require another
option to run successfully. Dependent options require another option to run
successfully. If you run a dependent option without the required option you see
an error message and the option does not run.


These options can be run without another option:

  --adduser                          Adds the Percona XtraDB Cluster application user to the ProxySQL database
  --config-file=<config-file>        Read login credentials from a configuration file
                                     (command line options override any configuration file values)
  --debug                            Enables additional debug logging.
  --disable, -d                      Removes any Percona XtraDB Cluster configurations from ProxySQL
  --disable-updates                  Disable admin updates for ProxySQL cluster for the
                                     current operation. The default is to not change the
                                     admin variable settings.  If this option is specifed,
                                     these options will be set to false.
                                     (default: updates are not disabled)
  --enable, -e                       Auto-configure Percona XtraDB Cluster nodes into ProxySQL
  --help                             Displays this help text.
  --is-enabled                       Checks if the current configuration is enabled in ProxySQL.
  --status                           Returns a status report on the current configuration.
  --trace                            Enables shell-level tracing for this shell script
  --update-cluster                   Updates the cluster membership, adds new cluster nodes
                                     to the configuration.

  --update-mysql-version             Updates the mysql-server_version variable in ProxySQL with the version
                                     from a node in the cluster.
  --use-stdin-for-credentials        If set, then the MySQL client uses stdin to send credentials
                                     to the client (instead of process substition).
                                     (default: process subsitution is used)
  --version, -v                      Prints the version info

The following options require another option or a specific mode. Running these
options by themselves or with an incorrect option causes an error.

  --add-query-rule                   Creates query rules for synced mysql user. This is applicable only
                                     for singlewrite mode and works only with '--syncusers'
                                     and '--sync-multi-cluster-users' options.
  --auto-assign-weights              When used with '--update-cluster', this option will auto assign
                                     the weights if in 'singlewrite' mode.
  --force                            Skips existing configuration checks in mysql_servers,
                                     mysql_users and mysql_galera_hostgroups tables. This option will
                                     work with '--enable' and '--update-cluster'.
                                     This will also cause certain checks to issue warnings instead
                                     of an error.
  --remove-all-servers               When used with '--update-cluster', this will remove all
                                     servers belonging to the current cluster before
                                     updating the list.
  --server=<IPADDRESS>:<PORT>        This option can be used with --syncusers or
                                     --sync-multi-cluster-users to sync a single non-cluster server
                                     node.
  --syncusers                        Sync user accounts currently configured in MySQL to ProxySQL
                                     May be used with '--enable'.  '--server' may be used with this
                                     to specify a single server to sync.
                                     NOTE: This option deletes the ProxySQL users not present in MySQL.

  --sync-multi-cluster-users         Sync user accounts currently configured in MySQL to ProxySQL
                                     May be used with '--enable'.  '--server' may be used with this
                                     to specify a single server to sync.
                                     NOTE: This option works in the same way as --syncusers but does not
                                     delete ProxySQL users not present in MySQL. It's indicated to be
                                     used when syncing proxysql instances that manage multiple clusters.

  --update-read-weight=<IP:PORT,WT>  When used with '--update-cluster', this option will assign the
                                     specified read weight to the given node.
  --update-write-weight=<IP:PORT,WT> When used with '--update-cluster', this option will assign the
                                     specified write weight to the given node.
  --write-node=<IPADDRESS>:<PORT>    Specifies the node that is to be used for writes for singlewrite mode.
                                     If left unspecified, the cluster node is then used as the write node.
                                     This only applies when 'mode=singlewrite' is used.

EOF
}


#
# Prints out the script version
#
# Globals:
#   PROXYSQL_ADMIN_VERSION
#
# Parameters:
#   None
#
function version()
{
    local path=$0
    printf "%s Version: %s\n" "${path##*/}" "${PROXYSQL_ADMIN_VERSION}"
    printf "$($SCHEDULER_BINARY --version)\n"
}


# Reads in the given line from the section in a config file
#
# Globals:
#   MY_PRINT_DEFAULTS (this must be setup beforehand)
#
# Arguments:
#   1: lineno
#   2: the path to the config file
#   3: the section in the config file
#   4: the option name
#   5: default value (if option does not exist)
#   6: required (set to 1 if a required value)
#
# Output:
#   Writes out the value of the option to stdout (if found)
#   Writes out the error message to stderr
#
# Returns:
#   0 (success) : if value is found
#                 if value is not found (returns 0 if not required)
#   1 (failure) : if value is not found and is required
#
function read_from_config_file()
{
  local lineno=$1
  local config_path="$2"
  local section=$3
  local option=$4
  local default=$5
  local required=$6
  local retval

  retval=$(${MY_PRINT_DEFAULTS} --defaults-file="${config_path}" --show "${section}" | \
          awk -F= '{
                     sub(/^--loose/,"-",$0);
                     st=index($0,"="); \
                     cur=$0; \
                     if ($1 ~ /_/) \
                         { gsub(/_/,"-",$1);} \
                     if (st != 0) \
                         { print $1"="substr(cur,st+1) } \
                     else { print cur }
                   }' | grep -- "--${option}=" | cut -d= -f2- | tail -1)

  # use default if we haven't found a value
  if [[ -z ${retval} ]]; then
      [[ -n ${default} ]] && retval=${default}
      if [[ $required -eq 1 ]]; then
        # This is a required value
        error "$LINENO" "Cannot find a required value : '${option}' in section '${section}'" \
                      "\n-- in the config file:${config_path}"
        echo ${retval}
        return 1
      fi
  fi
  echo ${retval}
  return 0
}


# Executes a SQL query with the (fully) specified server
#
# Globals:
#   DEBUG
#   TIMEOUT
#
# Arguments:
#   1: lineno
#   2: the name of the user
#   3: the user's password
#   4: the hostname of the server
#   5: the port used to connect to the server
#   6: the query to be run
#   7: (optional) arguments to the mysql client
#   8: (optional) additional options, space separated
#      Available options:
#       "hide_output"
#         This will not show the output of the query when DEBUG is set.
#         Used to stop the display of sensitve information (such as passwords)
#         from being displayed when debugging.
#
function exec_sql() {
  local lineno=$1
  local user=$2
  local password=$3
  local hostname=$4
  local port=$5
  local query=$6
  local args=""
  local more_options=""
  local retvalue
  local retoutput
  local default_auth=""
  local defaults=""

  if [[ $# -ge 7 ]]; then
    args=$7
  fi

  if [[ $# -ge 8 ]]; then
    more_options=$8
  fi

  debug "$lineno" "exec_sql : $user@$hostname:$port ==> $query"

  if [[ $MYSQL_CLIENT_VERSION == "8.0" ]]; then
    default_auth="default-auth=mysql_native_password"
  fi

  defaults=$(printf '[client]\nuser=%s\npassword="%s"\nhost=%s\nport=%s\nconnect-timeout=%s\n%s' \
    "${user}" \
    "${password}" \
    "${hostname}" \
    "${port}" \
    "${TIMEOUT}" \
    "${default_auth}"
  )

  if [[ $USE_STDIN_FOR_CREDENTIALS -eq 1 ]]; then
    retoutput=$(printf "%s" "${defaults}" | mysql --defaults-file=/dev/stdin --protocol=tcp --unbuffered --batch --silent ${args} -e "$query")
    retvalue=$?
  else
    retoutput=$(mysql --defaults-file=<(echo "${defaults}") --protocol=tcp --unbuffered --batch --silent ${args} -e "$query")
    retvalue=$?
  fi

  if [[ $DEBUG -eq 1 ]]; then
    local number_of_newlines=0
    local dbgoutput=$retoutput

    if [[ " $more_options " =~ [[:space:]]hide_output[[:space:]] ]]; then
      dbgoutput="**** data hidden ****"
    fi

    if [[ -n $dbgoutput ]]; then
      number_of_newlines=$(printf "%s" "${dbgoutput}" | wc -l)
    fi

    if [[ $retvalue -ne 0 ]]; then
      debug "" "--> query failed $retvalue"
    elif [[ -z $dbgoutput ]]; then
      debug "" "--> query returned $retvalue : <query returned no data>"
    elif [[ ${number_of_newlines} -eq 0 ]]; then
      debug "" "--> query returned $retvalue : ${dbgoutput}"
    else
      debug "" "--> query returned $retvalue : <data follows>"
      printf "%s\n" "${dbgoutput}" | while IFS= read -r line; do
        debug "" "----> $line"
      done
    fi
  fi

  printf "%s" "${retoutput}"
  return $retvalue
}


# Executes a SQL query on proxysql
#
# Globals:
#   PROXYSQL_USERNAME
#   PROXYSQL_PASSWORD
#   PROXYSQL_HOSTNAME
#   PROXYSQL_PORT
#
# Arguments:
#   1: lineno
#   2: The SQL query
#   3: (optional) Additional arguments to the mysql client for the query
#   4: (optional) more options, see exec_sql
#
function proxysql_exec() {
  local lineno=$1
  local query=$2
  local args=""
  local more_options=""

  if [[ $# -ge 3 ]]; then
    args=$3
  fi

  if [[ -z $args ]]; then
    args="--skip-column_names"
  fi

  if [[ $# -ge 4 ]]; then
    more_options=$4
  fi

  exec_sql "$lineno" "$PROXYSQL_USERNAME" "$PROXYSQL_PASSWORD" \
           "$PROXYSQL_HOSTNAME" "$PROXYSQL_PORT" \
           "$query" "$args" "$more_options"

  return $?
}


# Executes a SQL query on a specific node in the cluster
#
# Globals:
#   CLUSTER_USERNAME
#   CLUSTER_PASSWORD
#
# Arguments:
#   1: lineno
#   2: cluster host
#   3: cluster port
#   4: The SQL query
#   5: Additional arguments to the mysql client for the query
#   6: (optional) more options, see exec_sql
#
function cluster_exec() {
  local lineno=$1
  local cluster_host=$2
  local cluster_port=$3
  local query=$4
  local args=""
  local more_options=""

  if [[ $# -ge 5 ]]; then
    args=$5
  fi

  if [[ $# -ge 6 ]]; then
    more_options=$6
  fi

  exec_sql "$lineno" "$CLUSTER_USERNAME" "$CLUSTER_PASSWORD" \
           "$cluster_host" "$cluster_port" \
           "$query" "$args" "$more_options"

  return $?
}


# Executes a SQL query on the CLUSTER_HOSTNAME/CLUSTER_PORT
# specified in the config file.
#
# Globals:
#   CLUSTER_USERNAME
#   CLUSTER_PASSWORD
#   CLUSTER_HOSTNAME
#   CLUSTER_PORT
#
# Arguments:
#   1: lineno
#   2: The SQL query
#   3: Additional arguments to the mysql client for the query
#   4: (optional) more options, see exec_sql
#
function mysql_exec() {
  local lineno=$1
  local query=$2
  local args=""
  local more_options=""

  if [[ $# -ge 3 ]]; then
    args=$3
  fi

  if [[ $# -ge 4 ]]; then
    more_options=$4
  fi

  cluster_exec "$lineno" "$CLUSTER_HOSTNAME" "$CLUSTER_PORT" \
           "$query" "$args" "$more_options"

  return $?
}


# Executes a SQL query on a cluster ndde with the monitor credentials
#
# Globals:
#   CLUSTER_HOSTNAME
#   CLUSTER_PORT
#
# Arguments:
#   1: lineno
#   2: The monitor username
#   3: The monitor password
#   4: Additional arguments to the mysql client for the query
#   5: The SQL query
#   6: (optional) more options, see exec_sql
#
function monitor_exec() {
  local lineno=$1
  local user=$2
  local password=$3
  local args=$4
  local query=$5
  local more_options=""

  if [[ $# -ge 6 ]]; then
    more_options=$7
  fi

  exec_sql "$lineno" "$user" "$password" \
           "$CLUSTER_HOSTNAME" "$CLUSTER_PORT" \
           "$query" "$args" "$more_options"

  return $?
}


# Check proxysql running status
#
# Globals:
#   None
#
# Arguments:
#   None
#
# Exits if we could not connect to the proxysql instance
#
function proxysql_connection_check() {
  proxysql_exec "$LINENO" "SELECT 1" >/dev/null
  check_cmd $? "$LINENO" "ProxySQL connection check failed."\
                       "\n-- Could not connect to ProxySQL at $PROXYSQL_HOSTNAME:$PROXYSQL_PORT"\
                       "\n-- Please check the ProxySQL connection parameters and status."
  debug "$LINENO" "ProxySQL connection check succeeded"
}


# Check the PXC cluster running status
# (well one node in the cluster anyway)
#
# Globals:
#   None
#
# Arguments:
#   None
#
# Exits if we could not connect to a cluster node
#
function cluster_connection_check() {
  mysql_exec "$LINENO" "SELECT @@PORT" >/dev/null
  check_cmd $? "$LINENO" "Server connection check failed."\
                       "\n-- Could not connect to the server at $CLUSTER_HOSTNAME:$CLUSTER_PORT"\
                       "\n-- Please check the connection parameters and status."
  debug "$LINENO" "server connection check succeeded"
}


# Checks for certain variables and prompts the user for
# the values if necessary.
#
# Globals:
#   QUICK_DEMO
#   MONITOR_USERNAME, MONITOR_PASSWORD
#   CLUSTER_APP_USERNAME, CLUSTER_APP_PASSWORD
#   CLUSTER_HOSTNAME, CLUSTER_USERNAME
#   USER_HOST_RANGE
#
# Arguments:
#   1: the category for the variable, this may either be MONITOR or CLUSTER_APP
#   2: a description of the user
#   3: the hostgroup to associate the user with
#     (only needed if user_category='CLISTER APP')
#
function user_input_check() {
  debug "$LINENO" "user_input_check ( $(dump_arguments "$@") )"
  local user_category=$1
  local user_description=$2
  local hostgroup_id
  local username
  local password
  local safe_username
  local safe_password

  if [[ $user_category == "CLUSTER_APP" ]]; then
    hostgroup_id=$3
  fi

  username=$(eval "echo \$${user_category}_USERNAME")
  password=$(eval "echo \$${user_category}_PASSWORD")

  if [[ -z $username ]]; then
    read -r -p "Enter ${user_description}name : " ${user_category}_USERNAME
    username=$(eval "echo \$${user_category}_USERNAME")
    while [[ -z ${username} ]]
    do
      echo -n "No input entered, Enter ${user_description} name: "
      read -r ${user_category}_USERNAME
      username=$(eval "echo \$${user_category}_USERNAME")
    done
  else
    if [[ $QUICK_DEMO -eq 0 ]]; then
      echo -e "${user_description} name as per command line/config-file is ${BD}$(eval "echo \$${user_category}_USERNAME")${NBD}"
    fi
  fi
  if [[ -z $password ]]; then
    if [[ $QUICK_DEMO -eq 0 ]]; then
      read -r -s -p  "Enter ${user_description} password: " ${user_category}_PASSWORD
      password=$(eval "echo \$${user_category}_PASSWORD")
      while [[ -z $password ]]
      do
        read -r -s -p  "No input entered, Enter ${user_description} password: " ${user_category}_PASSWORD
        password=$(eval "echo \$${user_category}_PASSWORD")
      done
    fi
  fi
  username=$(eval "echo \$${user_category}_USERNAME")
  password=$(eval "echo \$${user_category}_PASSWORD")

  safe_username=${username//\'/\'\'}
  safe_password=${password//\'/\'\'}

  if [[ $user_category == "CLUSTER_APP" ]]; then
    local check_user
    check_user=$(mysql_exec "$LINENO" "SELECT user,host FROM mysql.user where user='$safe_username' and host='$USER_HOST_RANGE';")
    check_cmd $? "$LINENO" "Failed to retrieve the user information from PXC."\
                         "\n-- Please check the PXC connection parameters and status."

    if [[ -z "$check_user" ]]; then
      if [[ $FORCE -eq 1 ]]; then
        proxysql_exec "$LINENO" "DELETE FROM mysql_users where username='$safe_username';"
        check_cmd $? "$LINENO" "Failed to delete the PXC application user: '$username' from the ProxySQL database."\
                             "\n-- Please check the ProxySQL connection parameters and status."
      fi
      local precheck_user
      precheck_user=$(proxysql_exec "$LINENO" "SELECT username FROM mysql_users where username='$safe_username'")
      check_cmd $? "$LINENO" "Failed to query ProxySQL for the user."\
                           "\n-- Please check the ProxySQL connection parameters and status."

      if [[ -z "$precheck_user" ]]; then
        mysql_exec "$LINENO" "CREATE USER '$safe_username'@'$USER_HOST_RANGE' IDENTIFIED WITH mysql_native_password BY '$safe_password';"
        check_cmd $? "$LINENO" "Failed to add the PXC application user to PXC: $username" \
                             "\n-- Please check if '$CLUSTER_USERNAME'@'$CLUSTER_HOSTNAME' has the proper permissions to create the montioring user"

        if [[ $QUICK_DEMO -eq 1 ]]; then
          mysql_exec "$LINENO" "GRANT ALL ON *.* to '$safe_username'@'$USER_HOST_RANGE'"
          check_cmd $? "$LINENO" "Failed to grant permissions to '$username'@'$USER_HOST_RANGE'"\
                               "\n-- Please check if $CLUSTER_USERNAME@'$CLUSTER_HOSTNAME' has the GRANT privilege"\
                               "\n-- required to assign the requested permissions"
        fi

        proxysql_exec "$LINENO" \
          "INSERT INTO mysql_users
              (username,password,active,default_hostgroup)
            VALUES
              ('$safe_username','$safe_password',1,$hostgroup_id);"
        check_cmd $? "$LINENO" "Failed to add the PXC application user: '$username' to the ProxySQL database."\
                             "\n-- Please check the ProxySQL connection parameters and status."

        proxysql_load_to_runtime_save_to_disk "MYSQL USERS" "$LINENO" 1

        if [[ $QUICK_DEMO -eq 0 ]]; then
          echo -e "\nPercona XtraDB Cluster application user '${BD}$username'@'$USER_HOST_RANGE${NBD}' has been added with ${BD}ALL${NBD} privileges, ${BD}this user is created for testing purposes${NBD}"
        else
          echo -e "\nPercona XtraDB Cluster application user '${BD}$username'@'$USER_HOST_RANGE${NBD}' has been added with the USAGE privilege, please make sure to the grant appropriate privileges"
        fi
      else
        error "$LINENO" "The application user ${BD}$username${NBD} is already present in the ProxySQL database."
        echo -e "-- Note: ProxySQL does not allow duplicate usernames."
        exit 1
      fi
    else
      if [[ $FORCE -eq 1 ]]; then
        proxysql_exec "$LINENO" "DELETE FROM mysql_users where username='$safe_username';"
        check_cmd $? "$LINENO" "Failed to delete the PXC application user: '$safe_username' from the ProxySQL database."\
                             "\n-- Please check the ProxySQL connection parameters and status."
      fi
      local check_user
      check_user=$(proxysql_exec "$LINENO" "SELECT username FROM mysql_users where username='$safe_username'")
      check_cmd $? "$LINENO" "Could not retrieve the users from the ProxySQL database."\
                           "\n-- Please check the ProxySQL connection parameters and status."
      if [[ -z "$check_user" ]]; then
        echo -e "\nApplication user '${BD}${username}'@'$USER_HOST_RANGE${NBD}' already present in PXC.\n"
        proxysql_exec "$LINENO" "INSERT INTO mysql_users (username,password,active,default_hostgroup) values ('$safe_username','$safe_password',1,$hostgroup_id);"
        check_cmd $? "$LINENO" "Failed to add the PXC application user: '$username' to the ProxySQL database."\
                             "\n-- Please check the ProxySQL connection parameters and status."

        proxysql_load_to_runtime_save_to_disk "MYSQL USERS" "$LINENO" 1
      else
        error "$LINENO" "The application user ${BD}$username${NBD} is already present in the ProxySQL database."
        echo -e "-- Note: ProxySQL does not allow duplicate usernames."
        exit 1
      fi
    fi
  fi
}



# Gets the list of hostgroups given only the writer hostgroup
#
# Globals:
#   None
#
# Arguments:
#   Parameter 1: the writer hostgroup
#
# Outputs:
#   A list of hostgroups, separated by commas (',')
#   The order of the hostgroups is:
#     1. writer hostgroup
#     2. reader hostgroup
#     3. backup writer hostgroup
#     4. offline hostgroup
#
function get_all_hostgroups_from_writer_hostgroup()
{
  echo "$WRITER_HOSTGROUP_ID,$READER_HOSTGROUP_ID,$WRITER_MAINT_HOSTGROUP_ID,$WRITER_CONFIG_HOSTGROUP_ID,$READER_MAINT_HOSTGROUP_ID,$READER_CONFIG_HOSTGROUP_ID"
}


# Adds the list of servers to ProxySQL
# This will add all of the servers in the list to the:
#   Writer hostgroup
#   Reader hostgroup
#   Writer Configuration hostgroup
#   Reader Configuration hostgroup
#
# ProxySQL is responsible for moving them to the correct hostgroup
#
# Globals:
#   WRITER_HOSTGROUP_ID
#   READER_HOSTGROUP_ID
#   WRITER_CONFIG_HOSTGROUP_ID
#   READER_CONFIG_HOSTGROUP_ID
#   MAX_CONNECTIONS
#   USE_SSL_OPTION
#
# Arguments:
#   Parameter 1 : A list of servers (space-separated in a string)
#
function add_servers_to_proxysql()
{
  local server_list=$1
  for i in ${server_list}; do
    local ws_address ws_ip ws_port
    ws_address=$(separate_ip_port_from_address "$i")
    ws_ip=$(echo "$ws_address" | cut -d' ' -f1)
    ws_port=$(echo "$ws_address" | cut -d' ' -f2)

    proxysql_exec "$LINENO" \
      "INSERT INTO mysql_servers
        (hostname,hostgroup_id,port,weight,max_connections,use_ssl)
      VALUES
        ('$ws_ip',$WRITER_HOSTGROUP_ID,$ws_port,1000,$MAX_CONNECTIONS,$USE_SSL_OPTION),
        ('$ws_ip',$READER_HOSTGROUP_ID,$ws_port,1000,$MAX_CONNECTIONS,$USE_SSL_OPTION),
        ('$ws_ip',$WRITER_CONFIG_HOSTGROUP_ID,$ws_port,1000,$MAX_CONNECTIONS,$USE_SSL_OPTION),
        ('$ws_ip',$READER_CONFIG_HOSTGROUP_ID,$ws_port,1000,$MAX_CONNECTIONS,$USE_SSL_OPTION)
        ;"
    check_cmd $? "$LINENO" "Failed to add the PXC server node with address: $i."\
                         "\n-- Please check the ProxySQL connection parameters and status."
  done
}

# Updates the weight of a given node to a specific value
#
# Globals:
#   WRITER_HOSTGROUP_ID
#   READER_HOSTGROUP_ID
#   WRITER_CONFIG_HOSTGROUP_ID
#   READER_CONFIG_HOSTGROUP_ID
#

function update_weight() {

  local type=$1
  local update_weight=$2
  local update_hgs operation

  if [[ $type == "READ" ]]; then
    operation="--update-read-weight"
    update_hgs="($READER_HOSTGROUP_ID,$READER_CONFIG_HOSTGROUP_ID)"
  else
    operation="--update-write-weight"
    update_hgs="($WRITER_HOSTGROUP_ID,$WRITER_CONFIG_HOSTGROUP_ID)"
  fi

  # Extract node details and validate inputs
  local address=$(echo $update_weight | cut -d, -f1)
  local weight=$(echo $update_weight | cut -d, -f2)

  local node_address=$(separate_ip_port_from_address "$address")
  local node_ip=$(echo "$node_address" | cut -d' ' -f1)
  local node_port=$(echo "$node_address" | cut -d' ' -f2)

  host_info=$(proxysql_exec "$LINENO" \
          "SELECT COUNT(*)
             FROM mysql_servers
             WHERE hostgroup_id IN $update_hgs AND
               hostname='$node_ip' AND
               port=$node_port")
  check_cmd $? "$LINENO" "Could not retrieve hostgroup and server information from ProxySQL"\
                         "\n-- Please check the ProxySQL connection parameters and status."

  if [ $host_info -eq 0 ]; then
    error "$LINENO" "Failed to execute $operation: Could not find the node belonging to the cluster with address $address"
    exit 1
  fi

  local hgs

  # We are here means we are good to update the weight.
  proxysql_exec "$LINENO" \
    "UPDATE mysql_servers
      SET weight = $weight
      WHERE
        hostname='$node_ip' AND
        hostgroup_id IN $update_hgs AND
        port=$node_port"
  check_cmd $? "$LINENO" "Failed to update the write-node" \
                       "\n-- Please check the ProxySQL connection parameters and status."

}


# Adjusts the weights of the servers when in a singlewrite configuration.
#
# This will adjust the weights of the servers in the list
#
# Election of primary MUST be deterministic, this is a must have requirement in
# real-world. So we must set a clear priority for the writers like: 1000, 999,
# 998.
#
# At the same time when possible we should also reduce the load of reads on the
# primary, which means that we should have something like: 900 for the writer
# while 1000,1000 for the other readers.
#
# Also, this should be consistent both in writer HG and Writer Config HG  after
# which the scheduler will manage it using the Writer Config HG as reference.
#
# Globals:
#   WRITER_HOSTGROUP_ID
#   READER_HOSTGROUP_ID
#   WRITER_CONFIG_HOSTGROUP_ID
#   READER_CONFIG_HOSTGROUP_ID
#
function adjust_weights () {

  local errcode
  is_mode_singlewrite $WRITER_HOSTGROUP_ID
  if [[ $? -eq 0 && $AUTO_ASSIGN_WEIGHTS -eq 1 ]]; then

    # Load the servers to runtime and wait till the scheduler processes the cluster nodes.
    proxysql_load_to_runtime_save_to_disk "MYSQL SERVERS" $LINENO

    wait_for_scheduler_processing

    # Fetch the writer node.
    local writer_node

    writer_node=$(proxysql_exec "$LINENO" \
      "SELECT hostname  || ':' || port
        FROM mysql_servers
        WHERE
          hostgroup_id = $WRITER_HOSTGROUP_ID AND
          status NOT IN (\"OFFLINE_HARD\",\"OFFLINE_SOFT\")")
    check_cmd $? "$LINENO" "Failed to query ProxySQL for the user."\
                           "\n-- Please check the ProxySQL connection parameters and status."

    local writer_host=${writer_node%:*}
    local writer_port=${writer_node##*:}

    debug "Chosen writer node = $writer_host:$writer_port, reducing its reader's weightage to 900"

    # Set the weight of all nodes to 1000
    proxysql_exec "$LINENO" \
      "UPDATE mysql_servers
        SET weight = 1000"
    check_cmd $? "$LINENO" "Failed to update the weight" \
                         "\n-- Please check the ProxySQL connection parameters and status."

    # Set the writer node's weight in reader HG
    proxysql_exec "$LINENO" \
      "UPDATE mysql_servers
        SET weight = 900
        WHERE
          hostname = '$writer_host' AND
          port = $writer_port AND
          hostgroup_id IN ($READER_HOSTGROUP_ID,$READER_CONFIG_HOSTGROUP_ID);"
    check_cmd $? "$LINENO" "Failed to update weight" \
                         "\n-- Please check the ProxySQL connection parameters and status."


    # Now update write weights of other nodes.
    # To do so, update the writer-config HG.
    #
    # Get the list of nodes in writer config other than the current writer.
    # For each node update weight.
    local backup_writers
    backup_writers=$(proxysql_exec "$LINENO" \
        "SELECT hostname  || ':' || port AS bkp_writer
        FROM mysql_servers
        WHERE
          hostgroup_id = $WRITER_CONFIG_HOSTGROUP_ID AND
          bkp_writer != \"$writer_host:$writer_port\" AND
          status NOT IN (\"OFFLINE_HARD\",\"OFFLINE_SOFT\")")
    check_cmd $? "$LINENO" "Failed to query ProxySQL for the user."\
                           "\n-- Please check the ProxySQL connection parameters and status."
    if [[ -n $backup_writers ]]; then
      # Extract the hostname and port from the rows
      # Creates a string of "host:port" separated by spaces
      local counter=1
      while read line; do
        if [[ -z $line ]]; then
          continue
        fi

        local ip_addr=${line%:*}
        local port=${line##*:}
        local new_weight=$((1000-counter))

        # Update the new weights in WRITER_CONFIG_HOSTGROUP_ID
        proxysql_exec "$LINENO" \
        "UPDATE mysql_servers
          SET weight = $new_weight
          WHERE
          hostgroup_id IN ($WRITER_HOSTGROUP_ID, $WRITER_CONFIG_HOSTGROUP_ID) AND
            hostname = '$ip_addr' AND
            port = '$port' AND
            status NOT IN ('OFFLINE_HARD','OFFLINE_SOFT')"
        check_cmd $? "$LINENO" "Failed to query ProxySQL for the user."\
                     "\n-- Please check the ProxySQL connection parameters and status."
        counter=$(($counter+1))

      done< <(printf "%s\n" "$backup_writers")

      # Set the writer weight to 1000.
      proxysql_exec "$LINENO" \
      "UPDATE mysql_servers
        SET weight = 1000
        WHERE
        hostname='$writer_host' AND
        port=$writer_port AND
        hostgroup_id IN ($WRITER_HOSTGROUP_ID, $WRITER_CONFIG_HOSTGROUP_ID) AND
        status NOT IN ('OFFLINE_HARD','OFFLINE_SOFT')"
      check_cmd $? "$LINENO" "Failed to query ProxySQL for the user."\
                   "\n-- Please check the ProxySQL connection parameters and status."

    fi
    proxysql_load_to_runtime_save_to_disk "MYSQL SERVERS" $LINENO
  fi
}
# Auto configure Percona XtraDB Cluster nodes into ProxySQL
#
# Globals:
#   READER_HOSTGROUP_ID, WRITER_HOSTGROUP_ID, BACKUP_WRITER_HOSTGROUP_ID, OFFLINE_HOSTGROUP_ID
#   CLUSTER_PORT
#   USER_HOST_RANGE
#   MONITOR_USERNAME, MONITOR_PASSWORD
#   CLUSTER_APP_USERNAME, CLUSTER_APP_PASSWORD
#   CLUSTER_USERNAME, CLUSTER_HOSTNAME
#   USE_EXISTING_MONITOR_PASSWORD
#   WITH_CLUSTER_APP_USER
#   QUICK_DEMO
#   WRITE_NODE
#   MODE
#
# Arguments:
#   None
#
function enable_proxysql() {
  debug "$LINENO" "enable_proxysql ()"

  # Checking proxysql binary location
  check_binary "proxysql"
  if [[ $? -ne 0 ]]; then
    error "$LINENO" "The proxysql binary was not found."\
                  "\n-- Please install the ProxySQL package."
    exit 1
  fi

  proxysql_connection_check

  ! go_scheduler_capability_check "$LINENO" && exit 1


  cluster_connection_check
  local cluster_name
  cluster_name=$(mysql_exec "$LINENO" "select @@wsrep_cluster_name;")
  check_cmd $? "$LINENO" "Could not retrieve the cluster name from the PXC cluster at $CLUSTER_HOSTNAME:$CLUSTER_PORT"\
                       "\n-- Please check the PXC connection parameters and status."
  readonly cluster_name
  debug "$LINENO" "PXC cluster name is : $cluster_name"

  local all_hostgroups
  local check_hgs
  all_hostgroups=$(get_all_hostgroups_from_writer_hostgroup $WRITER_HOSTGROUP_ID)

  debug "$LINENO" "Hostgroups used for this configuration: ${all_hostgroups}"

  # Now, check to see if there are any entries in mysql_servers
  # using these hostgroups. This check will be ignored if we use --force option.
  if [[ $FORCE -ne 1 ]]; then
    check_hgs=$(proxysql_exec "$LINENO" \
      "SELECT hostgroup_id
        FROM mysql_servers
        WHERE
          hostgroup_id IN ($all_hostgroups) AND
          status <> 'OFFLINE_HARD'")
    check_cmd $? "$LINENO" "Could not retrieve hostgroup and server information from ProxySQL"\
                         "\n-- Please check the ProxySQL connection parameters and status."
    if [[ -n "$check_hgs" ]]; then
      error "$LINENO" "One or more of the hostgroups($all_hostgroups) has a server assigned to that hostgroup."\
                    "\n-- To avoid conflicts, please use different values for the hostgroups."
      proxysql_exec "$LINENO" \
        "SELECT
            hostgroup_id,
            hostname,
            port,
            status,
            weight
          FROM mysql_servers
          WHERE
            hostgroup_id IN ($all_hostgroups)" "-t"
      echo ""
      exit 1
    fi
  fi

  # Clear out any servers that are OFFLINE_HARD
  # If --disable is called, then --enable is called quickly afterwards,
  # ProxySQL may still be updating the previous server list.  So this
  # will remove any leftovers in the runtime_mysql_servers table
  proxysql_exec "$LINENO" \
    "DELETE
      FROM mysql_servers
      WHERE
        hostgroup_id in ($all_hostgroups) AND
        status = 'OFFLINE_HARD'"
  check_cmd $? "$LINENO" "Could not delete offline servers from ProxySQL"\
                       "\n-- Please check the ProxySQL connection parameters and status."

  local cluster_network
  local wsrep_addresses
  wsrep_addresses=$(mysql_exec "$LINENO" "show status like 'wsrep_incoming_addresses'")
  check_cmd $? "$LINENO" "Could not retrieve the cluster addresses from PXC."\
                       "\n-- Please check the PXC connection parameters and status."
  wsrep_addresses=$(echo "$wsrep_addresses" | awk '{print $2}' | sed 's|,| |g')
  cluster_network=$(echo "$wsrep_addresses" | cut -d' ' -f1 | cut -d'.' -f1)

  # If we have only digits in $cluster_network, then we can assume
  # that we have an IPv4 address
  if [[ "$cluster_network" =~ ^[0-9]+$ ]]; then
    USER_HOST_RANGE="$cluster_network.%"
  else
    USER_HOST_RANGE="%"
  fi

  # More sanity checks
  local writer_ws_ip
  local writer_ws_port
  local writer_ws_address=""

  # If we have specified a write-node, check that the node is NOT read-only
  if [[ $MODE == 'singlewrite' ]]; then

    # If there is no WRITE_NODE specified, check that we have at least
    # one write node available
    for i in $wsrep_addresses; do
      [[ -z $i ]] && continue;

      local ws_address ws_ip ws_port
      ws_address=$(separate_ip_port_from_address "$i")
      ws_ip=$(echo "$ws_address" | cut -d' ' -f1)
      ws_port=$(echo "$ws_address" | cut -d' ' -f2)

      local is_read_only
      is_read_only=$(exec_sql "$LINENO" "$CLUSTER_USERNAME" "$CLUSTER_PASSWORD" "$ws_ip" "$ws_port" \
        "select @@global.read_only")
      if [[ $? -eq 0 && $is_read_only -eq 0 ]]; then
        writer_ws_ip=$ws_ip
        writer_ws_port=$ws_port
        writer_ws_address=$(combine_ip_port_into_address "$writer_ws_ip" "$writer_ws_port")
        break
      fi
    done

    if [[ -z $writer_ws_address ]]; then
      error "$LINENO" "Could not find a writer node." \
                    "\n-- Cluster nodes checked: $wsrep_addresses"
      exit 1
    fi

    if [[ " ${wsrep_addresses} " != *"$writer_ws_address"* ]]; then
      error "$LINENO" "Writer node cluster address($writer_ws_address) does not exist"\
                    "\n-- in WSREP incoming address(${wsrep_addresses})."
      echo -e "-- Different wsrep incoming and cluster IP addresses are not supported"
      echo -e "-- by proxysql-admin at this time.  Please configure ProxySQL manually."
      exit 1
    fi

  fi

  echo -e "Configuring the ProxySQL monitoring user."
  user_input_check MONITOR "ProxySQL monitor user"
  SAFE_MONITOR_USERNAME=${MONITOR_USERNAME//\'/\'\'}
  SAFE_MONITOR_PASSWORD=${MONITOR_PASSWORD//\'/\'\'}
  readonly MONITOR_USERNAME

  local check_user
  check_user=$(mysql_exec "$LINENO" \
    "SELECT user,host
      FROM mysql.user
      WHERE user='${SAFE_MONITOR_USERNAME}' AND host='$USER_HOST_RANGE';")
  check_cmd $? "$LINENO" "Could not retrieve the monitor user information from PXC."\
                       "\n-- Please check the PXC cluster connection parameters and status."
  if [[ -z "$check_user" ]]; then
    # No monitor user found in MySQL, create the monitor user
    mysql_exec "$LINENO" "CREATE USER '$SAFE_MONITOR_USERNAME'@'$USER_HOST_RANGE' IDENTIFIED WITH mysql_native_password BY '$SAFE_MONITOR_PASSWORD';"
    check_cmd $? "$LINENO"  "Failed to create the ProxySQL monitoring user."\
                          "\n-- Please check that '$CLUSTER_USERNAME'@'$CLUSTER_HOSTNAME' has the proper permissions to create the montioring user"
  fi

  # Ensure that we have the coorrect permissions
  mysql_exec "$LINENO" "GRANT USAGE ON *.* TO '$SAFE_MONITOR_USERNAME'@'$USER_HOST_RANGE'"
  check_cmd $? "$LINENO"  "Failed to grant usage privileges to the ProxySQL monitoring user."\
                        "\n-- Please check that '$CLUSTER_USERNAME'@'$CLUSTER_HOSTNAME' has the proper permissions to create the montioring user"

  local sql_cmd
  sql_cmd='GRANT SELECT ON `performance_schema`.* TO '
  sql_cmd+="'$SAFE_MONITOR_USERNAME'@'$USER_HOST_RANGE'; FLUSH PRIVILEGES"
  mysql_exec "$LINENO" "${sql_cmd}"
  check_cmd $? "$LINENO"  "Failed to grant perfschema privileges to the ProxySQL monitoring user."\
                        "\n-- Please check that '$CLUSTER_USERNAME'@'$CLUSTER_HOSTNAME' has the proper permissions to create the montioring user"

  if [[ -z "$check_user" ]]; then
    proxysql_exec "$LINENO" \
      "UPDATE global_variables
        SET variable_value='$SAFE_MONITOR_USERNAME'
        WHERE variable_name='mysql-monitor_username';
       UPDATE global_variables
        SET variable_value='$SAFE_MONITOR_PASSWORD'
        WHERE variable_name='mysql-monitor_password'; "
    check_cmd $? "$LINENO"  "Failed to set the mysql-monitor variables in ProxySQL."\
                          "\n-- Please check the ProxySQL connection parameters and status."

    proxysql_load_to_runtime_save_to_disk "MYSQL VARIABLES" $LINENO

    echo -e "\nUser '${BD}$MONITOR_USERNAME'@'$USER_HOST_RANGE${NBD}' has been added with USAGE privileges"
  else
    # Monitor user found in MySQL, do we want to use this existing user?
    local check_param

    if [[ $USE_EXISTING_MONITOR_PASSWORD -eq 1 ]]; then
      # We have a password, so no need to ask for a new password
      check_param="n"
    else
      echo ""
      echo -e "The monitoring user is already present in Percona XtraDB Cluster.\n"
      read -p "Would you like to enter a new password [y/n] ? " check_param
    fi
    case $check_param in
      y|Y)
        read -r -s -p  "Please enter the password you have assigned to the monitoring user '$MONITOR_USERNAME': " MONITOR_PASSWORD
        echo ""

        SAFE_MONITOR_PASSWORD=${MONITOR_PASSWORD//\'/\'\'}

        monitor_exec "$LINENO" "$MONITOR_USERNAME" "$MONITOR_PASSWORD" "-Bs" "SELECT 1" >/dev/null
        check_cmd $? "$LINENO" "Failed to connect to Percona XtraDB Cluster using monitor credentials."\
                             "\n-- Please check MONITOR_USERNAME and MONITOR_PASSWORD"

        proxysql_exec "$LINENO" \
          "UPDATE global_variables
            SET variable_value='$SAFE_MONITOR_USERNAME'
            WHERE variable_name='mysql-monitor_username';
           UPDATE global_variables
            SET variable_value='$SAFE_MONITOR_PASSWORD'
            WHERE variable_name='mysql-monitor_password'; "
        check_cmd $? "$LINENO"  "Failed to set the mysql-monitor variables in ProxySQL."\
                              "\n-- Please check the ProxySQL connection parameters"
        proxysql_load_to_runtime_save_to_disk "MYSQL VARIABLES" $LINENO
        echo -e "\nMonitoring user '${BD}$MONITOR_USERNAME'@'$USER_HOST_RANGE${NBD}' has been setup in the ProxySQL database."
      ;;
      n|N)
        monitor_exec "$LINENO" "$MONITOR_USERNAME" "$MONITOR_PASSWORD" "-Bs" "SELECT 1" >/dev/null
        check_cmd $? "$LINENO" "Failed to connect to Percona XtraDB Cluster using monitor credentials."\
                             "\n-- Please check MONITOR_USERNAME and MONITOR_PASSWORD"

        proxysql_exec "$LINENO" \
          "UPDATE global_variables
            SET variable_value='$SAFE_MONITOR_USERNAME'
            WHERE variable_name='mysql-monitor_username';
           UPDATE global_variables
            SET variable_value='$SAFE_MONITOR_PASSWORD'
            WHERE variable_name='mysql-monitor_password';"
        check_cmd $? "$LINENO"  "Failed to set the mysql-monitor variables in ProxySQL."\
                              "\n-- Please check the ProxySQL connection parameters"

        proxysql_load_to_runtime_save_to_disk "MYSQL VARIABLES" $LINENO
        echo -e "\nMonitoring user '${BD}$MONITOR_USERNAME'@'$USER_HOST_RANGE${NBD}' has been setup in the ProxySQL database."
      ;;
      *)
        error "" "Please type [y/n]!"
        exit 1
      ;;
    esac
  fi
  debug "$LINENO" "monitor username is '$MONITOR_USERNAME'"

  if [[ $WITH_CLUSTER_APP_USER -eq 1 ]]; then
    echo -e "\nConfiguring the Percona XtraDB Cluster application user to connect through ProxySQL"
    user_input_check CLUSTER_APP "Percona XtraDB Cluster application user" $WRITER_HOSTGROUP_ID

    SAFE_CLUSTER_APP_USERNAME=${CLUSTER_APP_USERNAME//\'/\'\'}
    readonly CLUSTER_APP_USERNAME
    readonly CLUSTER_APP_PASSWORD
  fi

  # Adding Percona XtraDB Cluster nodes to ProxySQL
  # -----------------------------------------------

  if [[ $MODE == 'singlewrite' ]]; then
    if [[ $QUICK_DEMO -ne 0 ]]; then
      writer_ws_ip=$(mysql_exec "$LINENO" "show variables like 'wsrep_provider_options'" | grep -o -P '(?<=base_host =).*(?=; base_port)' | xargs)
      check_cmd $? "$LINENO" "Could not get the wsrep_provider_options from the PXC cluster"\
                           "\n-- Please check the PXC connection parameters and status."
      writer_ws_port=$CLUSTER_PORT
      writer_ws_address=$(combine_ip_port_into_address "$writer_ws_ip" "$writer_ws_port")

      if [[ " ${wsrep_addresses} " != *"$writer_ws_address"* ]]; then
        error "$LINENO" "Writer node cluster address($writer_ws_address) does not exist"\
                      "\n-- in WSREP incoming address(${wsrep_addresses})."
        echo -e "-- Different wsrep incoming and cluster IP addresses are not supported"
        echo -e "-- by proxysql-admin at this time.  Please configure ProxySQL manually."
        exit 1
      fi
    fi
  fi


  echo -e "Adding the Percona XtraDB Cluster nodes to ProxySQL"

  # Remove any existing nodes from mysql_servers
  proxysql_exec "$LINENO" "DELETE FROM mysql_servers WHERE hostgroup_id IN ($all_hostgroups)"
  check_cmd $? "$LINENO" "Failed to delete the existing servers with hostgroup: ${all_hostgroups}"\
                       "\n-- Please check the ProxySQL connection parameters and status."

  # TODO: also remove fast query rules

  # Add all nodes as writer nodes (to begin with)
  add_servers_to_proxysql "${wsrep_addresses}"

  if [[ $MODE == "loadbal" ]]; then
    # loadbal for ProxySQL 2.0
    # All nodes are writers (so number of writers is a very large number)
    # All queries go to the write hostgroup
    # All nodes have read_only=0
    # (Reader hostgroup doesn't matter)
    # writer-is-also-reader=0, since we only have writers

    # Setup parameters for loadbal
    MAX_WRITERS=1000000

    # Force this for loadbal, since in loadbal, everyone is a writer
    WRITERS_ARE_READERS_OPTION=0

  elif [[ $MODE == "singlewrite" ]]; then
    # singlewrite for ProxySQL 2.0
    # Number of writers is 1
    # Query rules added so writes go to write hostgroup
    # Reads go to read hostgroup
    # Also writer-is-also-reader=2 (so that backup writers get reads)
    # Must have at least one node with read-only = 0

    if [[ -n $WRITE_NODE ]]; then
      local ws_address ws_ip ws_port
      ws_address=$(separate_ip_port_from_address "$WRITE_NODE")
      ws_ip=$(echo "$ws_address" | cut -d' ' -f1)
      ws_port=$(echo "$ws_address" | cut -d' ' -f2)

      # Check if the node exists.
      local host_info
      host_info=$(proxysql_exec "$LINENO" \
              "SELECT COUNT(*)
                 FROM mysql_servers
                 WHERE hostgroup_id IN ($WRITER_HOSTGROUP_ID) AND
                   hostname='$ws_ip' AND
                   port=$ws_port")
      check_cmd $? "$LINENO" "Could not retrieve hostgroup and server information from ProxySQL"\
                         "\n-- Please check the ProxySQL connection parameters and status."

      if [ $host_info -eq 0 ]; then
        error "$LINENO" "Failed to perform --write-node: Could not find the node belonging to the cluster with address $WRITE_NODE"
        exit 1
      fi

      local is_read_only
      is_read_only=$(exec_sql "$LINENO" "$CLUSTER_USERNAME" "$CLUSTER_PASSWORD" "$ws_ip" "$ws_port" \
        "select @@global.read_only")
      if [[ $? -eq 0 && $is_read_only -eq 1 ]]; then
        writer_ws_ip=$ws_ip
        writer_ws_port=$ws_port
        writer_ws_address=$(combine_ip_port_into_address "$writer_ws_ip" "$writer_ws_port")

        if [[ $FORCE -eq 1 ]]; then
          warning "$LINENO" "The specified write node ($WRITE_NODE) is read-only."
        else
          error "$LINENO" "The specified write node ($WRITE_NODE) is read-only" \
                        "\n-- and cannot be used as a writer node."
          exit 1
        fi
      fi

      # Update the write_node to have a higher priority
      proxysql_exec "$LINENO" \
        "UPDATE mysql_servers
          SET weight = 1000000
          WHERE
            hostname = '$ws_ip' AND
            port = $ws_port AND
            hostgroup_id IN ($WRITER_HOSTGROUP_ID,$WRITER_CONFIG_HOSTGROUP_ID);"
      check_cmd $? "$LINENO" "Failed to update the writer-node for singlewrite mode" \
                           "\n-- Please check the ProxySQL connection parameters and status."
    fi

    if [[ $WITH_CLUSTER_APP_USER -eq 1 ]]; then
      local reader_query_rule_check
      local writer_query_rule_check

      writer_query_rule_check=$(proxysql_exec $LINENO \
        "SELECT COUNT(*)
          FROM mysql_query_rules
          WHERE username = '$SAFE_CLUSTER_APP_USERNAME' AND
            destination_hostgroup = $WRITER_HOSTGROUP_ID AND
            active = 1 AND
            match_digest = '^SELECT.*FOR UPDATE'")
      check_cmd $? "$LINENO" "Failed to retrieve query rules from ProxySQL."\
                           "\n-- Please check the ProxySQL connection parameters and status."
      if [[ $writer_query_rule_check -ge 1 ]]; then
        if [[ $FORCE -eq 1 ]]; then
          # issue a warning
          warning $LINENO "Query rules already exist for user:$SAFE_CLUSTER_APP_USERNAME" \
                        "\n-- and hostgroup:$WRITER_HOSTGROUP_ID." \
                        "\n-- Please verify that they are still valid for this instance."
        else
          # issue an error and exit
          error $LINENO "Query rules already exist for this user:$SAFE_CLUSTER_APP_USERNAME" \
                      "\n-- and hostgroup:$WRITER_HOSTGROUP_ID.  Exiting in case of a possible conflict."
          exit 1
        fi
      else
        # rule doesn't exist, add it
        proxysql_exec "$LINENO" \
          "INSERT INTO mysql_query_rules
            (username,destination_hostgroup,active,match_digest,apply)
           VALUES
            ('$SAFE_CLUSTER_APP_USERNAME',$WRITER_HOSTGROUP_ID,1,'^SELECT.*FOR UPDATE',1)"
        check_cmd $? "$LINENO" "Failed to add query rules to ProxySQL."\
                             "\n-- Please check the ProxySQL connection parameters and status."
      fi

      reader_query_rule_check=$(proxysql_exec $LINENO \
        "SELECT COUNT(*)
          FROM mysql_query_rules
          WHERE username = '$SAFE_CLUSTER_APP_USERNAME' AND
            destination_hostgroup = $READER_HOSTGROUP_ID AND
            active = 1 AND
            match_digest = '^SELECT '")
      check_cmd $? "$LINENO" "Failed to retrieve query rules from ProxySQL."\
                           "\n-- Please check the ProxySQL connection parameters and status."

      if [[ $reader_query_rule_check -ge 1 ]]; then
        if [[ $FORCE -eq 1 ]]; then
          # issue a warning
          warning $LINENO "Query rules already exist for user:$SAFE_CLUSTER_APP_USERNAME" \
                        "\n-- and hostgroup:$READER_HOSTGROUP_ID." \
                        "\n-- Please verify that they are still valid for this instance."
        else
          # issue an error and exit
          error $LINENO "Query rules already exist for this user:$SAFE_CLUSTER_APP_USERNAME" \
                      "\n-- and hostgroup:$READER_HOSTGROUP_ID.  Exiting in case of a possible conflict."
          exit 1
        fi
      else
        proxysql_exec "$LINENO" \
          "INSERT INTO mysql_query_rules
            (username,destination_hostgroup,active,match_digest,apply)
           VALUES
            ('$SAFE_CLUSTER_APP_USERNAME',$READER_HOSTGROUP_ID,1,'^SELECT ',1);"
        check_cmd $? "$LINENO" "Failed to add query rules to ProxySQL."\
                             "\n-- Please check the ProxySQL connection parameters and status."
      fi

      check_cmd $? "$LINENO" "Failed to add the read query rule to ProxySQL."\
                           "\n-- Please check the ProxySQL connection parameters and status."
    fi

    # Setup parameters for singlewrite
    MAX_WRITERS=1
  fi

#  if [[ $NODE_CHECK_INTERVAL -ne -1 ]]; then
#    local healthcheck_value
#    healthcheck_value=$(proxysql_exec "$LINENO" "select variable_value from global_variables where variable_name = 'mysql-monitor_galera_healthcheck_interval'" |
#                                grep -v variable_value)
#    check_cmd $? "$LINENO" "Failed to get the node-check-interval value from ProxySQL."\
#                         "\n-- Please check the ProxySQL connection parameters and status."
#    if [[ $healthcheck_value -ne $NODE_CHECK_INTERVAL ]]; then
#      echo ""
#      warning "$LINENO" "Changing the node check interval to $NODE_CHECK_INTERVAL" \
#              "\n-- This is a global setting and will affect all Galera clusters" \
#              "\n-- managed by this ProxySQL instance."
#
#      proxysql_exec "$LINENO" "SET mysql-monitor_galera_healthcheck_interval = $NODE_CHECK_INTERVAL"
#      check_cmd $? "$LINENO" "Failed to set the node-check-interval in ProxySQL."\
#                           "\n-- Please check the ProxySQL connection parameters and status."
#    fi
#  fi

  echo "Using the scheduler binary located at ${SCHEDULER_BINARY}"

  proxysql_load_to_runtime_save_to_disk "MYSQL VARIABLES" $LINENO
  proxysql_load_to_runtime_save_to_disk "MYSQL SERVERS" $LINENO
  proxysql_load_to_runtime_save_to_disk "MYSQL QUERY RULES" $LINENO

  # TODO: review, possible SQL injection
  # Note: the COMMENT field is essential, this is used with --disable
  # to identify the scheduler row to remove.
  proxysql_exec "$LINENO" \
    "INSERT INTO scheduler
      (active,
       interval_ms,
       filename,
       arg1,
       arg2,
       comment)
     VALUES
      (1,
       ${NODE_CHECK_INTERVAL},
       '${SCHEDULER_BINARY}',
       '--configfile=${CONFIG_FILENAME}',
       '--configpath=${CONFIG_PATH}',
       '{ hgW:${WRITER_HOSTGROUP_ID}, hgR:${READER_HOSTGROUP_ID} }'
       );"
  check_cmd $? "$LINENO" "Failed to add query rules to ProxySQL."\
                       "\n-- Please check the ProxySQL connection parameters and status."

  proxysql_load_to_runtime_save_to_disk "SCHEDULER" $LINENO

  if [[ $MODE == "singlewrite" ]]; then
    echo -e "\nWaiting for scheduler script to process new nodes..."
    wait_for_scheduler_processing

    # Adjust weights after adding servers to proxysql.
    adjust_weights

    report_status $WRITER_HOSTGROUP_ID
    echo ""
  fi
}


# Removing Percona XtraDB Cluster configuration from proxysql
#
# Globals:
#   CLUSTER_APP_USERNAME
#   WRITER_HOSTGROUP_ID
#
# Arguments:
#
function disable_proxysql(){
  debug "$LINENO" "disable_proxysql ()"

  local all_hostgroups
  all_hostgroups="$WRITER_HOSTGROUP_ID,$READER_HOSTGROUP_ID,$WRITER_MAINT_HOSTGROUP_ID,$WRITER_CONFIG_HOSTGROUP_ID,$READER_MAINT_HOSTGROUP_ID,$READER_CONFIG_HOSTGROUP_ID"

  echo "Removing cluster application users from the ProxySQL database."
  proxysql_exec "$LINENO" \
    "DELETE FROM mysql_users
      WHERE default_hostgroup IN ($all_hostgroups)"
  check_cmd $? "$LINENO" "Failed to delete the mysql users from ProxySQL."\
                       "\n-- Please check the ProxySQL connection parameters and status."

  echo "Removing cluster nodes from the ProxySQL database."
  proxysql_exec "$LINENO" \
    "DELETE FROM mysql_servers
      WHERE hostgroup_id in ($all_hostgroups);"
  check_cmd $? "$LINENO" "Failed to delete the PXC nodes from ProxySQL."\
                       "\n-- Please check the ProxySQL connection parameters and status."

  echo "Removing query rules from the ProxySQL database if any."
  proxysql_exec "$LINENO" \
    "DELETE FROM mysql_query_rules
      WHERE destination_hostgroup in ($all_hostgroups)"
  check_cmd $? "$LINENO" "Failed to delete the query rules from ProxySQL."\
                       "\n-- Please check the ProxySQL connection parameters amd status."

  proxysql_exec "$LINENO" \
    "DELETE FROM  mysql_query_rules_fast_routing
      WHERE destination_hostgroup in ($all_hostgroups)"
  check_cmd $? "$LINENO" "Failed to delete the fast query routing rules from ProxySQL."\
                       "\n-- Please check the ProxySQL connection parameters amd status."

  proxysql_exec "$LINENO" \
    "DELETE FROM scheduler
      WHERE comment like '% hgW:${WRITER_HOSTGROUP_ID}, hgR:${READER_HOSTGROUP_ID} %'"
  check_cmd $? "$LINENO" "Failed to delete the scheduler from ProxySQL."\
                       "\n-- Please check the ProxySQL connection parameters amd status."

  proxysql_load_to_runtime_save_to_disk "SCHEDULER" $LINENO
  proxysql_load_to_runtime_save_to_disk "MYSQL USERS" $LINENO
  proxysql_load_to_runtime_save_to_disk "MYSQL SERVERS" $LINENO
  proxysql_load_to_runtime_save_to_disk "MYSQL QUERY RULES" $LINENO
}


# Tries to find a cluster node in the cluster that contains
# the given writer hostgroup/  The node must be a Primary node.
#
# Globals:
#   None
#
# Parameters:
#   Argument 1: The writer hostgroup
#
# Ouput:
#   If a node is found, then "<ip-addr> <port>" is printed to stdout.
#
# Returns:
#   0 : if a node is found (the address is printed to stdout)
#   1 : if a node could not be found
function find_cluster_node()
{
  local write_hg=$1
  local host_info
  local all_hostgroups

  all_hostgroups=$(get_all_hostgroups_from_writer_hostgroup $write_hg)
  if [[ -z $all_hostgroups ]]; then
    error "$LINENO" "Could not find a cluster (with writer_hostgroup:$write_hg) to update"
    exit 1
  fi

  host_info=$(proxysql_exec "$LINENO" \
                "SELECT DISTINCT hostname, port
                 FROM mysql_servers
                 WHERE hostgroup_id in ($all_hostgroups)")
    check_cmd $? "$LINENO" "Failed to retrieve host information from ProxySQL"\
                         "\n-- Please check the ProxySQL connection parameters and status."
  if [[ -z $host_info ]]; then
    error "$LINENO" "Could not find any nodes belonging to the cluster with writer hostgroup:$write_hg"
    exit 1
  fi

  # Check each node to see if it is Primary
  while read line; do
    [[ -z $line ]] && continue

    local ip_addr port
    local data

    ip_addr=$(echo "$line" | cut -f1)
    port=$(echo "$line" | cut -f2)

    data=$(cluster_exec "$LINENO" "$ip_addr" "$port" \
              "SHOW STATUS LIKE 'wsrep_cluster_status'")
    [[ $? -ne 0 ]] && continue;

    data=$(echo -e "$data" | cut -f2)
    if [[ $data == "Primary" ]]; then
      echo -e "${ip_addr}\t${port}"
      return 0
    fi
  done< <(printf "%s\n" "$host_info")

  # Failed to find a cluster node
  return 1
}


# Create query rules for given user. This is applicable only for singlewrite mode
# Globals:
#   WRITER_HOSTGROUP_ID
#
# Arguments:
#   Parameter 1: the username
function add_query_rule()
{
  local username=$1
  if ! is_mode_singlewrite "$WRITER_HOSTGROUP_ID"; then
      warning "$LINENO" "Ignoring query rule for $username, since this is not using" \
        "singlewrite mode (max-writers is not 1)"
  else
    proxysql_exec "$LINENO" \
      "INSERT INTO mysql_query_rules
        (username,destination_hostgroup,active,match_digest,apply)
       VALUES
        ('$username',$WRITER_HOSTGROUP_ID,1,'^SELECT.*FOR UPDATE',1),
        ('$username',$READER_HOSTGROUP_ID,1,'^SELECT ',1);"

    check_cmd $? "$LINENO" "Failed to add the read query rule to ProxySQL."\
                         "\n-- Please check the ProxySQL connection parameters and status."
    echo -e "  Added query rule for user: $username"
  fi
}


# Returns 0 (success) if max_writers is 1
# (which we consider singlewrite mode)
#
# Globals:
#   None
#
# Arguments:
#   Parameter 1: the writer hostgroup to be checked
#
# Returns:
#   0 : (success) if the galera hostgroup corresponding to
#       the writer hostgroup has max_writers == 1
#   1 : (failure) otherwsie
function is_mode_singlewrite()
{
  if [[ $MODE == 'singlewrite' ]]; then
    return 0
  else
    return 1
  fi
}


# Update the cluster membership
# New members will be added to the configuration.
# By design, members that are no longer in the cluster will not be removed.
#
# Globals:
#   WRITER_HOSTGROUP_ID
#   WRITE_NODE
#
# Parameters:
#   None
#
function update_cluster()
{
  local host_info
  local current_hosts=""
  local ip_addr port
  local cluster_host cluster_port
  local all_hostgroups
  local cluster_addresses=""
  local data
  local -i force_to_runtime=0

  # If we have a write node specified, check for read-only
  if [[ -n $WRITE_NODE ]]; then
    local ws_address ws_ip ws_port
    ws_address=$(separate_ip_port_from_address "$WRITE_NODE")
    ws_ip=$(echo "$ws_address" | cut -d' ' -f1)
    ws_port=$(echo "$ws_address" | cut -d' ' -f2)

    local is_read_only
    is_read_only=$(exec_sql "$LINENO" "$CLUSTER_USERNAME" "$CLUSTER_PASSWORD" "$ws_ip" "$ws_port" \
      "select @@global.read_only")
    if [[ $? -eq 0 && $is_read_only -eq 1 ]]; then
      writer_ws_ip=$ws_ip
      writer_ws_port=$ws_port
      writer_ws_address=$(combine_ip_port_into_address "$writer_ws_ip" "$writer_ws_port")

      if [[ $FORCE -eq 1 ]]; then
        warning "$LINENO" "--write-node is a read-only node($writer_ws_address)."
      else
        error "$LINENO" "--write-node is a read-only node($writer_ws_address). Terminating."
        exit 1
      fi
    fi
  fi
  all_hostgroups=$(get_all_hostgroups_from_writer_hostgroup $WRITER_HOSTGROUP_ID)
  if [[ -z $all_hostgroups ]]; then
    error "$LINENO" "Could not find a cluster (with writer_hostgroup:$WRITER_HOSTGROUP_ID) to update"
    exit 1
  fi

  # First, reset the list of servers if desired (this will force us to use
  # the CLUSTER_HOSTNAME to find the cluster)
  if [[ $REMOVE_ALL_SERVERS -eq 1 ]]; then
    echo "Removing all servers from ProxySQL"
    proxysql_exec "$LINENO" \
      "DELETE FROM mysql_servers
       WHERE
        hostgroup_id IN (${all_hostgroups})"
    check_cmd "$?" "$LINENO" "Failed to remove the list of servers from ProxySQL"
    current_hosts=""
  else
    # If we're not removing all of the servers
    # then try get the list of servers from ProxySQL
    # Second, get the list of hosts from the hosts given in the galera hostgroups
    host_info=$(proxysql_exec "$LINENO" \
                  "SELECT DISTINCT hostname, port
                   FROM mysql_servers
                   WHERE hostgroup_id in ($all_hostgroups)")
    check_cmd $? "$LINENO" "Failed to retrieve host information from ProxySQL"\
                         "\n-- Please check the ProxySQL connection parameters and status."
    if [[ -n $host_info ]]; then
      # Extract the hostname and port from the rows
      # Creates a string of "host:port" separated by spaces
      while read line; do
        if [[ -z $line ]]; then
          continue
        fi

        local net_address
        ip_addr=$(echo "$line" | cut -f1)
        port=$(echo "$line" | cut -f2)
        net_address=$(combine_ip_port_into_address "$ip_addr" "$port")
        current_hosts+="$net_address "
      done< <(printf "%s\n" "$host_info")

      current_hosts=${current_hosts% }
    fi
  fi

  # Third, iterate through our known list of hosts
  # till we find an active host
  # (Must also check that this is a primary partition)
  for i in $current_hosts; do
    [[ -z $i ]] && continue;

    net_address=$(separate_ip_port_from_address "$i")

    local ip_addr port
    local status=""
    ip_addr=$(echo "$net_address" | cut -d' ' -f1)
    port=$(echo "$net_address" | cut -d' ' -f2)

    # This call may fail (the node may be offline)
    status=$(cluster_exec "$LINENO" "$ip_addr" "$port" \
              "SHOW STATUS LIKE 'wsrep_cluster_status';
               SHOW STATUS LIKE 'wsrep_incoming_addresses'" \
             "--skip-column-names --silent")
    [[ $? -ne 0 || -z $status ]] && continue;

    local cluster_status
    cluster_status=$(echo -en "$status" | grep "wsrep_cluster_status" | cut -f2)
    if [[ $cluster_status == "Primary" ]]; then
      debug "$LINENO" "Using informataion from the ProxySQL node list"
      cluster_addresses=$(echo -en "$status" | grep "wsrep_incoming_addresses" | cut -f2 | tr ',' ' ')
      break
    fi
  done

  # Fourth, if we still have no cluster addresses, try the CLUSTER_HOSTNAME
  if [[ -z $cluster_addresses ]]; then
    status=$(mysql_exec "$LINENO" \
              "SHOW STATUS LIKE 'wsrep_cluster_status';
               SHOW STATUS LIKE 'wsrep_incoming_addresses'" \
               "--skip-column-names --silent")
    if [[ $? -eq 0 && -n $status ]]; then
      local cluster_status
      cluster_status=$(echo -en "$status" | grep "wsrep_cluster_status" | cut -f2)
      if [[ $cluster_status == "Primary" ]]; then
        debug "$LINENO" "Using informataion from the CLUSTER_HOSTNAME:$CLUSTER_HOSTNAME"
        cluster_addresses=$(echo -en "$status" | grep "wsrep_incoming_addresses" | cut -f2 | tr ',' ' ')
      fi
    fi
  fi


  if [[ -z $cluster_addresses && -z $current_hosts ]]; then
    warning "$LINENO" "update_cluster: both PXC and ProxySQL have no active nodes"
    return
  fi

  #
  # Given the WSREP members, compare to ProxySQL
  # If missing from ProxySQL, add to ProxySQL as a writer
  #
  debug "$LINENO" "Looking for PXC nodes not in ProxySQL"
  local new_node_list=""
  for i in ${cluster_addresses}; do
    debug "$LINENO" "Looking for '$i' in '${current_hosts}'"

    # if we have a match, then the PXC node is in ProxySQL and we can skip
    local escaped_i
    escaped_i=${i/[/\\[}
    escaped_i=${escaped_i/]/\\]}
    if [[ -n $current_hosts && " ${current_hosts} " =~ [[:space:]]${escaped_i}[[:space:]] ]]; then
      continue
    fi

    echo "Cluster node (${i}) does not exist in ProxySQL, adding to the writer hostgroup($WRITER_HOSTGROUP_ID)"
    net_address=$(separate_ip_port_from_address "$i")
    ip_addr=$(echo "$net_address" | cut -d' ' -f1)
    port=$(echo "$net_address" | cut -d' ' -f2)

    # Add to list of nodes to add to proxysql
    net_address=$(combine_ip_port_into_address "$ip_addr" "$port")
    new_node_list+="$net_address "
  done

  if [[ -n $new_node_list ]]; then
    add_servers_to_proxysql "$new_node_list"
  else
    echo "No new nodes detected."
  fi


  # Now that the nodes are up-to-date, do we have to check
  # for a write-node?  If we don't have a write-node, leave
  # things the way they are.
  if [[ -n $WRITE_NODE ]]; then
    # Check if max_writers is 1, we only need to check this
    # if we are in singlewrite mode
    if ! is_mode_singlewrite "$WRITER_HOSTGROUP_ID"; then
      warning "$LINENO" "Ignoring --write-node, since this is not using" \
                        "singlewrite mode (max-writers is not 1)"
    else
      local writer_ws_address
      local writer_ws_ip
      local writer_ws_port

      writer_ws_address=$(separate_ip_port_from_address "$WRITE_NODE")
      writer_ws_ip=$(echo "$writer_ws_address" | cut -d' ' -f1)
      writer_ws_port=$(echo "$writer_ws_address" | cut -d' ' -f2)
      writer_ws_address=$(combine_ip_port_into_address "$writer_ws_ip" "$writer_ws_port")

      # see if our node exists in mysql_servers
      data=$(proxysql_exec "$LINENO" \
              "SELECT COUNT(*)
                FROM mysql_servers
                WHERE
                  hostname = '$writer_ws_ip' AND
                  port = $writer_ws_port AND
                  hostgroup_id in ($all_hostgroups)")
      check_cmd $? "$LINENO" "Failed to search for the write-node" \
                           "\n-- Please check the ProxySQL connection parameters and status."

      # If it exists, then we need to update the table, otherwise
      # ignore
      if [[ -z $data || $data -eq 0 ]]; then
        warning "$LINENO" "Could not find write-node in list of cluster nodes: $writer_ws_address"
      else
        # The server exists

        # First, remove all the entries
        proxysql_exec "$LINENO" \
            "DELETE FROM mysql_servers
              WHERE
                hostname = '$writer_ws_ip' AND
                port = $writer_ws_port AND
                hostgroup_id in ($all_hostgroups)"
        check_cmd $? "$LINENO" "Failed to delete other write node rows" \
                             "\n-- Please check the ProxySQL connection parameters and status."

        # Clear any other previous write-node
        proxysql_exec "$LINENO" \
          "UPDATE mysql_servers
            SET weight = 1000
            WHERE
              hostgroup_id IN ($all_hostgroups) AND
              weight = 1000000"
        check_cmd $? "$LINENO" "Failed to update the write-node" \
                             "\n-- Please check the ProxySQL connection parameters and status."

        # readd as a writer node
        add_servers_to_proxysql $writer_ws_address
        force_to_runtime=1

        # Update the write_node to have a higher priority
        proxysql_exec "$LINENO" \
          "UPDATE mysql_servers
            SET weight = 1000000
            WHERE
              hostname = '$writer_ws_ip' AND
              port = $writer_ws_port AND
              hostgroup_id in ($WRITER_HOSTGROUP_ID, $WRITER_CONFIG_HOSTGROUP_ID);"
        check_cmd $? "$LINENO" "Failed to update the writer-node" \
                             "\n-- Please check the ProxySQL connection parameters and status."
      fi
    fi
  fi

  # Check if --update-write-weight has been specified
  if [[ -n $UPDATE_WRITE_WEIGHT ]]; then
    update_weight "WRITE" $UPDATE_WRITE_WEIGHT
    force_to_runtime=1
  fi

  # Check if --update-read-weight has been specified
  if [[ -n $UPDATE_READ_WEIGHT ]]; then
    update_weight "READ" $UPDATE_READ_WEIGHT
    force_to_runtime=1
  fi

  if [[ -n $new_node_list || $force_to_runtime -eq 1 ]]; then
    proxysql_load_to_runtime_save_to_disk "MYSQL SERVERS" $LINENO
    echo "Waiting for scheduler script to process the nodes..."
    wait_for_scheduler_processing
  fi

  # Adjust weights after updating servers to proxysql.
  adjust_weights

  echo -e "\nCluster node info"
  proxysql_exec "$LINENO" \
    "SELECT
        CASE
            WHEN hostgroup_id IN ($WRITER_HOSTGROUP_ID)
              THEN 'writer'
            WHEN hostgroup_id IN ($READER_HOSTGROUP_ID)
              THEN 'reader'
            WHEN hostgroup_id IN ($WRITER_MAINT_HOSTGROUP_ID)
              THEN 'writer-maint'
            WHEN hostgroup_id IN ($WRITER_CONFIG_HOSTGROUP_ID)
              THEN 'writer-config'
            WHEN hostgroup_id IN ($READER_MAINT_HOSTGROUP_ID)
              THEN 'reader-maint'
            WHEN hostgroup_id IN ($READER_CONFIG_HOSTGROUP_ID)
              THEN 'reader-config'
            ELSE
              'unknown'
        END hostgroup,
        hostgroup_id AS hg_id,
        hostname,
        port,
        status,
        weight
     FROM runtime_mysql_servers
     WHERE hostgroup_id in ($all_hostgroups)
     ORDER BY hostgroup_id,weight,hostname,port" "-t"
  echo ""
}


# Checks if the current configuration has been enabled and is active
#
# Globals:
#   None
#
# Arguments:
#   Parameter 1 : the writer hostgroup
#
function check_if_enabled()
{
  local write_hg=$1
  local data

  data=$(proxysql_exec "$LINENO" \
          "SELECT active
           FROM runtime_scheduler
           WHERE comment like '% hgW:${WRITER_HOSTGROUP_ID}, hgR:${READER_HOSTGROUP_ID} %'")
  check_cmd "$?" "$LINENO" "Could not get the galera hostgroup information from ProxySQL"

  if [[ -z $data ]]; then
    # No data, there is no entry in mysql_galera_hostgroups
    return 1
  else
    if [[ $data -eq 0 ]]; then
      # The entry exists, but is not active
      return 2
    fi
  fi

  # Entry exists and is active
  return 0
}


# Dumps a status report on the current configuration
#
# Globals:
#   WRITER_HOSTGROUP_ID
#   WRITER_HOSTGROUP_COMMAND_LINE
#
# Arguments:
#   Parameter 1 : the writer hostgroup id
#
function report_status()
{
  local write_hg=$1
  local all_hostgroups=""
  local data

  all_hostgroups=$(get_all_hostgroups_from_writer_hostgroup $write_hg)
  check_cmd "$?" "$LINENO" "Cannot retrieve hostgroup information from ProxySQL" \
                         "\n-- Please check the ProxySQL configuration and status."

  echo -e "Proxysql status (mysql_servers rows) for this configuration"
  proxysql_exec "$LINENO" \
    "SELECT
        CASE
          WHEN hostgroup_id IN ($WRITER_HOSTGROUP_ID)
            THEN 'writer'
          WHEN hostgroup_id IN ($READER_HOSTGROUP_ID)
            THEN 'reader'
          WHEN hostgroup_id IN ($WRITER_MAINT_HOSTGROUP_ID)
            THEN 'writer-maint'
          WHEN hostgroup_id IN ($WRITER_CONFIG_HOSTGROUP_ID)
            THEN 'writer-config'
          WHEN hostgroup_id IN ($READER_MAINT_HOSTGROUP_ID)
            THEN 'reader-maint'
          WHEN hostgroup_id IN ($READER_CONFIG_HOSTGROUP_ID)
            THEN 'reader-config'
          ELSE
            'unknown'
        END hostgroup,
        hostgroup_id as hg_id,
        hostname,
        port,
        status,
        weight,
        max_connections as max_conn,
        use_ssl,
        gtid_port
      FROM runtime_mysql_servers
      WHERE hostgroup_id IN ($all_hostgroups)" "-t"
  echo ""
}


# Parses the script arguments and sets the global variables
#
# Arguments:
#   The arguments to the script.
#
function parse_args() {
  local go_out=""

  local argument_list=(
    "config-file:"
    "write-node:"
    "enable"
    "disable"
    "update-cluster"
    "is-enabled"
    "adduser"
    "auto-assign-weights"
    "update-read-weight:"
    "update-write-weight:"
    "syncusers"
    "sync-multi-cluster-users"
    "update-mysql-version"
    "add-query-rule"
    "status"
    "remove-all-servers"
    "disable-updates"
    "force"
    "use-stdin-for-credentials"
    "server:"
    "version"
    "trace"
    "debug"
    "help"
  )

  # TODO: kennt, what happens if we don't have a functional getopt()?
  # Check if we have a functional getopt(1)
  if ! getopt --test; then
    go_out="$(getopt \
                --options=edv \
                --longoptions "$(printf "%s," "${argument_list[@]}")" \
                --name="$(basename "$0")" -- "$@")"
    check_cmd $? "$LINENO" "Script error: getopt() failed with arguments: $*"
    eval set -- "$go_out"
  fi
  if [[ $go_out == " --" ]];then
    usage
    exit 1
  fi

  #
  # We iterate through the command-line options twice
  # (1) to handle options that don't need permissions (such as --help)
  # (2) to handle options that need to be done before other
  #     options, such as loading the config file
  #

  while [[ $# -gt 0 ]];
  do
    arg="$1"
    case "$arg" in
      --) shift; break;;
      --config-file)
        CONFIG_FILE="$2"
        shift 2
        ;;
      --help)
        usage
        exit 0
        ;;
      -v | --version)
        version
        exit 0
        ;;
      --debug)
        DEBUG=1
        shift
        ;;
      *)
        shift
        ;;
    esac
  done

  # The config file is required!
  if [[ -z $CONFIG_FILE ]]; then
    error "" "The --config-file option is required but is missing from the command."
    exit 2
  fi

  # the config file must be an absolute path
  CONFIG_FILE=$(cd "$(dirname "${CONFIG_FILE}")" && pwd)/$(basename "${CONFIG_FILE}")
  readonly CONFIG_FILE

  #
  # Load the config file before reading in the command-line options
  #
  if [[ ! -r "$CONFIG_FILE" ]]; then
    error "" "Could not locate or read from the configuration file: $CONFIG_FILE"
    exit 2
  fi

  CONFIG_PATH=$(dirname "${CONFIG_FILE}")
  CONFIG_FILENAME=$(basename "${CONFIG_FILE}")
  if [[ -z $CONFIG_PATH || -z $CONFIG_FILENAME ]]; then
    error "" "Could not extract the path and filename from: ${CONFIG_FILE}"
    exit 2
  fi

  check_permission -r "$LINENO" "$CONFIG_FILE" "scheduler-admin configuration file"
  debug "$LINENO" "Loading config from: $CONFIG_FILE"

  #
  # Check that we can find my_print_defaults
  #
  check_binary "my_print_defaults"
  if [[ $? -ne 0 ]]; then
    error "$LINENO" "my_print_defaults was not found, please install the mysql client package."
    exit 1
  fi

  MY_PRINT_DEFAULTS=$(command -v my_print_defaults)

  # Read in the information from the config file
  PROXYSQL_HOSTNAME=$(read_from_config_file "$LINENO" "$CONFIG_FILE" "proxysql" "host" "" 1)
  [[ $? -ne 0 ]] && exit 1

  PROXYSQL_PORT=$(read_from_config_file "$LINENO" "$CONFIG_FILE" "proxysql" "port" "" 1)
  [[ $? -ne 0 ]] && exit 1

  PROXYSQL_USERNAME=$(read_from_config_file "$LINENO" "$CONFIG_FILE" "proxysql" "user" "" 1)
  [[ $? -ne 0 ]] && exit 1

  PROXYSQL_PASSWORD=$(read_from_config_file "$LINENO" "$CONFIG_FILE" "proxysql" "password" "" 1)
  [[ $? -ne 0 ]] && exit 1


  CLUSTER_HOSTNAME=$(read_from_config_file "$LINENO" "$CONFIG_FILE" "setup" "clusterHost" "" 1)
  [[ $? -ne 0 ]] && exit 1

  CLUSTER_PORT=$(read_from_config_file "$LINENO" "$CONFIG_FILE" "setup" "clusterPort" "" 1)
  [[ $? -ne 0 ]] && exit 1

  CLUSTER_USERNAME=$(read_from_config_file "$LINENO" "$CONFIG_FILE" "setup" "clusterUser" "" 1)
  [[ $? -ne 0 ]] && exit 1

  CLUSTER_PASSWORD=$(read_from_config_file "$LINENO" "$CONFIG_FILE" "setup" "clusterUserPassword" "" 1)
  [[ $? -ne 0 ]] && exit 1


  MONITOR_USERNAME=$(read_from_config_file "$LINENO" "$CONFIG_FILE" "setup" "monitorUser" "" 1)
  [[ $? -ne 0 ]] && exit 1

  MONITOR_PASSWORD=$(read_from_config_file "$LINENO" "$CONFIG_FILE" "setup" "monitorUserPassword" "" 1)
  [[ $? -ne 0 ]] && exit 1

  # Get the HG information
  WRITER_HOSTGROUP_ID=$(read_from_config_file "$LINENO" "$CONFIG_FILE" "pxccluster" "hgW" "-1" 1)
  [[ $? -ne 0 ]] && exit 1
  READER_HOSTGROUP_ID=$(read_from_config_file "$LINENO" "$CONFIG_FILE" "pxccluster" "hgR" "-1" 1)
  [[ $? -ne 0 ]] && exit 1

  # Determine configuration and maintenance ranges
  local config_range maint_range
  config_range=$(read_from_config_file "$LINENO" "$CONFIG_FILE" "pxccluster" "configHgRange" "-1" 1)
  [[ $? -ne 0 ]] && exit 1
  WRITER_CONFIG_HOSTGROUP_ID=$(($config_range + $WRITER_HOSTGROUP_ID))
  READER_CONFIG_HOSTGROUP_ID=$(($config_range + $READER_HOSTGROUP_ID))

  maint_range=$(read_from_config_file "$LINENO" "$CONFIG_FILE" "pxccluster" "maintenanceHgRange" "-1" 1)
  [[ $? -ne 0 ]] && exit 1
  WRITER_MAINT_HOSTGROUP_ID=$((maint_range + $WRITER_HOSTGROUP_ID))
  READER_MAINT_HOSTGROUP_ID=$((maint_range + $READER_HOSTGROUP_ID))

  # Check for LockFilePath
  LOCK_FILE_PATH=$(read_from_config_file "$LINENO" "$CONFIG_FILE" "proxysql" "lockfilepath" "" 1)
  [[ $? -ne 0 ]] && exit 1

  if [[ ! -d $(cd "$(dirname ${LOCK_FILE_PATH})" && pwd)/$(basename ${LOCK_FILE_PATH}) ]]; then
    error "" "lockfilepath is set to an invalid location ${LOCK_FILE_PATH}. Make sure the directory exists."
    exit 1
  fi

  # Hostgroup value checks
  # the config range must be less than the maint range
  if [[ $config_range -ge $maint_range ]]; then
    error "" "The configuration range($config_range) must be less than the maintenance range($maint_range)"
    exit 1
  fi
  local diff_range
  diff_range=$(( $maint_range - $config_range ))
  # The hostgroup must be within the range, otherwise there may
  # be an overlap and conflict
  if [[ $WRITER_HOSTGROUP_ID -ge $diff_range || $READER_HOSTGROUP_ID -ge $diff_range ]]; then
    error "" "The config range is too small and the values may overlap" \
           "\nwith the maint range.  For example:" \
           "\nThe config range($config_range) + writer hostgroup id($WRITER_HOSTGROUP_ID) > maint range($maint_range)."
    exit 1
  fi

  local writers_are_readers
  writers_are_readers=$(read_from_config_file "$LINENO" "$CONFIG_FILE" "pxccluster" "writerIsAlsoReader" 1 0)
  if [[ ! $writers_are_readers =~ ^(0|1) ]]; then
    error "$LINENO" "Invalid --writers-are-readers option: '$writers_are_readers'"
    echo "Please use one of these values: '0', '1'"
    exit 1
  fi

  # Configure the 'mode' option
  local singlePrimary
  local maxNumWriters
  singlePrimary=$(read_from_config_file "$LINENO" "$CONFIG_FILE" "pxccluster" "singlePrimary" "false" 0)
  maxNumWriters=$(read_from_config_file "$LINENO" "$CONFIG_FILE" "pxccluster" "maxNumWriters" "" 0)

  if [[ $singlePrimary == "true" ]]; then
    if [[ -z $maxNumWriters ]]; then
       # Set maxNumWriters=1 when singlePrimary = true
       maxNumWriters=1
    elif [[ -n $maxNumWriters && $maxNumWriters -ne 1 ]]; then
      # if singlePrimary is true, the only allowed value for maxNumWriters is 1
      error "$LINENO" "singlePrimary is true but maxNumWriters is not 1 (value is $maxNumWriters}"
      exit 1
    fi
    MODE="singlewrite"
  else
    if [[ -z $maxNumWriters ]]; then
      error "$LINENO" "singlePrimary is false but maxNumWriters is not set"
      exit 1
    elif [[ $maxNumWriters -le 0 ]]; then
      error "$LINENO" "maxNumwWriters($maxNumWriters) must be greater than 0"
      exit 1
    elif [[ $maxNumWriters -le 100 ]]; then
      if [[ $writers_are_readers -eq 0 ]];then
        error "$LINENO" "Cannot have WriterIsAlsoReader=0 when maxNumWriters > 1"
        exit 1
      # Use singlewrite mode when $maxNumWriters=1
      elif [[ $maxNumWriters -eq 1 ]]; then
        debug "$LINENO" "Enabling singlewrite mode since singlePrimary=false and maxNumWriters=1"
        MODE="singlewrite"
      else
        MODE="loadbal"
      fi
    else
      MODE="loadbal"
    fi
  fi

  if [[ $writers_are_readers -eq 1 ]]; then
    WRITERS_ARE_READERS_OPTION=1
  else
    WRITERS_ARE_READERS_OPTION=0
  fi

  USE_SSL_OPTION=$(read_from_config_file "$LINENO" "$CONFIG_FILE" "setup" "useSSL" "0" 0)

  MAX_CONNECTIONS=$(read_from_config_file "$LINENO" "$CONFIG_FILE" "setup" "maxConnections" "1000" 0)

  # Verify that we have an integer >= 0
  if [[ -n $MAX_CONNECTIONS ]]; then
    if ! is_integer "$MAX_CONNECTIONS"
    then
      error "" "option 'maxConnections' requires a number : $MAX_CONNECTIONS"
      exit 1
    fi

    if [[ $MAX_CONNECTIONS -lt 0 ]]; then
      error "" "option 'maxConnections' requires a number >=0 : $MAX_CONNECTIONS"
      exit 1
    fi
  else
      error "" "option 'maxConnections' requires an argument"
      exit 1
  fi

  NODE_CHECK_INTERVAL=$(read_from_config_file "$LINENO" "$CONFIG_FILE" "setup" "nodeCheckInterval" "2000" 0)
  if ! is_integer "$NODE_CHECK_INTERVAL"; then
    error "" "option 'nodeCheckInterval' requires a number : $NODE_CHECK_INTERVAL"
    exit 1
  fi

  # Reset the command line for the next invocation
  eval set -- "$go_out"

  #
  # Iterate through the comamnd-line options
  #
  while [[ $# -gt 0 ]];
  do
    arg="$1"
    case "$arg" in
      -- ) shift; break;;
      --config-file )
        # Do no processing of config-file here, it is processed
        # before this loop (see above)
        shift 2
        ;;
      --write-node )
        WRITE_NODE="$2"
        shift 2
        ;;
      -e | --enable )
        shift
        ENABLE=1
        NEEDS_WRITER_HOSTGROUP=1
        NEEDS_READER_HOSTGROUP=1
        ;;
      --auto-assign-weights )
        shift
        AUTO_ASSIGN_WEIGHTS=1
        ;;
      --update-read-weight )
        UPDATE_READ_WEIGHT="$2"
        shift 2
        ;;
      --update-write-weight )
        UPDATE_WRITE_WEIGHT="$2"
        shift 2
        ;;
      --adduser )
        shift
        ADDUSER=1
        NEEDS_WRITER_HOSTGROUP=1
        ;;
      --syncusers )
        shift
        SYNCUSERS=1
        NEEDS_WRITER_HOSTGROUP=1
        ;;
      --sync-multi-cluster-users )
        shift
        SYNCMULTICLUSTERUSERS=1
        NEEDS_WRITER_HOSTGROUP=1
        ;;
      --add-query-rule )
        shift
        ADD_QUERY_RULE=1
        NEEDS_WRITER_HOSTGROUP=1
        ;;
      -d | --disable )
        shift
        DISABLE=1
        NEEDS_WRITER_HOSTGROUP=1
        NEEDS_READER_HOSTGROUP=1
        ;;
      --update-cluster )
        shift
        UPDATE_CLUSTER=1
        NEEDS_WRITER_HOSTGROUP=1
        ;;
      --update-mysql-version )
        shift
        UPDATE_MYSQL_VERSION=1
        ;;
      --is-enabled )
        shift
        CHECK_IF_ENABLED=1
        NEEDS_WRITER_HOSTGROUP=1
        ;;
      --status )
        shift
        REPORT_STATUS=1
        ;;
      --force )
        shift
        FORCE=1
        ;;
      --remove-all-servers )
        REMOVE_ALL_SERVERS=1
        shift
        ;;
      --disable-updates )
        DISABLE_UPDATES=1
        shift
        ;;
      --use-stdin-for-credentials )
        USE_STDIN_FOR_CREDENTIALS=1
        shift
        ;;
      --server )
        SINGLE_SERVER=$2
        shift 2
        ;;
      -v | --version )
        # Detection of this setting is done before this
        shift
        ;;
      --trace )
        shift
        ;;
      --debug )
        # Detection of this setting is done before this
        shift
        ;;
      --help )
        # Detection of this setting is done before this
        shift
        ;;
    esac
  done

  # These are optional parameters
  CLUSTER_APP_USERNAME=$(read_from_config_file "$LINENO" "$CONFIG_FILE" "setup" "clusterAppUser" "" 0)
  CLUSTER_APP_PASSWORD=$(read_from_config_file "$LINENO" "$CONFIG_FILE" "setup" "clusterAppUserPassword" "" 0)

  # --enable only checks
  if [[ $ENABLE -eq 1 ]]; then
    echo -e "Configuring using mode: $MODE\n"

    if [[ -z $CLUSTER_APP_USERNAME || -z $CLUSTER_APP_PASSWORD ]]; then
      echo -e "The ClusterApp User or Password was unspecified and will not be configured.\n"
      WITH_CLUSTER_APP_USER=0
      CLUSTER_APP_USERNAME=""
      CLUSTER_APP_PASSWORD=""
    fi
  fi

  # --update-write-weight and --auto-assign-weights are mutually exclusive
  if [[ -n $UPDATE_WRITE_WEIGHT && $AUTO_ASSIGN_WEIGHTS -eq 1 ]]; then
      error "" "--auto-assign-weights and --update-write-weight options are mutually exclusive."
      exit 1
  fi

  # --write-node and --auto-assign-weights are mutually exclusive
  if [[ -n $WRITE_NODE && $AUTO_ASSIGN_WEIGHTS -eq 1 ]]; then
      error "" "--write-node and --auto-assign-weights options are mutually exclusive."
      exit 1
  fi

  # WRITE_NODE validity check (check to see if the port is included)
  if [[ -n $WRITE_NODE ]]; then
    if [[ $WRITE_NODE =~ , ]]; then
      # If --write-node contains a comma, then it was from a previous
      # version of the script
      error "$LINENO" "The --write-node option expects to receive the address" \
                    "\n-- of a single node, therefore commas ',' are not allowed."
      exit 1
    fi
    if [[ -n $UPDATE_WRITE_WEIGHT ]]; then
        error "" "--write-node and --update-write-weight options are mutually exclusive."
        exit 1
    fi
    if [ $AUTO_ASSIGN_WEIGHTS -eq 1 ]; then
        error "" "--write-node and --auto-assign-weights options are mutually exclusive."
        exit 1
    fi

    local ws_address
    local ws_port

    ws_address=$(separate_ip_port_from_address "$WRITE_NODE")
    ws_port=$(echo "$ws_address" | cut -d' ' -f2)

    # Check that we have a port and that it only contains digits
    if [[ -z $ws_port || ! $ws_port =~ ^[[:digit:]]*$ ]]; then
      error "$LINENO" "--write-node : expected 'address:port' found '$WRITE_NODE'"
      exit 1
    fi
  fi
  readonly WRITE_NODE

  # SINGLE_SERVER should only be used with syncusers
  if [[ -n $SINGLE_SERVER && $SYNCUSERS -ne 1 && $SYNCMULTICLUSTERUSERS -ne 1 ]]; then
    error "$LINENO" "The --server option can only be used with --syncusers" \
                  "\nand --sync-multi-cluster-users."
    exit 1
  fi

  # SINGLE_SERVER validity check (check to see if the port is included)
  if [[ -n $SINGLE_SERVER ]]; then
    if [[ $SINGLE_SERVER =~ , ]]; then
      error "$LINENO" "The --server option expects to receive the address" \
                    "\n-- of a single node, therefore commas ',' are not allowed."
      exit 1
    fi
    local ws_address
    local ws_port

    ws_address=$(separate_ip_port_from_address "$SINGLE_SERVER")
    ws_port=$(echo "$ws_address" | cut -d' ' -f2)

    # Check that we have a port and that it only contains digits
    if [[ -z $ws_port || ! $ws_port =~ ^[[:digit:]]*$ ]]; then
      error "$LINENO" "--server : expected 'address:port' found '$SINGLE_SERVER'"
      exit 1
    fi
  fi
  readonly SINGLE_SERVER



  if [[ ! $MODE =~ ^(loadbal|singlewrite)$ ]]; then
    error "" "Invalid --mode passed: '$MODE'"
    echo "Please choose one of these modes: loadbal, singlewrite"
    exit 1
  fi
  readonly NODE_CHECK_INTERVAL
  readonly MODE

  if [[ $USE_SSL_OPTION != "0" && $USE_SSL_OPTION != "1" ]]; then
    error "" "Invalid useSSL option: '$USE_SSL_OPTION'"
    echo "Please use one of these values: '0','1'"
    exit 1
  fi

  if [[ $USE_SSL_OPTION -eq 1 ]]; then

    SSL_CERTIFICATE_PATH=$(read_from_config_file "$LINENO" "$CONFIG_FILE" "pxccluster" "sslCertificatePath" "" 1)
    # Check if the sslCertificatePath exists.
    if [ -z ${SSL_CERTIFICATE_PATH} ]; then
      error "$LINENO" "useSSL is set but sslCertificatePath is set to an empty path."
      exit 1
    fi

    if [[ ! -d ${SSL_CERTIFICATE_PATH} ]]; then
      error "$LINENO" "useSSL is set but sslCertificatePath is set to an invalid path ${SSL_CERTIFICATE_PATH}"
      exit 1
    fi

    local ssl_ca=$(read_from_config_file "$LINENO" "$CONFIG_FILE" "pxccluster" "sslCa" "" 1)
    local ssl_key=$(read_from_config_file "$LINENO" "$CONFIG_FILE" "pxccluster" "sslKey" "" 1)
    local ssl_client=$(read_from_config_file "$LINENO" "$CONFIG_FILE" "pxccluster" "sslClient" "" 1)

    if [ ! -f ${SSL_CERTIFICATE_PATH}/$ssl_ca ]; then
      error "$LINENO" "Could not find SSL CA in ${SSL_CERTIFICATE_PATH}/$ssl_ca"
      exit 1
    fi

    if [ ! -f ${SSL_CERTIFICATE_PATH}/$ssl_key ]; then
      error "$LINENO" "Could not find SSL Key in ${SSL_CERTIFICATE_PATH}/$ssl_key"
      exit 1
    fi

    if [ ! -f ${SSL_CERTIFICATE_PATH}/$ssl_client ]; then
      error "$LINENO" "Could not find SSL Client cert in ${SSL_CERTIFICATE_PATH}/$ssl_client"
      exit 1
    fi
  fi
  readonly USE_SSL_OPTION
  readonly SSL_CERTIFICATE_PATH


  # Check the options gathered from the command line
  if [[ -z "$PROXYSQL_USERNAME" ]];then
    error "$LINENO" "The ProxySQL username (--proxysql-username) is required!"
    exit 1
  fi

  if [[ -z "$PROXYSQL_HOSTNAME" ]]; then
    PROXYSQL_HOSTNAME="127.0.0.1"
  fi

  if [[ -z "$PROXYSQL_PORT" ]]; then
    PROXYSQL_PORT="6032"
  fi

  if [[ -z "$CLUSTER_USERNAME" ]];then
    error "" "The Percona XtraDB Cluster username (--cluster-username) is required!"
    exit 1
  fi

  if [[ -z "$CLUSTER_HOSTNAME" ]]; then
    CLUSTER_HOSTNAME="localhost"
  fi

  if [[ -z "$CLUSTER_PORT" ]]; then
    CLUSTER_PORT="3306"
  fi

  if [[ -n $UPDATE_READ_WEIGHT ]]; then
    validate_update_weights "READ" $UPDATE_READ_WEIGHT
  fi

  if [[ -n $UPDATE_WRITE_WEIGHT ]]; then
    validate_update_weights "WRITE" $UPDATE_WRITE_WEIGHT
  fi

  #
  # By this point, all of the connection parameters
  # (proxysql and PXC cluster) should have been set.
  #
  readonly PROXYSQL_USERNAME
  readonly PROXYSQL_PASSWORD
  readonly PROXYSQL_PORT
  readonly PROXYSQL_HOSTNAME

  readonly CLUSTER_USERNAME
  readonly CLUSTER_PASSWORD

  readonly ENABLE
  readonly DISABLE
  readonly ADDUSER
  readonly SYNCUSERS
  readonly SYNCMULTICLUSTERUSERS
  readonly ADD_QUERY_RULE
  readonly QUICK_DEMO
  readonly UPDATE_CLUSTER
  readonly UPDATE_MYSQL_VERSION
  readonly CHECK_IF_ENABLED
  readonly REPORT_STATUS
  readonly REMOVE_ALL_SERVERS

  readonly WITH_CHECK_MONITOR_USER
  readonly WITH_CLUSTER_APP_USER

  readonly MAX_CONNECTIONS

  readonly LOCK_FILE_PATH
  readonly AUTO_ASSIGN_WEIGHTS
  readonly UPDATE_READ_WEIGHT
  readonly UPDATE_WRITE_WEIGHT
}

# Validate update-* options
function validate_update_weights() {

  local option="$1"
  local new_weight_info=$2
  local update_operation arg_count

  if [[ $option == "READ" ]]; then
    update_operation="--update-read-weight"
  else
    update_operation="--update-write-weight"
  fi

  # The update option requires exact 2 arguments
  arg_count=$(echo $new_weight_info | tr ',' '\n' | wc -l)
  if [[ $arg_count -lt 2 || $arg_count -gt 2 ]]; then
    error "" "$update_operation option requires exactly two arguments in format \"<IP_ADDR:PORT>,NEW_WEIGHT\". Found $new_weight_info"
    exit 1
  fi

  # Extract node details and validate inputs
  local address=$(echo $new_weight_info | cut -d, -f1)
  local weight=$(echo $new_weight_info | cut -d, -f2)

  local node_address=$(separate_ip_port_from_address "$address")
  local node_ip=$(echo "$node_address" | cut -d' ' -f1)
  local node_port=$(echo "$node_address" | cut -d' ' -f2)

  # Check that we have a port and that it only contains digits
  if [[ -z $node_port || ! $node_port =~ ^[[:digit:]]*$ ]]; then
    error "$LINENO" "$update_operation : expected address in format 'address:port' but found '$new_weight_info'"
    exit 1
  fi

  if ! is_integer "$node_port"; then
    error "" "Port in $update_operation requires a number : $node_port"
    exit 1
  fi

  if ! is_integer "$weight"; then
    error "" "Weight in $update_operation requires a number : $weight"
    exit 1
  fi

  if [[ $weight -le 0 || $weight -ge 10000000 ]]; then
    error "" "Weight in $update_operation is out of range. Please set it to a value in range [0,10000000]."
    exit 1
  fi
}

# Main function
#
# Globals:
#   ENABLE
#   DISABLE
#   ADDUSER
#   SYNCUSERS
#   SYNCMULTICLUSTERUSERS
#   QUICK_DEMO
#   UPDATE_CLUSTER
#   UPDATE_MYSQL_VERSION
#   CHECK_IF_ENABLED
#   REPORT_STATUS
#   MODE
#   CLUSTER_APP_USERNAME
#   PROXYSQL_HOSTNAME, PROXYSQL_CLIENT_PORT
#   WRITER_HOSTGROUP_ID
#
# Arguments:
#   None
#
function main() {

  if [[ $UPDATE_CLUSTER -eq 1 ]]; then

    check_if_enabled $WRITER_HOSTGROUP_ID
    errcode=$?
    if [[ $ENABLE -eq 1 ]]; then
      if [[ $errcode -eq 1 ]]; then
        # hostgroup is not in use, so we need to enable it
        echo -e "ProxySQL has not been configured, enabling"
        enable_proxysql
        echo -e "\nProxySQL configuration completed!\n"
        exit 0
      elif [[ $errcode -eq 2 ]]; then
        echo -e "ProxySQL has been enabled, but scheduler is inactive, skipping"
        exit 1
      fi
    elif [[ $errcode -eq 1 ]]; then
      error "$LINENO" "The cluster (with writer hostgroup: $WRITER_HOSTGROUP_ID) has not been configured in ProxySQL. Use along with --enable option to enable the configuration."
      exit 1
    fi

    update_cluster
    echo -e "\nCluster membership updated in the ProxySQL database!"

  elif [[ $ENABLE -eq 1 ]]; then

    if [[ $QUICK_DEMO -eq 0 ]]; then
      echo -e "\nThis script will assist with configuring ProxySQL for use with"
      echo -e "Percona XtraDB Cluster (currently only PXC in combination"
      echo -e "with ProxySQL is supported)"
    fi
    echo -e "\nProxySQL read/write configuration mode is ${BD}$MODE${NBD}\n"

    enable_proxysql

    echo -e "\nProxySQL configuration completed!\n"

    PROXYSQL_CLIENT_PORT=$(proxysql_exec "$LINENO" "SELECT * FROM runtime_global_variables WHERE variable_name='mysql-interfaces'" |
                            awk '{print $2}' |
                            grep -o -P '(?<=:).*' |
                            cut -d';' -f1 )
    echo -e "ProxySQL has been successfully configured to use with Percona XtraDB Cluster\n"
    if [[ $WITH_CLUSTER_APP_USER -eq 1 ]]; then
      echo -e "You can use the following login credentials to connect your application through ProxySQL\n"
      if [[ $QUICK_DEMO -eq 1 ]]; then
        echo -e "${BD}mysql --user=$CLUSTER_APP_USERNAME --host=$PROXYSQL_HOSTNAME --port=$PROXYSQL_CLIENT_PORT --protocol=tcp ${NBD}\n"
      else
        echo -e "${BD}mysql --user=$CLUSTER_APP_USERNAME -p --host=$PROXYSQL_HOSTNAME --port=$PROXYSQL_CLIENT_PORT --protocol=tcp ${NBD}\n"
      fi
    fi

    if [[ $SYNCUSERS -eq 1 ]] || [[ $SYNCMULTICLUSTERUSERS -eq 1 ]]; then
      syncusers
      echo -e "\nSynced PXC users to the ProxySQL database!"
    fi

    # Check if the incoming connection limit is smaller
    # than the backend connection limit.
    local incoming_max
    incoming_max=$(proxysql_exec "$LINENO" "select variable_value from global_variables where variable_name like 'mysql-max_connections'")
    if [[ -n $incoming_max && $incoming_max -lt $MAX_CONNECTIONS ]]; then
      warning "$LINENO" "The value of the '--max-connections' option($MAX_CONNECTIONS) exceeds"
      echo "-- the limit on incoming client connections($incoming_max). The limit on incoming"
      echo "-- connections may be increased by changing the value of 'mysql-max_connections'"
      echo "-- in the ProxySQL global_variables table."
      echo ""
    fi

  elif [[ $DISABLE -eq 1 ]]; then

    disable_proxysql
    echo "ProxySQL configuration removed!"

  elif [[ $ADDUSER -eq 1 ]]; then

    adduser
    echo -e "\nAdded PXC application user to the ProxySQL database!"

  elif [[ $SYNCUSERS -eq 1 ]] || [[ $SYNCMULTICLUSTERUSERS -eq 1 ]]; then

    syncusers
    echo -e "\nSynced PXC users to the ProxySQL database!"

  elif [[ $CHECK_IF_ENABLED -eq 1 ]]; then
    local errcode

    check_if_enabled $WRITER_HOSTGROUP_ID
    errcode=$?
    if [[ $errcode -eq 1 ]]; then
      error "$LINENO" "The current configuration has not been enabled"
      exit 1
    elif [[ $errcode -eq 2 ]]; then
      error "$LINENO" "The current configuration has been enabled, but is not active"
      exit 2
    else
      echo -e "The current configuration has been enabled and is active"
      exit 0
    fi

  elif [[ $REPORT_STATUS -eq 1 ]]; then

    report_status $WRITER_HOSTGROUP_ID

  elif [[ $UPDATE_MYSQL_VERSION -eq 1 ]]; then

    update_mysql_version

  elif [[ $AUTO_ASSIGN_WEIGHTS -eq 1 ]]; then

    error "" "--auto-assign-weights requires either --update-cluster or --enable."
    exit 1

  elif [[ -n $UPDATE_READ_WEIGHT ]]; then

    error "" "--update-read-weight requires --update-cluster."
    exit 1

  elif [[ -n $UPDATE_WRITE_WEIGHT ]]; then

    error "" "--update-write-weight requires --update-cluster."
    exit 1

  else

    error "" "Must specify an operation: --enable, --disable, --update-cluster, --is-enabled, --adduser, --syncusers, --sync-multi-cluster-users, or --update-mysql-version"
    exit 1

  fi
}


# Disable ProxySQL Cluster updates
#
# Globals
#   ADMIN_CHECKSUM_MYSQL_QUERY_RULES_ORIGINAL_VALUE (sets the value)
#   ADMIN_CHECKSUM_MYSQL_SERVERS_ORIGINAL_VALUE (sets the value)
#   ADMIN_CHECKSUM_MYSQL_USERS_ORIGINAL_VALUE (sets the value)
#
# Arguments
#   None
#
function disable_updates()
{
  debug "$LINENO" "Disabling ProxySQL checksum updates"

  proxysql_exec $LINENO "SAVE ADMIN VARIABLES FROM RUNTIME"
  check_cmd $? "$LINENO" "Failed to retrieve admin variables from ProxySQL."\
                       "\n-- Could not connect to ProxySQL at $PROXYSQL_HOSTNAME:$PROXYSQL_PORT"\
                       "\n-- Please check the ProxySQL connection parameters and status."

  local query_rules_value
  local users_value
  local servers_value
  local load_to_runtime=0

  query_rules_value=$(proxysql_exec $LINENO \
    "SELECT variable_value
      FROM global_variables
      WHERE variable_name = 'admin-checksum_mysql_query_rules'
    ")
  check_cmd $? "$LINENO" "Failed to retrieve admin variables from ProxySQL."\
                       "\n-- Could not connect to ProxySQL at $PROXYSQL_HOSTNAME:$PROXYSQL_PORT"\
                       "\n-- Please check the ProxySQL connection parameters and status."

  servers_value=$(proxysql_exec $LINENO \
    "SELECT variable_value
      FROM global_variables
      WHERE variable_name = 'admin-checksum_mysql_servers'
    ")
  check_cmd $? "$LINENO" "Failed to retrieve admin variables from ProxySQL."\
                       "\n-- Could not connect to ProxySQL at $PROXYSQL_HOSTNAME:$PROXYSQL_PORT"\
                       "\n-- Please check the ProxySQL connection parameters and status."

  users_value=$(proxysql_exec $LINENO \
    "SELECT variable_value
      FROM global_variables
      WHERE variable_name = 'admin-checksum_mysql_users'
    ")
  check_cmd $? "$LINENO" "Failed to retrieve admin variables from ProxySQL."\
                       "\n-- Could not connect to ProxySQL at $PROXYSQL_HOSTNAME:$PROXYSQL_PORT"\
                       "\n-- Please check the ProxySQL connection parameters and status."


  # If the value is set to true, then set the ORIGINAL_VALUE vars so
  # that the value will be reset on exit.  If the value is already false,
  # there's no need to reset the value on exit.
  if [[ $query_rules_value == 'true' ]]; then
    ADMIN_CHECKSUM_MYSQL_QUERY_RULES_ORIGINAL_VALUE=$query_rules_value
    proxysql_exec $LINENO "SET admin-checksum_mysql_query_rules = 'false'"
    check_cmd $? "$LINENO" "Failed to set the admin-checksum_mysql_query_rules variable in ProxySQL."\
                         "\n-- Could not connect to ProxySQL at $PROXYSQL_HOSTNAME:$PROXYSQL_PORT"\
                         "\n-- Please check the ProxySQL connection parameters and status."
    load_to_runtime=1
  else
    ADMIN_CHECKSUM_MYSQL_QUERY_RULES_ORIGINAL_VALUE=""
  fi

  if [[ $servers_value == 'true' ]]; then
    ADMIN_CHECKSUM_MYSQL_SERVERS_ORIGINAL_VALUE=$servers_value
    proxysql_exec $LINENO "SET admin-checksum_mysql_servers = 'false'"
    check_cmd $? "$LINENO" "Failed to set the admin-checksum_mysql_servers variable in ProxySQL."\
                         "\n-- Could not connect to ProxySQL at $PROXYSQL_HOSTNAME:$PROXYSQL_PORT"\
                         "\n-- Please check the ProxySQL connection parameters and status."
    load_to_runtime=1
  else
    ADMIN_CHECKSUM_MYSQL_SERVERS_ORIGINAL_VALUE=""
  fi

  if [[ $users_value == 'true' ]]; then
    ADMIN_CHECKSUM_MYSQL_USERS_ORIGINAL_VALUE=$users_value
    proxysql_exec $LINENO "SET admin-checksum_mysql_users = 'false'"
    check_cmd $? "$LINENO" "Failed to set the admin-checksum_mysql_users variable in ProxySQL."\
                         "\n-- Could not connect to ProxySQL at $PROXYSQL_HOSTNAME:$PROXYSQL_PORT"\
                         "\n-- Please check the ProxySQL connection parameters and status."
    load_to_runtime=1
  else
    ADMIN_CHECKSUM_MYSQL_USERS_ORIGINAL_VALUE=""
  fi

  # Set this to '', to disable clustering replication
  ADMIN_CLUSTER_USERNAME_ORIGINAL_VALUE=$(proxysql_exec $LINENO \
    "SELECT variable_value
      FROM global_variables
      WHERE variable_name = 'admin-cluster_username'
    ")
  check_cmd $? "$LINENO" "Failed to retrieve admin variables from ProxySQL."\
                       "\n-- Could not connect to ProxySQL at $PROXYSQL_HOSTNAME:$PROXYSQL_PORT"\
                       "\n-- Please check the ProxySQL connection parameters and status."
  if [[ -n $ADMIN_CLUSTER_USERNAME_ORIGINAL_VALUE ]]; then
    proxysql_exec $LINENO "SET admin-cluster_username = '';"
    check_cmd $? "$LINENO" "Failed to set the admin-cluster_username variable in ProxySQL."\
                         "\n-- Could not connect to ProxySQL at $PROXYSQL_HOSTNAME:$PROXYSQL_PORT"\
                         "\n-- Please check the ProxySQL connection parameters and status."
    load_to_runtime=1
  fi

  # BUG WORKAROUND
  # ProxySQL 'load admin variables to runtime' will not clear the cluster_username
  # so we change the frequency instead
  ADMIN_CLUSTER_CHECK_INTERVAL_ORIGINAL_VALUE=$(proxysql_exec $LINENO \
    "SELECT variable_value
      FROM global_variables
      WHERE variable_name = 'admin-cluster_check_interval_ms'")
  check_cmd $? "$LINENO" "Failed to retrieve admin variables from ProxySQL."\
                       "\n-- Could not connect to ProxySQL at $PROXYSQL_HOSTNAME:$PROXYSQL_PORT"\
                       "\n-- Please check the ProxySQL connection parameters and status."
  if [[ -n $ADMIN_CLUSTER_CHECK_INTERVAL_ORIGINAL_VALUE ]]; then
    proxysql_exec $LINENO "SET admin-cluster_check_interval_ms = 300000;"
    check_cmd $? "$LINENO" "Failed to set the admin-cluster_check_interval_ms variable in ProxySQL."\
                         "\n-- Could not connect to ProxySQL at $PROXYSQL_HOSTNAME:$PROXYSQL_PORT"\
                         "\n-- Please check the ProxySQL connection parameters and status."
    load_to_runtime=1
  fi


  if [[ $load_to_runtime -eq 1 ]]; then
    proxysql_exec $LINENO "LOAD ADMIN VARIABLES TO RUNTIME"
    check_cmd $? "$LINENO" "Failed to save the admin variables in ProxySQL."\
                         "\n-- Could not connect to ProxySQL at $PROXYSQL_HOSTNAME:$PROXYSQL_PORT"\
                         "\n-- Please check the ProxySQL connection parameters and status."
  fi

}


# Restore the ProxySQL Cluster admin update values
#
# Globals
#   ADMIN_CHECKSUM_MYSQL_QUERY_RULES_ORIGINAL_VALUE
#   ADMIN_CHECKSUM_MYSQL_SERVERS_ORIGINAL_VALUE
#   ADMIN_CHECKSUM_MYSQL_USERS_ORIGINAL_VALUE
#
# Arguments
#   None
#
function restore_updates()
{
  debug "$LINENO" "Restoring ProxySQL checksum updates"
  local vars_changed=0
  local change_cmds="\n--\tSAVE ADMIN VARIABLES FROM RUNTIME;"


  if [[ -n $ADMIN_CLUSTER_USERNAME_ORIGINAL_VALUE ]]; then
    change_cmds+="\n--\tSET admin-cluster_username='$ADMIN_CLUSTER_USERNAME_ORIGINAL_VALUE';"
  fi
  if [[ -n $ADMIN_CLUSTER_CHECK_INTERVAL_ORIGINAL_VALUE ]]; then
    change_cmds+="\n--\tSET admin-cluster_check_interval_ms='$ADMIN_CLUSTER_CHECK_INTERVAL_ORIGINAL_VALUE';"
  fi

  if [[ -n $ADMIN_CHECKSUM_MYSQL_QUERY_RULES_ORIGINAL_VALUE ]]; then
    change_cmds+="\n--\tSET admin-checksum_mysql_query_rules='$ADMIN_CHECKSUM_MYSQL_QUERY_RULES_ORIGINAL_VALUE';"
  fi
  if [[ -n $ADMIN_CHECKSUM_MYSQL_SERVERS_ORIGINAL_VALUE ]]; then
    change_cmds+="\n--\tSET admin-checksum_mysql_servers='$ADMIN_CHECKSUM_MYSQL_SERVERS_ORIGINAL_VALUE';"
  fi
  if [[ -n $ADMIN_CHECKSUM_MYSQL_USERS_ORIGINAL_VALUE ]]; then
    change_cmds+="\n--\tSET admin-checksum_mysql_users='$ADMIN_CHECKSUM_MYSQL_USERS_ORIGINAL_VALUE';"
  fi
  change_cmds+="\n--\tLOAD ADMIN VARIABLES TO RUNTIME;"

  if [[ -n $ADMIN_CLUSTER_USERNAME_ORIGINAL_VALUE ]]; then
    proxysql_exec $LINENO "SET admin-cluster_username = '$ADMIN_CLUSTER_USERNAME_ORIGINAL_VALUE'"
    check_cmd $? "$LINENO" "Failed to restore the admin-cluster_username variable in ProxySQL."\
                         "\n-- Could not connect to ProxySQL at $PROXYSQL_HOSTNAME:$PROXYSQL_PORT"\
                         "\n-- Please run the following commands manually to restore the state"\
                         "$change_cmds"
    vars_changed=1
  fi

  if [[ -n $ADMIN_CLUSTER_CHECK_INTERVAL_ORIGINAL_VALUE ]]; then
    proxysql_exec $LINENO "SET admin-cluster_check_interval_ms = '$ADMIN_CLUSTER_CHECK_INTERVAL_ORIGINAL_VALUE'"
    check_cmd $? "$LINENO" "Failed to restore the admin-cluster_check_interval_ms variable in ProxySQL."\
                         "\n-- Could not connect to ProxySQL at $PROXYSQL_HOSTNAME:$PROXYSQL_PORT"\
                         "\n-- Please run the following commands manually to restore the state"\
                         "$change_cmds"
    vars_changed=1
  fi

  if [[ -n $ADMIN_CHECKSUM_MYSQL_QUERY_RULES_ORIGINAL_VALUE ]]; then
    proxysql_exec $LINENO "SET admin-checksum_mysql_query_rules = '$ADMIN_CHECKSUM_MYSQL_QUERY_RULES_ORIGINAL_VALUE'"
    check_cmd $? "$LINENO" "Failed to restore the admin-checksum_mysql_query_rules variable in ProxySQL."\
                         "\n-- Could not connect to ProxySQL at $PROXYSQL_HOSTNAME:$PROXYSQL_PORT"\
                         "\n-- Please run the following commands manually to restore the state"\
                         "$change_cmds"
    vars_changed=1
  fi

  if [[ -n $ADMIN_CHECKSUM_MYSQL_SERVERS_ORIGINAL_VALUE ]]; then
    proxysql_exec $LINENO "SET admin-checksum_mysql_servers = '$ADMIN_CHECKSUM_MYSQL_SERVERS_ORIGINAL_VALUE'"
    check_cmd $? "$LINENO" "Failed to restore the admin-checksum_mysql_servers variable in ProxySQL."\
                         "\n-- Could not connect to ProxySQL at $PROXYSQL_HOSTNAME:$PROXYSQL_PORT"\
                         "\n-- Please run the following commands manually to restore the state"\
                         "$change_cmds"
    vars_changed=1
  fi

  if [[ -n $ADMIN_CHECKSUM_MYSQL_USERS_ORIGINAL_VALUE ]]; then
    proxysql_exec $LINENO "SET admin-checksum_mysql_users = '$ADMIN_CHECKSUM_MYSQL_USERS_ORIGINAL_VALUE'"
    check_cmd $? "$LINENO" "Failed to restore the admin-checksum_mysql_users variable in ProxySQL."\
                         "\n-- Could not connect to ProxySQL at $PROXYSQL_HOSTNAME:$PROXYSQL_PORT"\
                         "\n-- Please run the following commands manually to restore the state"\
                         "$change_cmds"
    vars_changed=1
  fi



  if [[ $vars_changed -eq 1 ]]; then
    proxysql_exec $LINENO "LOAD ADMIN VARIABLES TO RUNTIME"
    check_cmd $? "$LINENO" "Failed to save the admin variables in ProxySQL."\
                         "\n-- Could not connect to ProxySQL at $PROXYSQL_HOSTNAME:$PROXYSQL_PORT"\
                         "\n-- Please run the following commands manually to restore the state"\
                         "$change_cmds"
  fi
}


function on_exit()
{
  if [[ $DISABLE_UPDATES -eq 1 ]]; then
    restore_updates
  fi
}

#
# Waits until the scheduler processes the mysql_servers table in singlewrite
# mode i.e waits until the writer count becomes 1.
#
function wait_for_scheduler_processing ()
{
    # Wait for 30 seconds
    local timeout=30
    if is_mode_singlewrite $WRITER_HOSTGROUP_ID; then
      for i in `seq 1 $timeout`;
      do
        sleep 1
        # Check if there is only 1 writer
        data=$(proxysql_exec "$LINENO" \
                "SELECT COUNT(*)
                  FROM runtime_mysql_servers
                  WHERE
                    hostgroup_id = $WRITER_HOSTGROUP_ID AND
                    status = 'ONLINE'")
        [[ -n $data && $data -eq 1 ]] && break

      done

      if [[ $i -ge $timeout ]]; then
          error "" "Error: Timeout exceeded ($timeout seconds) while waiting for the scheduler to process the nodes."
          echo -e "Please check proxysql error logs for more information. Log will usually be found in /var/lib/proxysql/proxysql.log"
          echo -e ""
          echo -e "Note: The pxc_scheduler_handler is kept running for debugging purpose. Please fix the error and re-run the script with \"--update-cluster --remove-all-servers\" to setup proxysql from beginning."
        exit 1
      fi
    fi
}

#-------------------------------------------------------------------------------
#
# Step 4 : Begin script execution
#


# Check for --trace option
for var in "$@"
do
  if [[ $var == "--trace" ]]; then
    set -o xtrace
  fi
done


function check_binary() {
  local _binary="$1" _full_path

  # Checks if the binary is available.
  _full_path=$( command -v "$_binary" )
  commandStatus=$?
  debug "Checking binary $_binary"
  if [ $commandStatus -ne 0 ]; then
    return 1
  else
    # Checks if the binary has "execute" permission.
    [ -x "$_full_path" ] && return 0
  fi

  # Otherwise, return an error code.
  return 1
}



#
# Check that we can find the mysql client utility
#
  check_binary "mysql"
  if [[ $? -ne 0 ]]; then
  error "$LINENO" "The mysql client was not found, please install the mysql package."
  exit 1
fi

MYSQL_CLIENT_VERSION=$(get_mysql_version "mysql")
if [[ $? -ne 0 ]]; then
  error "$LINENO" "Cannot determine the MySQL client version from the version string"
  echo "-- Version string : $MYSQL_CLIENT_VERSION"
  exit 1
fi

#
# Check that we can find the scheduler
#
SCHEDULER_BINARY="$(dirname ${BASH_SOURCE[0]})/pxc_scheduler_handler"
SCHEDULER_BINARY=$(cd "$(dirname "${SCHEDULER_BINARY}")" && pwd)/$(basename "${SCHEDULER_BINARY}")
if [[ ! -x $SCHEDULER_BINARY ]]; then
  error "$LINENO" "Cannot find pxc_scheduler_handler or either the file does not have execute permission."\
                "\n---- Check the proxysql-admin installation."
  exit 1
fi


parse_args "$@"

# Set the default login path to point to a non-existent file
# This is to avoid having any login path settings interfere with the
# mysql client connections.
if [ -e "/dummypathnonexisting/.mylogin.cnf" ]; then
  error "" "/dummypathnonexisting/.mylogin.cnf found. This should not happen.";
  exit 1
else
  export MYSQL_TEST_LOGIN_FILE="/dummypathnonexisting/.mylogin.cnf"
fi


# We now use the tables for configuration and thus they should reflect
# the current state before we do any modification.
proxysql_connection_check
proxysql_exec "$LINENO" "SAVE MYSQL SERVERS FROM RUNTIME"
proxysql_exec "$LINENO" "SAVE MYSQL QUERY RULES FROM RUNTIME"
proxysql_exec "$LINENO" "SAVE MYSQL USERS FROM RUNTIME"
proxysql_exec "$LINENO" "SAVE MYSQL VARIABLES FROM RUNTIME"
proxysql_exec "$LINENO" "SAVE SCHEDULER FROM RUNTIME"

# on_exit will restore the update variables
trap on_exit EXIT

if [[ $DISABLE_UPDATES -eq 1 ]]; then
  disable_updates

  # sleep to ensure that any ongoing update has completed
  # before we continue
  sleep 3
fi

main

exit 0
