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

# This program is copyright 2016-2018 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

#bash prompt internal configuration
declare BD=""
declare NBD=""
declare RED=""
declare NRED=""

# Test if stdout and stderr are open to a terminal
if [[ -t 1 ]]; then
  BD=$(tput bold)
  NBD=$(tput sgr0)
fi
if [[ -t 2 ]]; then
  RED=$(tput setaf 1)
  NRED=$(tput sgr0)
fi

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

#
# Script parameters/constants
#
readonly    PROXYSQL_ADMIN_VERSION="1.4.14"
declare  -i DEBUG=0

#
# Global variables used by the script
#
declare    CONFIG_FILE="/etc/proxysql-admin.cnf"
declare    PROXYSQL_DATADIR=""

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    SLAVE_NODES=""
declare    SLAVE_HOSTNAME=""
declare    SLAVE_PORT=""
declare    SLAVE_IS_WRITER=""

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

declare -i WRITE_HOSTGROUP_ID=10
declare -i READ_HOSTGROUP_ID=11

declare    WRITE_NODE=""
declare    WRITE_NODES=""
declare    ENABLE_PRIORITY=""
declare    P_HOST_PRIORITY=""

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 USE_EXISTING_MONITOR_PASSWORD=0
declare -i WITH_CLUSTER_APP_USER=1

declare    WRITER_IS_READER="ondemand"

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

#
# This is the options string to be set in the proxysql_galera_checker
# command.  This is only set if MAX_CONNECTIONS is set via the command-line
#
declare    MAX_CONNECTIONS_ARGS=""

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

function error() {
  local lineno=$1
  shift
  if [[ -n $lineno ]]; then
    printf "${BD}ERROR${NBD} (line:$lineno) : ${*//%/%%}\n" 1>&2
  else
    printf "${BD}ERROR${NBD} : ${*//%/%%}\n" 1>&2
  fi
}

function warning() {
  local lineno=$1
  shift
  if [[ -n $lineno ]]; then
    printf "${BD}WARNING${NBD} (line:$lineno) : ${*//%/%%}\n" 1>&2
  else
    printf "${BD}WARNING${NBD}: ${*//%/%%}\n" 1>&2
  fi
}

function debug() {
  if [[ $DEBUG -eq 1 ]]; then
    local lineno=$1
    shift
    if [[ -n $lineno ]]; then
      printf "${RED}${BD}debug (line:$lineno) : ${*//%/%%}${NBD}${NRED}\n" 1>&2
    else
      printf "${RED}debug: ${*//%/%%}${NRED}\n" 1>&2
    fi
  fi
}

function dump_arguments() {
  local arg_list=""
  for arg do
    arg_list+=" '$arg'"
  done
  echo $arg_list
}


#
# Dispay script usage details
#
function usage() {
  ### Consider adding a select or prompt option for --write-node that will let you select from discovered nodes during setup ###
  local path=$0
  cat << EOF
Usage: ${path##*/} [ options ]
Options:
  --config-file=<config-file>        Read login credentials from a configuration file
                                     (command line options override any configuration file login credentials)
  --proxysql-datadir=<datadir>       Specify the proxysql data directory location
  --proxysql-username=user_name      ProxySQL service username
  --proxysql-password[=password]     ProxySQL service password
  --proxysql-port=port_num           ProxySQL service port number
  --proxysql-hostname=host_name      ProxySQL service hostname
  --cluster-username=user_name       Percona XtraDB Cluster node username
  --cluster-password[=password]      Percona XtraDB Cluster node password
  --cluster-port=port_num            Percona XtraDB Cluster node port number
  --cluster-hostname=host_name       Percona XtraDB Cluster node hostname
  --cluster-app-username=user_name   Percona XtraDB Cluster node application username
  --cluster-app-password[=password]  Percona XtraDB Cluster node application passwrod
  --without-cluster-app-user         Configure Percona XtraDB Cluster without application user
  --monitor-username=user_name       Username for monitoring Percona XtraDB Cluster nodes through ProxySQL
  --monitor-password[=password]      Password for monitoring Percona XtraDB Cluster nodes through ProxySQL
  --use-existing-monitor-password    Do not prompt for a new monitor password if one is provided.
  --node-check-interval=3000         Interval for monitoring node checker script (in milliseconds)
                                     (default: 3000)
  --mode=[loadbal|singlewrite]       ProxySQL read/write configuration mode
                                     currently supporting: 'loadbal' and 'singlewrite'
                                     (default: 'singlewrite')
  --write-node=host_name:port        Writer node to accept write statments.
                                     This option is supported only when using --mode=singlewrite
                                     Can accept comma delimited list with the first listed being
                                     the highest priority.
  --include-slaves=host_name:port    Add specified slave node(s) to ProxySQL, these nodes will go
                                     into the reader hostgroup and will only be put into
                                     the writer hostgroup if all cluster nodes are down (this
                                     depends on the value of --use-slave-as-writer).
                                     Slaves must be read only.  Can accept a comma delimited list.
                                     If this is used make sure 'read_only=1' is in the slave's my.cnf
  --use-slave-as-writer=<yes/no>     If this value is 'yes', then a slave may be used as a writer
                                     if the entire cluster is down. If 'no', then a slave
                                     will not be used as a writer. This option is required
                                     if '--include-slaves' is used.
  --writer-is-reader=<value>         Defines if the writer node also accepts writes.
                                     Possible values are 'always', 'never', and 'ondemand'.
                                     'ondemand' means that the writer node only accepts reads
                                     if there are no other readers.
                                     (default: 'ondemand')
  --max-connections=<NUMBER>         Value for max_connections in the mysql_servers table.
                                     This is the maximum number of connections that
                                     ProxySQL will open to the backend servers.
                                     (default: 1000)
  --debug                            Enables additional debug logging.
  --help                             Dispalys this help text.

These options are the possible operations for proxysql-admin.
One of the options below must be provided.
  --adduser                          Adds the Percona XtraDB Cluster application user to the ProxySQL database
  --disable, -d                      Remove any Percona XtraDB Cluster configurations from ProxySQL
  --enable, -e                       Auto-configure Percona XtraDB Cluster nodes into ProxySQL
  --quick-demo                       Setup a quick demo with no authentication
  --syncusers                        Sync user accounts currently configured in MySQL to ProxySQL
                                     May be used with --enable.
                                     (deletes ProxySQL users not in MySQL)
  --sync-multi-cluster-users         Sync user accounts currently configured in MySQL to ProxySQL
                                     May be used with --enable.
                                     (doesn't delete ProxySQL users not in MySQL)
  --version, -v                      Print version info

EOF
}


# Checks the return value of the most recent command
#
# Globals:
#   None
#
# Arguments:
#   1: the error code of the most recent command
#   2: the lineno where the error occurred
#   3: the error message if the error code is non-zero
#
# Exits the script if the retcode is non-zero.
#
function check_cmd() {
  local retcode=$1
  local lineno=$2
  shift 2

  if [[ ${retcode} -ne 0 ]]; then
    error "$lineno" $*
    exit 1 
  fi
}


# Check the permissions for a file or directory
#
# Globals:
#   None
#
# Arguments:
#   1: the bash test to be applied to the file
#   2: the lineno where this call is invoked (used for errors)
#   3: the path to the file
#   4: (optional) description of the path (mostly used for existence checks)
#
# Exits the script if the permissions test fails.
#
function check_permission() {
  local permission=$1
  local lineno=$2
  local path_to_check=$3
  local description=""
  if [[ $# -gt 3 ]]; then
    description="$4"
  fi

  if [ ! $permission "$path_to_check" ] ; then
    if [[ $permission == "-r" ]]; then
      error $lineno "You do not have READ permission for: $path_to_check"
    elif [[ $permission == "-w" ]]; then
      error $lineno "You do not have WRITE permission for: $path_to_check"
    elif [[ $permission == "-x" ]]; then
      error $lineno "You do not have EXECUTE permission for: $path_to_check"
    elif [[ $permission == "-e" ]]; then
      if [[ -n $description ]]; then
        error $lineno "Could not find the $description: $path_to_check"
      else
        error $lineno "Could not find: $path_to_check"
      fi
    elif [[ $permission == "-d" ]]; then
      if [[ -n $description ]]; then
        error $lineno "Could not find the $description: $path_to_check"
      else
        error $lineno "Could not find the directory: $path_to_check"
      fi
    elif [[ $permission == "-f" ]]; then
      if [[ -n $description ]]; then
        error $lineno "Could not find the $description: $path_to_check"
      else
        error $lineno "Could not find the file: $path_to_check"
      fi
    else
      error $lineno "You do not have the correct permissions for: $path_to_check"
    fi
    exit 1
  fi
}


# Executes a SQL query with the (fully) specified server
#
# Globals:
#   DEBUG
#
# 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

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

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

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

  retoutput=$(printf "[client]\nuser=${user//%/%%}\npassword=\"${password//%/%%}\"\nhost=${hostname//%/%%}\nport=${port//%/%%}"  \
      | mysql --defaults-file=/dev/stdin --protocol=tcp \
           --unbuffered --batch --silent ${args} -e "${query}")
  retvalue=$?

  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" "${dbgoutput}\n" | 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
#   2: (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 node in the cluster
#
# Globals:
#   CLUSTER_USERNAME
#   CLUSTER_PASSWORD
#   CLUSTER_HOSTNAME
#   CLUSTER_PORT
#
# Arguments:
#   1: The SQL query
#   2: Additional arguments to the mysql client for the query
#   3: (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

  exec_sql "$lineno" "$CLUSTER_USERNAME" "$CLUSTER_PASSWORD" \
           "$CLUSTER_HOSTNAME" "$CLUSTER_PORT" \
           "$query" "$args" "$more_options"

  return $?
}


# Executes a SQL query on a slave node
#
# Globals:
#   CLUSTER_USERNAME
#   CLUSTER_PASSWORD
#   SLAVE_HOSTNAME
#   SLAVE_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 slave_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

  exec_sql "$lineno" "$CLUSTER_USERNAME" "$CLUSTER_PASSWORD" \
           "$SLAVE_HOSTNAME" "$SLAVE_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 $?
}

# Separates the IP address from the port in a network address
# Works for IPv4 and IPv6
#
# Globals:
#   None
#
# Params:
#   1. The network address to be parsed
#
# Outputs:
#   A string with a space separating the IP address from the port
#
function separate_ip_port_from_address()
{
  #
  # Break address string into host:port/path parts
  #
  local address=$1

  # Has to have at least one ':' to separate the port from the ip address
  if [[ $address =~ : ]]; then
    ip_addr=${address%:*}
    port=${address##*:}
  else
    ip_addr=$address
    port=""
  fi

  # Remove any braces that surround the ip address portion
  ip_addr=${ip_addr#\[}
  ip_addr=${ip_addr%\]}

  echo "${ip_addr} ${port}"
}

# Combines the IP address and port into a network address
# Works for IPv4 and IPv6
# (If the IP address is IPv6, the IP portion will have brackets)
#
# Globals:
#   None
#
# Params:
#   1: The IP address portion
#   2: The port
#
# Outputs:
#   A string containing the full network address
#
function combine_ip_port_into_address()
{
  local ip_addr=$1
  local port=$2
  local addr

  if [[ ! $ip_addr =~ \[.*\] && $ip_addr =~ .*:.* ]] ; then
    # If there are no brackets and it does have a ':', then add the brackets
    # because this is an unbracketed IPv6 address
    addr="[${ip_addr}]:${port}"
  else
    addr="${ip_addr}:${port}"
  fi
  echo $addr
}

# 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" "show tables" >/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 "PXC connection check failed."\
                       "\n-- Could not connect to the PXC cluster at $CLUSTER_HOSTNAME:$CLUSTER_PORT"\
                       "\n-- Please check the PXC connection parameters and status."
  debug $LINENO "cluster connection check succeeded"
}


# Queries the user for the proxysql connection parameters
#
# Globals:
#   PROYXSQL_HOST
#   PROXYSQL_PORT
#   PROXYSQL_USERNAME
#   PROXYSQL_PASSWORD
#
# Arguments:
#   None
#
function quickdemo_get_proxysql_params() {
  debug $LINENO "quickdemo_get_proxysql_params ( $(dump_arguments "$@") )"
  read -r -p "Do you want to use the default ProxySQL credentials (admin:admin:6032:127.0.0.1) [y/n] ? " check_param
  case $check_param in
    y|Y)
      PROXYSQL_USERNAME="admin"
      PROXYSQL_PASSWORD="admin"
      PROXYSQL_PORT="6032"
      PROXYSQL_HOSTNAME="127.0.0.1"
    ;;
    n|N)
      echo ""
      echo -n "Enter the ProxySQL user name: "
      read -r PROXYSQL_USERNAME
      read -r -s -p  "Enter the ProxySQL user password: " PROXYSQL_PASSWORD;echo ""
      echo -n "Enter the ProxySQL port: "
      read -r PROXYSQL_PORT
      echo -n "Enter the ProxySQL hostname: "
      read -r PROXYSQL_HOSTNAME
      echo ""
    ;;
    *)
      error "" "Please type [y/n]!"
      exit 1
    ;;
  esac
}

# Queries the user for the PXC cluster connection parameters
#
# Globals:
#   CLUSTER_HOSTNAME
#   CLUSTER_PORT
#   CLUSTER_USERNAME
#   CLUSTER_PASSWORD
#
# Arguments:
#   None
#
function quickdemo_get_cluster_params() {
  debug $LINENO "quickdemo_get_cluster_params ( $(dump_arguments "$@") )"
  read -r -p "Do you want to use the default Percona XtraDB Cluster credentials (root::3306:127.0.0.1) [y/n] ? " check_param
  case $check_param in
    y|Y)
      CLUSTER_USERNAME="root"
      CLUSTER_PASSWORD=""
      CLUSTER_PORT="3306"
      CLUSTER_HOSTNAME="127.0.0.1"
    ;;
    n|N)
      echo ""
      echo -n "Enter the Percona XtraDB Cluster username (super user): "
      read -r CLUSTER_USERNAME
      read -r -s -p  "Enter the Percona XtraDB Cluster user password: " CLUSTER_PASSWORD; echo ""
      echo -n "Enter the Percona XtraDB Cluster port: "
      read -r CLUSTER_PORT
      echo -n "Enter the Percona XtraDB Cluster hostname: "
      read -r CLUSTER_HOSTNAME
      echo ""
    ;;
    *)
      error "" "Please type [y/n]."
      exit 1
    ;;
  esac
}


# 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

  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
    while [[ -z "${user_category}_USERNAME" ]]
    do
      echo -n "No input entered, Enter ${user_description} name: "
      read -r ${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
      while [[ -z "${user_category}_PASSWORD" ]]
      do
        read -r -s -p  "No input entered, Enter ${user_description} password: " ${user_category}_PASSWORD
      done
    fi
  fi
  username=$(eval "echo \$${user_category}_USERNAME")
  password=$(eval "echo \$${user_category}_PASSWORD")

  if [[ $user_category == "CLUSTER_APP" ]]; then
    local check_user
    check_user=$(mysql_exec "$LINENO" "SELECT user,host FROM mysql.user where user='$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
      local precheck_user
      precheck_user=$(proxysql_exec "$LINENO" "SELECT username FROM mysql_users where username='$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 $username@'$USER_HOST_RANGE' IDENTIFIED BY '$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 $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 ('$username','$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
      local check_user
      check_user=$(proxysql_exec "$LINENO" "SELECT username FROM mysql_users where username='$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."
        proxysql_exec "$LINENO" "INSERT INTO mysql_users (username,password,active,default_hostgroup) values ('$username','$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
}


# This will move the configuration from memory to the runtime (load)
# and from memory to disk (save)
#
# Globals:
#   None
#
# Arguments:
#   1: the proxysql data that is being loaded and saved
#      (for example "MYSQL USERS" or "MYSQL SERVERS").
#   2: the lineno where this function was invoked
#
# This function will exit the program if an error occurs while
# loaded to runtime or saving to disk.
#
function proxysql_load_to_runtime_save_to_disk() {
  local data_type=$1
  local lineno=$2
  local reload_from_runtime=0

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

  proxysql_exec "$LINENO" "LOAD ${data_type} TO RUNTIME"
  check_cmd $? $lineno "Failed to load the ${data_type} configuration to runtime."\
                       "\n-- Please check the ProxySQL configuration and status."
  debug "$lineno" "Loaded ${data_type} to runtime"

  if [[ $reload_from_runtime -eq 1 ]]; then
    # This has a specific purpose for the MYSQL USERS
    # This will cause the password field to be loaded with the encrypted version
    # of the password field
    proxysql_exec "$LINENO" "SAVE ${data_type} FROM RUNTIME"
    check_cmd $? $lineno "Failed to safe the ${data_type} configuration from the runtime."\
                         "\n-- Please check the ProxySQL configuration and status."
    debug "$lineno" "Saved ${data_type} from runtime"
  fi

  proxysql_exec "$LINENO" "SAVE ${data_type} TO DISK;"
  check_cmd $? $lineno "Failed to save the ${data_type} configuration to disk."\
                       "\n-- Please check the ProxySQL configuration and status."
  debug "$lineno" "Saved ${data_type} to disk"
}


# Auto configure Percona XtraDB Cluster nodes into ProxySQL
#
# Globals:
#   READ_HOSTGROUP_ID, WRITE_HOSTGROUP_ID
#   CLUSTER_NETWORK
#   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
#   SLAVE_NODES
#   MODE
#   WRITE_NODE
#   WRITE_NODES
#   P_HOST_PRIORITY
#
# Arguments:
#   None
#
function enable_proxysql() {
  local host_priority=""
  debug $LINENO "enable_proxysql ( $(dump_arguments "$@") )"
  # Checking proxysql binary location
  if [[ ! -e $(which proxysql 2> /dev/null) ]]; then
    error $LINENO "The proxysql binary was not found."\
                  "\n-- Please install the ProxySQL package."
    exit 1
  fi
  if [[ ! -e $(which proxysql_galera_checker 2> /dev/null) ]] ;then
    error $LINENO "The proxysql_galera_checker binary was not found."\
                  "\n-- Please check the ProxySQL package installation."
    exit 1
  fi
  if [[ ! -e $(which proxysql_node_monitor 2> /dev/null) ]]; then
    error $LINENO "The proxysql_node_monitor binary was not found."\
                  "\n-- Please check the ProxySQL package installation."
    exit 1
  fi

  local proxysql_galera_check
  proxysql_galera_check=$(which proxysql_galera_checker)
  debug $LINENO "Found the proxysql_galera_checker at $proxysql_galera_check"
  # Check for existing proxysql process
  proxysql_connection_check
  
  #modifying proxysql-admin.cnf file with command line proxysql user credentials if you dont use --config-file option.
  #if [ -z "${CONFIG_FILE}" ]; then 
  #  sed -i "s|[ \t]*PROXYSQL_USERNAME[ \t]*=.*$| PROXYSQL_USERNAME=\"${PROXYSQL_USERNAME}\"|" /etc/proxysql-admin.cnf
  #  sed -i "s|[ \t]*PROXYSQL_PASSWORD[ \t]*=.*$| PROXYSQL_PASSWORD=\"${PROXYSQL_PASSWORD}\"|" /etc/proxysql-admin.cnf
  #  sed -i "s|[ \t]*PROXYSQL_HOSTNAME[ \t]*=.*$| PROXYSQL_HOSTNAME=\"${PROXYSQL_HOSTNAME}\"|" /etc/proxysql-admin.cnf
  #  sed -i "s|[ \t]*PROXYSQL_PORT[ \t]*=.*$| PROXYSQL_PORT=\"${PROXYSQL_PORT}\"|" /etc/proxysql-admin.cnf
  #fi

  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 check_hgs
  check_hgs=$(proxysql_exec "$LINENO" "SELECT hostgroup_id FROM mysql_servers where hostgroup_id in ($READ_HOSTGROUP_ID,$WRITE_HOSTGROUP_ID)")
  if [[ ! -z "$check_hgs" ]]; then
    error $LINENO "READ/WRITE hostgroups($READ_HOSTGROUP_ID,$WRITE_HOSTGROUP_ID) are already being used by ProxySQL."\
                  "\n-- To avoid conflicts, please use different values for the hostgroups."
    exit 1
  fi

  CLUSTER_NETWORK=$(mysql_exec "$LINENO" "show status like 'wsrep_incoming_addresses'" |
                      awk '{print $2}' |
                      cut -d'.' -f1)
  check_cmd $? $LINENO "Could not retrieve the cluster addresses from PXC."\
                       "\n-- Please check the PXC connection parameters and status."
  if [[ "$CLUSTER_NETWORK" =~ ^[0-9]+$ ]]; then 
    USER_HOST_RANGE="$CLUSTER_NETWORK.%"
  else
    USER_HOST_RANGE="%"
  fi

  echo -e "\nConfiguring the ProxySQL monitoring user."
  user_input_check MONITOR "ProxySQL monitor user"
  readonly MONITOR_USERNAME

  local check_user
  check_user=$(mysql_exec "$LINENO" "SELECT user,host FROM mysql.user where user='$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 '$MONITOR_USERNAME'@'$USER_HOST_RANGE' IDENTIFIED BY '$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"

    proxysql_exec "$LINENO" "update global_variables set variable_value='$MONITOR_USERNAME' where variable_name='mysql-monitor_username'; update global_variables set variable_value='$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)
        if [[ $QUICK_DEMO -eq 0 ]]; then
          read -r -s -p  "Please enter the password you have assigned to the monitoring user '$MONITOR_USERNAME': " MONITOR_PASSWORD
          echo ""

          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='$MONITOR_USERNAME' where variable_name='mysql-monitor_username'; update global_variables set variable_value='$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."
        fi
      ;;
      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='$MONITOR_USERNAME' where variable_name='mysql-monitor_username'; update global_variables set variable_value='$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" $WRITE_HOSTGROUP_ID
    readonly CLUSTER_APP_USERNAME
    readonly CLUSTER_APP_PASSWORD
  fi

  # Get the nodes in the cluster
  wsrep_address=($(mysql_exec "$LINENO" "show status like 'wsrep_incoming_addresses'" | awk '{print $2}' | sed 's|,| |g'))
  check_cmd $? $LINENO "Failed to get the list of addresses from PXC"\
                       "\n-- Please check the PXC connection paramters and status."
  # If any slave nodes were specified, verify they are replicating from a valid cluster node
  if [[ -n $SLAVE_NODES ]];then
    echo -e "\nVerifying specified slave nodes"
    for i in $SLAVE_NODES; do
      ws_address=$(separate_ip_port_from_address "$i")
      SLAVE_HOSTNAME=$(echo "$ws_address" | cut -d' ' -f1)
      SLAVE_PORT=$(echo "$ws_address" | cut -d' ' -f2)

      # Verify we can connect to the slave
      slave_exec "$LINENO" 'show slave status\G' > /dev/null
      check_cmd $? $LINENO "Failed to connect to $i."\
                           "\n-- Please check the connection parameters for connecting to the slave."

      # Check each of the specified slaves and verify they are a slave of one of the XtraDB cluster nodes
      slave_master=$(slave_exec "$LINENO" 'show slave status\G'|
                        egrep "Master_Host|Master_Port" |
                        sed 's/ //g' |
                        cut -d: -f2 |
                        tr '\n' ':' |
                        sed 's/:$//g')
      local slave_master_address=$(separate_ip_port_from_address "$slave_master")
      local slave_master_ip=$(echo "$slave_master_address" | cut -d' ' -f1)
      local slave_master_port=$(echo "$slave_master_address" | cut -d' ' -f2)
      slave_master_address=$(combine_ip_port_into_address "$slave_master_ip", "$slave_master_port")
      if [[ ${wsrep_address[*]} != *"$slave_master"* ]]; then
        error $LINENO "The slave node's master ($slave_master_address) is not in the list"\
                      "\n-- of WSREP incoming address(${wsrep_address[*]})."\
                      "\n-- The specified slave is not replicating from any of the PXC nodes,"\
                      "\n-- this is not supported.  Please configure ProxySQL manually."
        exit 1
      fi

      # Verify the slave read-only variable is set to '1'
      slave_ro=$(slave_exec "$LINENO" 'SELECT @@read_only')
      check_cmd $? $LINENO "Could not get slave read_only status from $i"\
                           "\n-- Please check the connection parameters for connecting to the slave."
      if [ "$slave_ro" != "1" ];then
        error $LINENO "The slave $i is not set to read only."
        echo -e "-- Add 'read_only=1' to the my.cnf on the slave and restart MySQL."
        echo -e "-- Execute 'SET GLOBAL read_only = 1' on the slave to avoid the restart."
        echo -e "-- Correct this on the slave nodes and rerun proxysql-admin again."
        exit 1
      fi
    done

    # TODO: kennt, shouldn't this be on the slave node?
    # since that is where we will be querying for the slave status
    # GRANT REPLICATION CLIENT permissions to the monitoring user account
    echo -e "\nGranting 'REPLICATION CLIENT' privilege to $MONITOR_USERNAME@$USER_HOST_RANGE"
    mysql_exec "$LINENO" "GRANT REPLICATION CLIENT ON *.* TO '$MONITOR_USERNAME'@'$USER_HOST_RANGE';"
    check_cmd $? $LINENO "$CLUSTER_USERNAME@'$CLUSTER_HOSTNAME' does not have the GRANT privilege"\
                         "\n-- required to assign the requested permissions"
  fi

  # Adding Percona XtraDB Cluster nodes to ProxySQL
  echo -e "\nAdding the Percona XtraDB Cluster server nodes to ProxySQL"
  if [[ $MODE == "loadbal" ]]; then
    proxysql_exec "$LINENO" "DELETE FROM mysql_servers WHERE hostgroup_id=$WRITE_HOSTGROUP_ID"
    check_cmd $? $LINENO "Failed to delete the existing servers with hostgroup: ${WRITE_HOSTGROUP_ID}"\
                         "\n-- Please check the ProxySQL connection parameters and status."
    for i in "${wsrep_address[@]}"; do  
      local ws_address=$(separate_ip_port_from_address "$i")
      local ws_ip=$(echo "$ws_address" | cut -d' ' -f1)
      local ws_port=$(echo "$ws_address" | cut -d' ' -f2)
      ws_address=$(combine_ip_port_into_address "$ws_ip" "$ws_port")
      proxysql_exec "$LINENO" "INSERT INTO mysql_servers (hostname,hostgroup_id,port,weight,comment,max_connections) VALUES ('$ws_ip',$WRITE_HOSTGROUP_ID,$ws_port,1000,'READWRITE',$MAX_CONNECTIONS);"
      check_cmd $? $LINENO "Failed to add the PXC server node $ws_address."\
                           "\n-- Please check the ProxySQL connection parameters and status."
    done

    proxysql_load_to_runtime_save_to_disk "MYSQL SERVERS" $LINENO

  elif [[ $MODE == "singlewrite" ]]; then
    proxysql_exec "$LINENO" "DELETE FROM mysql_servers WHERE hostgroup_id in ($WRITE_HOSTGROUP_ID,$READ_HOSTGROUP_ID)"
    check_cmd $? $LINENO "Failed to delete the existing servers with hostgroup: ${WRITE_HOSTGROUP_ID},${READ_HOSTGROUP_ID}"\
                         "\n-- Please check the ProxySQL connection parameters and status."

    proxysql_exec "$LINENO" "DELETE FROM mysql_query_rules WHERE destination_hostgroup in ($WRITE_HOSTGROUP_ID,$READ_HOSTGROUP_ID)"
    check_cmd $? $LINENO "Failed to delete the existing query rules for hostgroup: ${WRITE_HOSTGROUP_ID},${READ_HOSTGROUP_ID}"\
                         "\n-- Please check the ProxySQL connection parameters and status."
    if [[ $QUICK_DEMO -eq 0 ]]; then
      local writer_ws_ip
      local writer_ws_port
      local writer_ws_address

      if [ -z "$WRITE_NODE" ]; then
        writer_ws_ip=$(mysql_exec "$LINENO" "show variables like 'wsrep_provider_options'" |
                        grep -o -P '(?<=base_host =).*(?=; base_port)' |
                        xargs)
        writer_ws_port=$CLUSTER_PORT
        writer_ws_address=$(combine_ip_port_into_address "$writer_ws_ip" "$writer_ws_port")
      else
        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)
        if [ -z "$writer_ws_port" ];then
          writer_ws_port=3306
        fi
        writer_ws_address=$(combine_ip_port_into_address "$writer_ws_ip" "$writer_ws_port")

        # TODO: kennt, do we want to display the error message?
        exec_sql $LINENO "$CLUSTER_USERNAME" "$CLUSTER_PASSWORD" "$writer_ws_ip" "$writer_ws_port" "select @@port" &>/dev/null
        if [ $? -ne 0 ]; then 
          error $LINENO "Failed to establish a connection to the write node $writer_ws_address."\
                        "\n-- Please check that the write node is alive and the connection parameters are correct"; 
          proxysql_exec "$LINENO" "DELETE FROM mysql_users WHERE default_hostgroup in ($WRITE_HOSTGROUP_ID,$READ_HOSTGROUP_ID);"
          check_cmd $? $LINENO "Failed to delete PXC user from ProxySQL."\
                               "\n-- Please check the ProxySQL connection parameters and status."
          exit 1
        fi
      fi
    else
      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)
    fi
    proxysql_exec "$LINENO" "DELETE FROM mysql_servers WHERE hostgroup_id=$WRITE_HOSTGROUP_ID"
    check_cmd $? $LINENO "Failed to delete the existing servers with hostgroup: ${WRITE_HOSTGROUP_ID}"\
                         "\n-- Please check the ProxySQL connection parameters and status."

    if [[ ${wsrep_address[*]} != *"$writer_ws_address"* ]]; then
      error $LINENO "Writer node cluster address($writer_ws_address) does not exist"\
                    "\n-- in WSREP incoming address(${wsrep_address[*]})."
      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

    # Create the host priority file if multiple write nodes were specified
    host_priority=""
    if [ -n "$ENABLE_PRIORITY" ];then
      echo -e "\nConfiguring $MODE mode with the following nodes designated as priority order:"
        for i in $WRITE_NODES;do
          echo " $i"
        done
      host_priority="--priority=$P_HOST_PRIORITY"
    fi

    for i in "${wsrep_address[@]}"; do
      local ws_address=$(separate_ip_port_from_address $i)
      local ws_ip=$(echo "$ws_address" | cut -d' ' -f1)
      local ws_port=$(echo "$ws_address" | cut -d' ' -f2)
      if [ "$ws_ip" == "$writer_ws_ip" ] && [ "$ws_port" == "$writer_ws_port" ]; then
        proxysql_exec "$LINENO" "INSERT INTO mysql_servers (hostname,hostgroup_id,port,weight,comment,max_connections) VALUES ('$writer_ws_ip',$WRITE_HOSTGROUP_ID,$writer_ws_port,1000000,'WRITE',$MAX_CONNECTIONS);"
        check_cmd $? $LINENO "Failed to add the PXC server node $ws_address."\
                             "\n-- Please check the ProxySQL connection parameters and status."
        echo -e "\nWrite node info"
        proxysql_exec "$LINENO" "SELECT hostname,hostgroup_id,port,weight,comment FROM mysql_servers WHERE hostgroup_id=$WRITE_HOSTGROUP_ID" "-t"
        echo ""
      else
        proxysql_exec "$LINENO" "INSERT INTO mysql_servers (hostname,hostgroup_id,port,weight,comment,max_connections) VALUES ('$ws_ip',$READ_HOSTGROUP_ID,$ws_port,1000,'READ',$MAX_CONNECTIONS);"
        check_cmd $? $LINENO "Failed to add the PXC server node $i."\
                              "\n-- Please check the ProxySQL connection parameters and status."
      fi
    done
    if [[ $WITH_CLUSTER_APP_USER -eq 1 ]]; then
      proxysql_exec "$LINENO" "INSERT INTO mysql_query_rules (username,destination_hostgroup,active,match_digest,apply) values('$CLUSTER_APP_USERNAME',$WRITE_HOSTGROUP_ID,1,'^SELECT.*FOR UPDATE',1),('$CLUSTER_APP_USERNAME',$READ_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."
    fi
    proxysql_load_to_runtime_save_to_disk "MYSQL SERVERS" $LINENO
    proxysql_load_to_runtime_save_to_disk "MYSQL QUERY RULES" $LINENO
  fi

  # Adding slave nodes here if specified
  if [ -n "$SLAVE_NODES" ];then
    echo -e "\nAdding the following slave server nodes to ProxySQL:"
    for i in $SLAVE_NODES;do
      local ws_address=$(separate_ip_port_from_address "$i")
      local ws_ip=$(echo "$ws_address" | cut -d' ' -f1)
      local ws_port=$(echo "$ws_address" | cut -d' ' -f2)
      proxysql_exec "$LINENO" "INSERT INTO mysql_servers (hostname,hostgroup_id,port,weight,comment,max_connections) VALUES ('$ws_ip',$SLAVEREAD_HOSTGROUP_ID,$ws_port,1000,'SLAVEREAD',$MAX_CONNECTIONS);"
      check_cmd $? $LINENO "Failed to add the slave node $i."\
                           "\n-- Please check the ProxySQL connection parameters and status."

      proxysql_load_to_runtime_save_to_disk "MYSQL SERVERS" $LINENO
      echo " $i"
    done
  fi

  # Adding Percona XtraDB Cluster monitoring scripts
  # Adding proxysql galera check scheduler
  proxysql_exec "$LINENO" "DELETE FROM SCHEDULER WHERE COMMENT='$cluster_name';"
  check_cmd $? $LINENO "Failed to delete the PXC nodes from ProxySQL."\
                       "\n-- Please check the ProxySQL connection parameters and status."
  local number_writers
  if [ $MODE == "singlewrite" ]; then
    number_writers=1
  else
    number_writers=0
  fi
  proxysql_exec "$LINENO" "INSERT INTO SCHEDULER (active,interval_ms,filename,arg1,comment) VALUES (1,$NODE_CHECK_INTERVAL,'$proxysql_galera_check','--config-file=$CONFIG_FILE --writer-is-reader=$WRITER_IS_READER --write-hg=$WRITE_HOSTGROUP_ID --read-hg=$READ_HOSTGROUP_ID --writer-count=$number_writers --mode=$MODE $host_priority --use-slave-as-writer=$SLAVE_IS_WRITER $MAX_CONNECTIONS_ARGS --log=$PROXYSQL_DATADIR/${cluster_name}_proxysql_galera_check.log','$cluster_name');"
  check_cmd $? $LINENO "Failed to add the PXC monitoring scheduler in ProxySQL."\
                       "\n-- Please check the ProxySQL connection parameters and status."

  proxysql_load_to_runtime_save_to_disk "SCHEDULER" $LINENO
}

# Removing Percona XtraDB Cluster configuration from proxysql
#
# Globals:
#   CLUSTER_HOSTNAME, CLUSTER_PORT
#   CLUSTER_APP_USERNAME
#   WRITE_HOSTGROUP_ID
#   READ_HOSTGROUP_ID
#
# Arguments:
#
function disable_proxysql(){
  debug $LINENO "disable_proxysql ( $(dump_arguments "$@") )"

  proxysql_connection_check
  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

  echo "Removing default cluster application user from ProxySQL database."
  proxysql_exec "$LINENO" "DELETE FROM mysql_users WHERE username='$CLUSTER_APP_USERNAME';"
  check_cmd $? $LINENO "Failed to delete the PXC user ($CLUSTER_APP_USERNAME) from ProxySQL."\
                       "\n-- Please check the ProxySQL connection parameters and status."

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

  echo "Removing scheduler script from ProxySQL database."
  proxysql_exec "$LINENO" "DELETE FROM SCHEDULER WHERE COMMENT='$cluster_name';"
  check_cmd $? $LINENO "Failed to delete the Galera checker from ProxySQL."\
                       "\n-- Please check the ProxySQL connection parameters and status."

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


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

# Adds an application user to ProxySQL
#
# Globals:
#   WRITE_HOSTGROUP_ID
#
# Arguments:
#   None
#
function adduser(){
  debug $LINENO "adduser ( $(dump_arguments "$@") )"

  proxysql_connection_check
  cluster_connection_check

  local cluster_app_write_username
  local cluster_app_write_password

  echo -e "\nAdding PXC application user to the ProxySQL database"
  echo -n "Enter the PXC application user name: "
  read -r cluster_app_write_username
  while [[ -z "$cluster_app_write_username" ]]
  do
    echo -n "No input entered. Enter the PXC application user name: "
    read -r cluster_app_write_username
  done
  read -r -s -p  "Enter the PXC application user password: " cluster_app_write_password
  while [[ -z "$cluster_app_write_password" ]]
  do
    read -r -s -p  "No input entered. Enter the PXC application user password: " cluster_app_write_password
  done

  # check to see if the user already exists
  local check_user
  local check_cluster_user

  check_user=$(proxysql_exec "$LINENO" "SELECT username FROM mysql_users where username='$cluster_app_write_username'")
  check_cmd $? $LINENO "Could not retrieve the user from ProxySQL."\
                       "\n-- Check the ProxySQL connection parameters and status."
  if [[ -n "$check_user" ]]; then
    error $LINENO "The application user '$cluster_app_write_username' already exists in ProxySQL."
    exit 1
  fi

  check_cluster_user=$(mysql_exec "$LINENO" "SELECT user,host FROM mysql.user where user='$cluster_app_write_username'")
  check_cmd $? $LINENO "Could not retrieve the user from PXC."\
                       "\n-- Check the PXC connection parameters and status."
  if [[ -z "$check_cluster_user" ]]; then
    local check_param
    echo -e "\n\n"
    read -r -p "The application user '$cluster_app_write_username' does not exist in PXC. Would you like to proceed [y/n] ? " check_param
    case $check_param in
      y|Y)
        proxysql_exec "$LINENO" "INSERT INTO mysql_users (username,password,active,default_hostgroup) values ('$cluster_app_write_username','$cluster_app_write_password',1,$WRITE_HOSTGROUP_ID);"
        check_cmd $? $LINENO "Failed to add the PXC application user: '$cluster_app_write_username' to ProxySQL."\
                             "\n-- Please check the ProxySQL connection parameters and status."
        echo -e "\nPlease create the user ${BD}$cluster_app_write_username${NBD} in PXC to access the application through ProxySQL"
      ;;
      n|N)
        exit 0
      ;;
      *)
        error "" "Please type [y/n]!"
        exit 1
      ;;
    esac
  else
    proxysql_exec "$LINENO" "INSERT INTO mysql_users (username,password,active,default_hostgroup) values ('$cluster_app_write_username','$cluster_app_write_password',1,$WRITE_HOSTGROUP_ID);"
    check_cmd $? $LINENO "Failed to add the PXC application user: '$cluster_app_write_username' to ProxySQL."\
                         "\n-- Please check the ProxySQL connection parameters and status."
  fi

  proxysql_load_to_runtime_save_to_disk "MYSQL USERS" $LINENO "1"
}


# Returns a list of users from the ProxySQL database
#
# Globals:
#   WRITE_HOSTGROUP_ID
#
# Arguments:
#   None
#
function get_proxysql_users() {
  local proxysql_users
  proxysql_users=$(proxysql_exec "$LINENO" "SELECT username,password FROM mysql_users where password!='' and default_hostgroup=$WRITE_HOSTGROUP_ID" "" "hide_output")
  check_cmd $? $LINENO "Failed to load user list from ProxySQL database."\
                       "\n-- Please check the ProxySQL connection parameters and status."
  proxysql_users=$(echo "$proxysql_users" |
                      sed 's/\t/,/g' |
                      egrep -v "username,password" |
                      sort |
                      uniq )
  printf "$proxysql_users"
}


# Checks if a user is a ProxySQL admin user
#
# Globals:
#   None
#
# Arguments:
#   1: the name of the use to be checked
#
# Outputs (to stdout):
#   1 if the user is a proxysql admin user
#   0 if the user is not a proxysql admin user
#
function proxysql_admin_user_check(){
  local userchk=$1
  local proxysql_admin_users
  local is_proxysql_admin_user

  proxysql_admin_users=($(proxysql_exec "$LINENO" "select variable_value from global_variables where variable_name like 'admin-%_credentials'" |
                              cut -d':' -f1 |
                              grep -v variable_value))
  if [[ " ${proxysql_admin_users[@]} " =~ " $userchk " ]]; then
    is_proxysql_admin_user=1
  else
    is_proxysql_admin_user=0
  fi
  printf "$is_proxysql_admin_user"
}


# Synchronizes the users between ProxySQL and PXC
#
# This function was created to auto sync all the existing users already
# in MySQL to proxySQL's mysql_users table.  As there is not much point
# in having users in ProxySQL that don't exist in MySQL, this function
# will delete any users from ProxySQL that were not found in MySQL.
#
# Going forward you can add/remove application users in MySQL then
# rerun proxysql-admin with the --syncusers switch to replicate the changes
# to ProxySQL.
#
# LIMITATIONS: Will not work properly in cases where the same user name
#              exists in MySQL with several hosts and different passwords.
#              This will cause ProxySQL to throw a "UNIQUE constraint failed"
#              error message.
#
# Globals:
#   WRITE_HOSTGROUP_ID
#   SYNCUSERS
#
# Arguments:
#   None
#
function syncusers() {
  debug $LINENO "syncusers ( $(dump_arguments "$@") )"

  proxysql_connection_check
  cluster_connection_check

  local mysql_version
  local password_field

  # Get current MySQL users, filter out header row and mysql.sys user
  mysql_version=$(mysql_exec "$LINENO" "SELECT VERSION();" | tail -1 | cut -d'.' -f1,2 )
  check_cmd $? $LINENO "Could not connect to the PXC node."\
                       "\n-- Please check the PXC connection parameters and status."

  case $mysql_version in
    5.6)
      password_field="Password"
      ;;
    5.7)
      password_field="authentication_string"
      ;;
    10.2)
      password_field="Password"
      ;;
    10.3)
      password_field="Password"
      ;;
    *)
      error $LINENO "Unexpected database server version: ${mysql_version}"\
                    "\n-- This version of proxysql-admin needs to be updated."
      exit 1
      ;;
  esac
  
  mysql_users=$(mysql_exec "$LINENO" "SELECT User,${password_field} FROM mysql.user where ${password_field}!=''" "" "hide_output" |
                  sed 's/\t/,/g' |
                  egrep -v "User,${password_field}|mysql.sys|mysql.session" |
                  sort |
                  uniq )
  check_cmd $? $LINENO "Failed to load the user list from PXC."\
                       "\n-- Please check the PXC connection parameters and status."

  #Checking whether user is part of proxysql admin user list
  # Get current ProxySQL users and filter out header row
  proxysql_users=$(get_proxysql_users)

  echo -e "\nSyncing user accounts from PXC to ProxySQL"

  # TEST FOR USERS THAT EXIST IN MYSQL BUT NOT IN PROXYSQL HERE AND ADD
  for mysql_user in $mysql_users;do
    local match=0
    for proxysql_user in $proxysql_users;do
      if [ "$proxysql_user" == "$mysql_user" ];then
        match=1
        break
      fi
    done
    if [[ $match -eq 0 ]]; then
      local user=$(echo $mysql_user | cut -d, -f1)
      local password=$(echo $mysql_user | cut -d, -f2)

      # Check if same username exists with a different password, delete the user to recreate.
      for proxysql_user in $proxysql_users; do
        echo $proxysql_user | grep "^${user}," > /dev/null
        if [ "$?" == 0 ];then
          # Remove the user
          echo "Removing existing user from ProxySQL: $user"
          proxysql_exec "$LINENO" "DELETE FROM mysql_users WHERE username='${user}' and default_hostgroup=$WRITE_HOSTGROUP_ID"
          check_cmd $? $LINENO "Failed to delete the user ($user) from ProxySQL database."\
                               "\n-- Please check the ProxySQL connection parameters and status."
        fi
      done

      local is_proxysql_admin_user
      is_proxysql_admin_user=$(proxysql_admin_user_check $user)
      if [[ $is_proxysql_admin_user -eq 1 ]]; then
        echo -e "\nNote : '$user' is in proxysql admin user list, this user cannot be addded to ProxySQL"\
                "\n-- (For more info, see https://github.com/sysown/proxysql/issues/709)"
      else
        check_user=$(proxysql_exec "$LINENO" "SELECT username from mysql_users where username='${user}'")
        if [[ -z $check_user ]]; then
          echo "Adding user to ProxySQL: $user"
          proxysql_exec "$LINENO" "INSERT INTO mysql_users (username, password, active, default_hostgroup) VALUES ('${user}', '${password}', 1, $WRITE_HOSTGROUP_ID)"
          check_cmd $? $LINENO "Failed to add the user ($user) from PXC to ProxySQL database."\
                               "\n-- Please check the ProxySQL connection parameters and status."
        else
          echo "Cannot add the user (${user}). The user (${user}) already exists in ProxySQL database with different hostgroup."
          check_user=""
        fi
      fi
    fi
  done

  if [[ $SYNCUSERS -eq 1 ]]; then
    # TEST FOR USERS THAT EXIST IN PROXYSQL BUT NOT IN MYSQL HERE AND REMOVE
    # Again get all users
    proxysql_users=$(get_proxysql_users)
    for proxysql_user in $proxysql_users; do
      local match=0
      for mysql_user in $mysql_users;do
        if [ "$proxysql_user" == "$mysql_user" ];then
          match=1
          break
        fi
      done

      if [ "$match" == 0 ];then
        # Delete the ProxySQL user
        user=$(echo $proxysql_user | cut -d, -f1)
        echo -e "\nRemoving user from ProxySQL: $proxysql_user"
        proxysql_exec "$LINENO" "DELETE FROM mysql_users WHERE username='${user}' and default_hostgroup=$WRITE_HOSTGROUP_ID"
        check_cmd $? $LINENO "Failed to delete the user ($user) from ProxySQL database."\
                             "\n-- Please check the ProxySQL connection parameters and status."
      fi
    done
  fi
  proxysql_load_to_runtime_save_to_disk "MYSQL USERS" $LINENO
}


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

  # 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=config-file:,proxysql-datadir:,proxysql-username:,proxysql-password::,proxysql-hostname:,proxysql-port:,cluster-username:,cluster-password::,cluster-hostname:,cluster-port:,monitor-username:,monitor-password:,cluster-app-username:,cluster-app-password:,node-check-interval:,quick-demo,mode:,write-node:,include-slaves:,use-slave-as-writer:,use-existing-monitor-password,without-cluster-app-user,writer-is-reader:,enable,disable,adduser,syncusers,sync-multi-cluster-users,max-connections:,version,debug,help \
    --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
  #
  for arg
  do
    case "$arg" in
      -- ) shift; break;;
      --config-file )
        CONFIG_FILE="$2"
        check_permission -e $LINENO "$CONFIG_FILE" "proxysql-admin configuration file"
        debug $LINENO  "--config-file specified, using : $CONFIG_FILE"
        shift 2
        ;;
      --help)
        usage
        exit 0
        ;;
      -v | --version)
        echo "proxysql-admin version ${PROXYSQL_ADMIN_VERSION}"
        exit 0
        ;;
      --debug)
        DEBUG=1
        shift
        ;;
      --quick-demo )
        shift
        QUICK_DEMO=1
        ENABLE=1
        ;;
      *)
        shift
        ;;
    esac
  done

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

  #
  # Load the config file before reading in the command-line options
  #
  readonly CONFIG_FILE
  if [[ $QUICK_DEMO -eq 0 ]]; then
    if [ ! -e "$CONFIG_FILE" ]; then
        warning "" "Could not locate the configuration file: $CONFIG_FILE"
    else
        check_permission -r $LINENO "$CONFIG_FILE"
        debug $LINENO "Loading $CONFIG_FILE"
        source "$CONFIG_FILE"
    fi
  fi

  #
  # Iterate through the comamnd-line options
  #
  for arg
  do
    case "$arg" in
      -- ) shift; break;;
      --config-file )
        # Do no processing of config-file here, it is processed
        # before this loop (see above)
        shift 2
        ;;
      --proxysql-datadir )
        PROXYSQL_DATADIR="$2"
        shift 2
        ;;
      --proxysql-username )
        PROXYSQL_USERNAME="$2"
        shift 2
        ;;
      --proxysql-password )
        case "$2" in
          "")
            read -r -s -p  "Enter ProxySQL password:" INPUT_PASS
            if [ -z "$INPUT_PASS" ]; then
              PROXYSQL_PASSWORD=""
              printf "\nContinuing without ProxySQL password...\n";
            else
              PROXYSQL_PASSWORD="$INPUT_PASS"
            fi
            printf "\n"
            ;;
          *)
            PROXYSQL_PASSWORD="$2"
            ;;
        esac
        shift 2
        ;;
      --proxysql-hostname )
        PROXYSQL_HOSTNAME="$2"
        shift 2
        ;;
      --proxysql-port )
        PROXYSQL_PORT="$2"
        shift 2
        ;;
      --cluster-username )
        CLUSTER_USERNAME="$2"
        shift 2
        ;;
      --cluster-password )
        case "$2" in
          "")
            read -r -s -p  "Enter PXC password:" INPUT_PASS
            if [ -z "$INPUT_PASS" ]; then
              CLUSTER_PASSWORD=""
              printf "\nContinuing without PXC password...\n";
            else
              CLUSTER_PASSWORD="$INPUT_PASS"
            fi
            printf "\n"
            ;;
          *)
            CLUSTER_PASSWORD="$2"
            ;;
        esac
        shift 2
        ;;
      --cluster-hostname )
        CLUSTER_HOSTNAME="$2"
        shift 2
        ;;
      --cluster-port )
        CLUSTER_PORT="$2"
        shift 2
        ;;
      --monitor-username )
        MONITOR_USERNAME="$2"
        shift 2
        ;;
      --monitor-password )
        MONITOR_PASSWORD="$2"
        shift 2
        ;;
      --use-existing-monitor-password )
        USE_EXISTING_MONITOR_PASSWORD=1
        shift
        ;;
      --cluster-app-username )
        CLUSTER_APP_USERNAME="$2"
        shift 2
        ;;
      --cluster-app-password )
        CLUSTER_APP_PASSWORD="$2"
        shift 2
        ;;
      --without-cluster-app-user )
        shift
        WITH_CLUSTER_APP_USER=0
        ;;
      --writer-is-reader )
        WRITER_IS_READER="$2"
        shift 2
        ;;
      -e | --enable )
        shift
        ENABLE=1
        ;;
      --adduser )
        shift
        ADDUSER=1
        ;;
      --syncusers )
        shift
        SYNCUSERS=1
        ;;
      --sync-multi-cluster-users )
        shift
        SYNCMULTICLUSTERUSERS=1
        ;;
      -d | --disable )
        shift
        DISABLE=1
        ;;
      --node-check-interval )
        NODE_CHECK_INTERVAL="$2"
        shift 2
        ;;
      --mode )
        MODE="$2"
        shift 2
        ;;
      --write-node )
        P_HOST_PRIORITY=$2
        WRITE_NODES=$(echo "$2" | sed 's/,/ /g')
        WRITE_NODE=$(echo "$WRITE_NODES" | cut -d' ' -f1)
        ENABLE_PRIORITY=1
        shift 2
        ;;
      --include-slaves )
        SLAVE_NODES=`echo "$2" | sed 's/,/ /g'`
        shift 2
        ;;
      --use-slave-as-writer )
        SLAVE_IS_WRITER="$2"
        shift 2
        ;;
      --quick-demo )
        shift
        QUICK_DEMO=1
        ENABLE=1
        ;;
      --max-connections )
        MAX_CONNECTIONS="$2"
        shift 2

        # Verify that we have an integer >= 0
        if [[ -n $MAX_CONNECTIONS ]]; then
          if ! [ "$MAX_CONNECTIONS" -eq "$MAX_CONNECTIONS" ] 2>/dev/null
          then
            error "" "option '--max-connections' requires a number : $MAX_CONNECTIONS"
            exit 1
          fi

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

        # If set by the command-line, use it in the arguments
        # for proxysql_galera_checker (so that it overrides the config file)
        MAX_CONNECTIONS_ARGS="--max-connections=$MAX_CONNECTIONS"
        ;;
      -v | --version )
        # Detection of this setting is done before this
        shift
        ;;
      --debug )
        # Detection of this setting is done before this
        shift
        ;;
      --help )
        # Detection of this setting is done before this
        shift
        ;;
    esac
  done

  # WRITE_NODES validity check (check to see if the port is included)
  local ws_address
  local ws_port
  if [[ -n $WRITE_NODES ]]; then
    for node in $WRITE_NODES
    do
      ws_address=$(separate_ip_port_from_address "$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 '$node'"
        exit 1
      fi

    done
  fi

  # Check user permission to proxysql datadir
  if [[ -z $PROXYSQL_DATADIR ]]; then
    if [[ -d /var/lib/proxysql ]]; then
      PROXYSQL_DATADIR="/var/lib/proxysql"
    elif [[ -d /usr/share/proxysql ]]; then
      PROXYSQL_DATADIR="/usr/share/proxysql"
    else
      error "$LINENO" "Could not find the ProxySQL datadir.  Please specify using --proxysql-datadir"
      exit 1
    fi
  fi
  check_permission -d $LINENO $PROXYSQL_DATADIR "ProxySQL datadir"
  check_permission -w $LINENO $PROXYSQL_DATADIR
  readonly PROXYSQL_DATADIR

  debug "" "ProxySQL datadir: $PROXYSQL_DATADIR"

  if [[ $QUICK_DEMO -eq 1 ]]; 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)"
    echo -e "\nYou have selected the dry test run mode.\n"

    warning "" "This will create a test user (with all privileges)"

    echo -e "in the Percona XtraDB Cluster & ProxySQL installations.\n"
    echo -e "You may want to delete this user after you complete your testing!\n"

    read -r -p "Would you like to proceed with '--quick-demo' [y/n] ? " check_param
    case $check_param in
      y|Y)
        echo -e "\nSetting up proxysql test configuration!\n"
      ;;
      n|N)
        echo -e "\nYou have selected No. Terminating.\n"
        exit 0
      ;;
      *)
        error "" "Please type [y/n]! Terminating."
        exit 1
      ;;
    esac
  fi

  if [[ ! -e $(which mysql 2> /dev/null) ]] ;then
    error $LINENO "The mysql client was not found, please install the mysql client package."
    exit 1
  fi

  # Check the options gathered from the command line
  if [[ $QUICK_DEMO -eq 0 ]]; then
    if [[ -z "$PROXYSQL_USERNAME" ]];then
      error "" "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
  else
    quickdemo_get_proxysql_params
    quickdemo_get_cluster_params

    MONITOR_USERNAME='monitor'
    MONITOR_PASSWORD='monitor'
    CLUSTER_APP_USERNAME='pxc_test_user'
    CLUSTER_APP_PASSWORD=''

    readonly MONITOR_USERNAME
    readonly MONITOR_PASSWORD
    readonly CLUSTER_APP_USERNAME
    readonly CLUSTER_APP_PASSWORD
  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 CLUSTER_PORT
  readonly CLUSTER_HOSTNAME

  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 [ $MODE == "loadbal" ]; then
    SLAVEREAD_HOSTGROUP_ID=$READ_HOSTGROUP_ID
    READ_HOSTGROUP_ID=-1
  elif [ $MODE == "singlewrite" ]; then
    SLAVEREAD_HOSTGROUP_ID=$READ_HOSTGROUP_ID
  fi

  if [[ ! $WRITER_IS_READER =~ ^(always|never|ondemand)$ ]]; then
    error "" "Invalid --writer-is-reader option: '$WRITER_IS_READER'"
    echo "Please choose one of these values: always, never, or ondemand"
    exit 1
  fi

  if [[ -n $SLAVE_NODES ]]; then
    if [[ -z $SLAVE_IS_WRITER ]]; then
      error "" "Missing option: If --include-slaves is used, then --use-slave-as-writer must be set also"
      exit 1
    fi
  elif [[ -z $SLAVE_IS_WRITER ]]; then
    SLAVE_IS_WRITER="no"
  fi

  if [[ ! $SLAVE_IS_WRITER =~ ^(yes|YES|no|NO)$ ]]; then
    error "" "Invalid --use-slave-as-writer option: '$SLAVE_IS_WRITER'"
    echo "Please choose yes or no"
    exit 1
  fi

  readonly WRITE_HOSTGROUP_ID
  readonly READ_HOSTGROUP_ID
  readonly SLAVEREAD_HOSTGROUP_ID

  readonly WRITE_NODE
  readonly WRITE_NODES
  readonly ENABLE_PRIORITY
  readonly P_HOST_PRIORITY

  readonly SLAVE_NODES
  readonly SLAVE_IS_WRITER

  readonly ENABLE
  readonly DISABLE
  readonly ADDUSER
  readonly SYNCUSERS
  readonly SYNCMULTICLUSTERUSERS
  readonly QUICK_DEMO

  readonly WITH_CHECK_MONITOR_USER
  readonly WITH_CLUSTER_APP_USER
  readonly WRITER_IS_READER

  readonly MAX_CONNECTIONS
  readonly MAX_CONNECTIONS_ARGS
}

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

  if [[ $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}"

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

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

    if [[ -n $MAX_CONNECTIONS_ARGS ]]; then
      # --max-connections was specified on the command-line, issue a warning
      # 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
    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!"

  else

    error "" "Must specify an operation: --enable, --disable, --adduser, --quick-demo, --syncusers, or --sync-multi-cluster-users"
    exit 1

  fi
}


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

# Internal variables
if [ -e "/dummypathnonexisting/.mylogin.cnf" ]; then
  error "" "/dummypathnonexisting/.mylogin.cnf found. This should not happen.";
  exit 1
else
  export HOME="/dummypathnonexisting"
fi

parse_args "$@"
main

exit 0
