#!/bin/bash

# Copyright: 2023 Virtuozzo International GmbH

# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at

# http://www.apache.org/licenses/LICENSE-2.0

# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

[ -n "${BALLIB_VERSION:-}" ] && return 0;
BALLIB_VERSION="0.1";
VERBOSE=0
include log;
$PROGRAM 'grep';
$PROGRAM 'sed';
$PROGRAM 'awk';
$PROGRAM 'perl';

NGINX_CONFIG='/etc/nginx/nginx-jelastic.conf';					# main nginx config file
NGINX_SSL_CONFIG="/etc/nginx/conf.d/ssl.conf";
DHCPD_CONFIG='/etc/dhcp/dhcpd.conf';
NGINX_TCPMAP_CONFIG='/etc/nginx/tcpmaps/mappings.xml';
NGINX_TCPMAP_TEMPLATE='/etc/nginx/templates/nginx.tcp.conf.tpl';
NGINX_CONFIG_TEMPLATE='/etc/nginx/templates/nginx.conf.tpl' ;            # template of nginx configuration
NGINX_NOBACKENDS_CONFIG_TEMPLATE='/etc/nginx/templates/nginx.conf.nobackends.tpl';
DHCPD_CONFIG_TEMPLATE='/etc/dhcp/dhcpd.tpl';
UPSTREAMS_LOCATION_PATH='/etc/nginx/upstreams';				# upstream configs location
UPSTREAM_COMMON_CONFIG=$UPSTREAMS_LOCATION_PATH/common	;		# common upstream hosts
FLUSH_LEASES_COMMAND="echo > /var/lib/dhcpd/dhcpd.leases";
LEASES_FILE="/var/lib/dhcpd/dhcpd.leases";
TMP_CONFIG="/tmp/$$.tmp.conf";
TMP_TEMPLATE="/tmp/$$.tmp.template.conf";
SUBNET_STEP=32;
AMOUNT_OF_DHCPD_LEASES=0;
DEBUG_LOGFILE="/var/log/build_groups.log";
DEBUG_IS_ENABLED="yes";
NB_PATH="/etc/nginx/neighbors/common";
EXTRA_CONF="${UPSTREAMS_LOCATION_PATH}/extra-conf"
STICKY_CONF="${UPSTREAMS_LOCATION_PATH}/sticky-conf"
DISCLIMER_LINE="#This config is auto-generated. DO NOT modify the weight property. If changing the rest of settings, please, remember that you are doing this at your own risk."

export $(grep UPSTREAM_KEEPALIVE /.jelenv 2>/dev/null | tr -d '"') >/dev/null
[ -z "${UPSTREAM_KEEPALIVE##*[!0-9]*}" ] && UPSTREAM_KEEPALIVE=100
UPSTREAM_KEEPALIVE=${UPSTREAM_KEEPALIVE:-100}
[ ! -f "$UPSTREAM_COMMON_CONFIG" ] && touch "$UPSTREAM_COMMON_CONFIG"

############ SERVICE FUNCTIONS ###########

writeActionLog=1

function toActionLog(){
  local message=$1
  if [ "$writeActionLog" -eq 1 ] ;then
    echo "$message" >> $ACTIONS_LOG
  fi
  log "$message"
}

function getSubnetById(){
  CURRENT_GROUP=1
  GROUP_ID=$1
  for NET in 10.{0..255}
  do
    for SUBNET in $NET.{0..255}
    do
      ### for IP in $SUBNET.{1..255..$SUBNET_STEP} - new construction not working in old bash3
      for ((IP=1;IP<=255;IP+=$SUBNET_STEP)) ### so old stupid way is used ...
      do
        if [ $CURRENT_GROUP -eq $GROUP_ID ]
        then
          echo $SUBNET.$IP
          return 0;
        else
          CURRENT_GROUP=$[CURRENT_GROUP+1]
        fi
      done
    done
  done
}

function generateMapRegexFromIp(){
  echo "~"$( $SED -e 's/\./\\\\\\./g' <<< $1 )"\\\\\\:80$"
}

function reloadConfigs(){
    cat "$NGINX_CONFIG_TEMPLATE" > "$NGINX_CONFIG"
    [ -f "$DHCPD_CONFIG_TEMPLATE" ] && cat "$DHCPD_CONFIG_TEMPLATE" > "$DHCPD_CONFIG"
    if [ -e "$LEASES_FILE" ]; then
        set -f
        rm -f "$LEASES_FILE";
        set +f
    fi
    toActionLog "- Templates are reloaded"
    return 0;
}

function clearHosts(){
    set -f
    rm -Rf "$UPSTREAMS_LOCATION_PATH"/group*
    set +f
    echo '' > "$UPSTREAMS_LOCATION_PATH"/common
    toActionLog "- Upstreams cleaned!"
    return 0;
}

function removeDublicates(){
  OLDIFS=$IFS
  IFS=$'\n'
  nginx_config=( $(cat "$NGINX_CONFIG") )

  for ((i=0; i < "${#nginx_config[@]}"; i++))
  do
		if [ "${nginx_config[${i}]}" != "${nginx_config[${i}+1]}" ]
		then
	 		echo "${nginx_config[${i}]}" >> $TMP_CONFIG
		fi
  done
  mv "$TMP_CONFIG" "$NGINX_CONFIG"
  IFS=$OLDIFS
}

function getAmountOfDhcpdLeases(){
  [  -f "$LEASES_FILE" ] && {
    AMOUNT_OF_DHCPD_LEASES=$($GREP -c "lease 10" $LEASES_FILE );
    echo $AMOUNT_OF_DHCPD_LEASES;
  } || { echo 0; }
}

function makeSureNodesReceivedIpsFromDhcpd(){
  let local accepted_result=$AMOUNT_OF_DHCPD_LEASES+2;
  for check in {1..15}
  do
    current_result=$(getAmountOfDhcpdLeases);
    [ $current_result == $accepted_result ] && { break; } || {
      sleep 3s;
      [ $check == 15 ] && { toActionLog "- Could not make sure that DHCP works correctly now"; };
    }
  done
}

######## MAIN FUNCTIONS #########

function addRangeToDhcpdConfig(){
  GROUP_CONFIG=$UPSTREAMS_LOCATION_PATH/$1
  if [ -e "$GROUP_CONFIG" ]; then
    AMOUNT_OF_HOSTS=$( wc -l < $GROUP_CONFIG )
    RANGE=$(createDhcpdRange $AMOUNT_OF_HOSTS)
    insertDhcpdRangeIntoConfig "$RANGE #$1"
    toActionLog "- DHCP range $RANGE is added to config";
  else
    toActionLog "- ${GROUP_CONFIG} not exists"
    return 11;
  fi
  return 0;
}

function generateUpstreams(){
  GROUP_CONFIG=$UPSTREAMS_LOCATION_PATH/$1
  extraConfContent=''
  if [ -e "$GROUP_CONFIG" ]; then
    count=1
    OLD_IFS=$IFS; IFS=$'\n';
    for i in  $( sort -u ${GROUP_CONFIG} )
    do
      local ip=$( $SED -nre 's/^([a-z0-9\.\-]{1,})([_| ](([a-z=0-9\ _]{1,})*))?/\1/p' <<< ${i} )
      local weight=$( $SED -nre 's/.*([a-z0-9\.\-\=]{1,}[_| ])+([weight=[0-9]{1,}).*/\2/p' <<< ${i} )
      local port=$( $SED -nre 's/.*([a-z0-9\.\-\=]{1,}[_| ])+port=([[0-9]{1,}).*/\2/p' <<< ${i} )
      [ "x$port" = "x80" ] && port="";
      [ -n "$port" ] && port=":${port}";
      [ "$2" == "tcp" ] && upstreams[$count]="server ${ip}:BPORT ;" || upstreams[$count]="server ${ip}${port} ${weight};"
      ((count++))
    done
    IFS=$OLD_IFS
    upcount=${#upstreams[@]}
    if [ -f "$NB_PATH" ]; then
      balancers=($(cat ${NB_PATH} | tr ',' ' ') )
      count=${#balancers[@]}
      for i in $(seq 0 $((${#balancers[@]} - 1))); do
        backups[$i]="server ${balancers[$i]} backup;"
      done
    fi

    if [ "$2" == "tcp" ]; then
      echo "${upstreams[@]} ### UPSTREAMPROTO for $1 ###"
    else
      [ ! -z "$WEIGHT_CHANGED_FLAG" ] && { [ "$WEIGHT_CHANGED_FLAG" -eq 1 ] && [ -f "$EXTRA_CONF" ] && extraConfContent=$( <${EXTRA_CONF} ); }
      echo "upstream $1 { ${backups[@]}  ${upstreams[@]} $extraConfContent sticky path=/; keepalive $UPSTREAM_KEEPALIVE; } ### UPSTREAMPROTO for $1 ###"
    fi
  else
    toActionLog "- ${GROUP_CONFIG} not exists" ;
    return 11;
  fi
  toActionLog "- upstreams generated"

return 0;
}

function generateDefaultUpstreamLine(){
  GROUP_CONFIG=$UPSTREAMS_LOCATION_PATH/$1
  extraConfContent=''
  if [ -e "$GROUP_CONFIG" ]; then

    count=0
    OLD_IFS=$IFS; IFS=$'\n';
    for i in  $(sort -u ${GROUP_CONFIG} )
    do
      upstreams[$count]="server ${i};"
      # upstreams[$count]="server $( $SED -nre 's/^([a-z0-9\.\-]{1,})([_| ](([a-z=0-9\ _]{1,})*))?/\1/p' <<< ${i});"
      ((count++))
    done
    IFS=$OLD_IFS

    if [ -f "$NB_PATH" ]; then
      balancers=( $(cat ${NB_PATH} | tr ',' ' ') )
      count=${#balancers[@]}
      for i in $(seq 0 $((${#balancers[@]} - 1))); do
        backups[$i]="server ${balancers[$i]} backup;"
      done
    fi

      [ -f "$EXTRA_CONF" ] && extraConfContent=$( <${EXTRA_CONF} );
    echo "${upstreams[@]} ${backups[@]} $extraConfContent ### DEFUPPROTO for $1 ###"
  else
    toActionLog "- ${GROUP_CONFIG} not exists" ;
    return 11;
  fi
}

function createDhcpdRange(){
  #argument is hosts amount in subnet
  AMOUNT_OF_HOSTS=$1
  SUBNET_ID=$( $GREP -n NEXT_SUBNET_ID $DHCPD_CONFIG | tail -n 1 | $AWK '{print $3}' )
  NEXT_SUBNET_START=$(getSubnetById $SUBNET_ID)
  ((SUBNET_ID++))
  $SED -i "7 c ### NEXT_SUBNET_ID $SUBNET_ID" $DHCPD_CONFIG
  LAST_SUBNET_IP=$(echo $NEXT_SUBNET_START | $AWK -F "." {'print $4'})
  let "LAST_SUBNET_IP +=$AMOUNT_OF_HOSTS-1"
  NEXT_SUBNET_END="$(echo $NEXT_SUBNET_START | $AWK -F "." '{print $1"."$2"."$3"."}')$LAST_SUBNET_IP"
  echo "range $NEXT_SUBNET_START $NEXT_SUBNET_END;"
}

function updateUpstreamSection(){
  UPSTREAM_LINE=$(echo -e "\\x09 $1")
  CHECK_LINE=$( $GREP UPSTREAMPROTO ${NGINX_CONFIG} | $GREP -w $2 | tail -n 1)
  if [[ $CHECK_LINE == *$2* ]]; then
    LINE=( $($GREP -n UPSTREAMPROTO ${NGINX_CONFIG} | $GREP -w $2 | tail -n 1 | $AWK -F: '{print $1}') )
    $SED -i  "$LINE c $UPSTREAM_LINE" $NGINX_CONFIG
  elif [[ $UPSTREAM_LINE == *server* ||  $WEIGHT_CHANGED_FLAG -eq 1 ]]; then
    LINE=( $( $GREP -n UPSTREAMPROTO $NGINX_CONFIG | tail -n 1 | $AWK -F: '{print $1}' ) )
    $SED -i "$LINE a $UPSTREAM_LINE" $NGINX_CONFIG
    [ ! -z "$WEIGHT_CHANGED_FLAG" ] && { [ "$WEIGHT_CHANGED_FLAG" -eq 1 ] && $SED -i "$LINE a $DISCLIMER_LINE" $NGINX_CONFIG ; }
  fi
}

function updateMappingsSection(){
  MAPPING_LINE=$(echo -e "\\x09 $1")
  CHECK_LINE=$( $GREP MAPPROTO ${NGINX_CONFIG} | $GREP -w $2 | tail -n 1)
  if [[ $CHECK_LINE == *$2* ]]; then
  	LINE=( $( $GREP -n MAPPROTO ${NGINX_CONFIG} | $GREP -w $2 | tail -n 1 | $AWK -F: '{print $1}') )
    $SED -i "$LINE c $MAPPING_LINE" $NGINX_CONFIG
  elif [[ $MAPPING_LINE == *:* ]]; then
  	LINE=( $( $GREP -n MAPPROTO $NGINX_CONFIG | tail -n 1 | $AWK -F: '{print $1}') )
    $SED -i "$LINE a $MAPPING_LINE" $NGINX_CONFIG
  fi
  $SED -i  "/DEFUPPROTO/!b;n;n;s/100/$UPSTREAM_KEEPALIVE/" $NGINX_CONFIG
}

function updateDefaultUpstreamSection(){
  UPSTREAM_LINE=$(echo -e "\\x09 $1")
  CHECK_LINE=$( $GREP DEFUPPROTO $NGINX_CONFIG | $GREP -w $2 | tail -n 1)
  if [[ $CHECK_LINE == *$2* ]]; then
    LINE=( $( $GREP -n DEFUPPROTO ${NGINX_CONFIG} | $GREP -w $2 | tail -n 1 | $AWK -F: '{print $1}') )
    $SED -i "$LINE c $UPSTREAM_LINE" $NGINX_CONFIG
  elif [[ $UPSTREAM_LINE == *server* ]]; then
    LINE=( $( $GREP -n DEFUPPROTO $NGINX_CONFIG | tail -n 1 | $AWK -F: '{print $1}') )
    $SED -i "$LINE a $UPSTREAM_LINE" $NGINX_CONFIG
    [ ! -z "$WEIGHT_CHANGED_FLAG" ] && { [ "$WEIGHT_CHANGED_FLAG" -eq 1 ] && $SED -i "$LINE a $DISCLIMER_LINE" $NGINX_CONFIG ; }
  fi
}

function insertDhcpdRangeIntoConfig(){
  LINE=( $( $GREP -n range $DHCPD_CONFIG | tail -n 1 | $AWK -F: '{print $1}') )
  RANGE_LINE=$(echo -e "\\x09 $1")
  if [[ $RANGE_LINE == *.* ]]; then
    $SED -i "$LINE a $RANGE_LINE" $DHCPD_CONFIG
  fi
}

function clearGroupFromNginxConfig(){
  $GREP -v -E "UPSTREAMPROTO.*$1|MAPPROTO.*$1|DEFUPPROTO.*$1" $NGINX_CONFIG  >  $TMP_CONFIG
  mv $TMP_CONFIG $NGINX_CONFIG
  toActionLog "- Group $1 is removed from config"
  return 0;
}

function removeRangeFromDhcpd(){
  $GREP -v $1 $DHCPD_CONFIG  >  $TMP_CONFIG
  mv $TMP_CONFIG $DHCPD_CONFIG
  toActionLog "- DHCP range $1 is removed from config"
  return 0;
}

function addGroupToUpsreamCommon(){
  UPSTREAMS=$(generateUpstreams common)
  updateUpstreamSection "$UPSTREAMS" common
  toActionLog "- Updated common upstream section with $UPSTREAMS"
  return 0;
}

function addGroupToUpsreamSection(){
  UPSTREAMS=$(generateUpstreams $1)
  updateUpstreamSection "$UPSTREAMS" $1
  toActionLog "- Updated group upstream section with $UPSTREAMS"
  return 0;
}

function addGroupToDefaultUpsreamSection(){
  UPSTREAMS=$(generateDefaultUpstreamLine $1)
  updateDefaultUpstreamSection "$UPSTREAMS" $1
  toActionLog "- Updated default upstream section with $UPSTREAMS"
  return 0;
}

function addGroupToMappingsSection(){
  MAPPINGS=$(generateMapRegexLine $1)
  updateMappingsSection "$MAPPINGS" $1
  toActionLog "- Updated group mappings section with $UPSTREAMS"
  return 0;
}

function generateMapRegexLine(){
  GROUP_CONFIG=$UPSTREAMS_LOCATION_PATH/$1
  if [ -e ${GROUP_CONFIG} ]; then
    count=0
    OLD_IFS=$IFS; IFS=$'\n';
    for i in  $(sort ${GROUP_CONFIG} | uniq )
    do
      local ip=$( $SED -nre 's/^([a-z0-9\.\-]{1,})([_| ](([a-z=0-9\ _]{1,})*))?/\1/p' <<< ${i} )
      regex_line=$(generateMapRegexFromIp ${ip})
      mappings[$count]="$regex_line $1;"
      ((count++))
    done
    IFS=$OLD_IFS

    echo "${mappings[@]} ### MAPPROTO for $1 ###"
  else
    toActionLog "- ${GROUP_CONFIG} not exists"
    return 11;
  fi
}


function moveIpsFromGroupToCommon(){
  GROUP_CONFIG=$UPSTREAMS_LOCATION_PATH/$1
  if [ -e "$GROUP_CONFIG" ]; then
  	exec 9<$GROUP_CONFIG
  	while read -u9 line
  	do
    		echo $line >> $UPSTREAM_COMMON_CONFIG
  	done
   else
    toActionLog "- ${GROUP_CONFIG} not exists"
    return 11;
  fi
}

function removeGroupIpsFromCommon(){
  GROUP_CONFIG=$UPSTREAMS_LOCATION_PATH/$1
  if [ -e "$GROUP_CONFIG" ]; then
  	exec 9<$GROUP_CONFIG
  	while read -u9 line
  	do
    		$GREP -v '$line' $UPSTREAM_COMMON_CONFIG  >  $TMP_CONFIG
    		mv $TMP_CONFIG $UPSTREAM_COMMON_CONFIG
  	done
  else
    toActionLog "- ${GROUP_CONFIG} not exists"
    return 11;
  fi
}

############ TCP Balancing functions #############

function updateTcpUpstreamSection(){
  fend_port=$1
  bend_port=$2
  TCP_UPSTREAM=$(generateUpstreams common tcp)
  $SED -i -e  "s/UPSTREAMPROTO/$TCP_UPSTREAM/g" -e "s/FPORT/$fend_port/g" -e "s/BPORT/$bend_port/g" $TMP_TEMPLATE;
}

function updateMainNginxConfig(){
  cat $TMP_TEMPLATE >> $NGINX_CONFIG;
  [ -e "$TMP_TEMPLATE" ] && rm $TMP_TEMPLATE;
}

function addTCPPortMappings(){
  if [ -e "$NGINX_TCPMAP_CONFIG" ]
  then
  	#cat $NGINX_TCPMAP_TEMPLATE > $TMP_TEMPLATE;
  	OLDIFS=$IFS
  	IFS=$'\n'
    if $GREP pair $NGINX_TCPMAP_CONFIG | $GREP -v '!--' -q ; then
    	    echo -e "\ntcp {\n">> $TMP_TEMPLATE;
    for line in $( $GREP pair $NGINX_TCPMAP_CONFIG | $GREP -v '!--' )
    do
        cat $NGINX_TCPMAP_TEMPLATE >> $TMP_TEMPLATE;
        fend_port=$($GREP "frontend_port=" <<<  $line | cut -d\" -f2)
        bend_port=$($GREP "backend_port=" <<< $line | cut -d\" -f4)
        IFS=$OLDIFS
        updateTcpUpstreamSection "$fend_port" "$bend_port"
        updateMainNginxConfig
    done
    echo     "}" >> $NGINX_CONFIG ;
    fi
  	IFS=$OLDIFS
  else
  	toActionLog "- mappings.xml not found"
  fi
}

function clearTCPPortMappings(){
  $SED -i '1,/TCP SECTION/!d' $NGINX_CONFIG
}

function addCommonHostConfig(){
  $GREP -q "^$1$" "$UPSTREAM_COMMON_CONFIG" || echo "$1" >> "$UPSTREAM_COMMON_CONFIG";
}

function removeCommonHostConfig	(){
  $SED -i "/^$1$/d"  "$UPSTREAM_COMMON_CONFIG" ;
}

function weightChanged(){
  NGINX_CONFIG_TEMPLATE="/etc/nginx/templates/nginx.conf.traf.distrib.tpl";
  if ! grep -q -E 'UPSTREAM' $NGINX_CONFIG ; then
    rebuildCommonTrafD;
    [ "$STICKY_NAME" ] && $SED -i "s/sticky/sticky name=${STICKY_NAME}/g" $NGINX_CONFIG ;
    reloadServiceSilent $SERVICE ;
    return $?;
  fi
  VERBOSE=1;
  if [ -e "$UPSTREAM_COMMON_CONFIG" ]; then
       count=1 ;
    OLD_IFS=$IFS; IFS=$'\n';
       for i in  $( sort -u ${UPSTREAM_COMMON_CONFIG} )
    do
	ip=$( $SED -nre 's/^([a-z0-9\.\-]{1,})([_| ](([a-z=0-9\ _]{1,})*))?/\1/p' <<< ${i} )
	port=$( $SED -nre 's/.*([a-z0-9\.\-\=]{1,}[_| ])+port=([[0-9]{1,}).*/\2/p' <<< ${i} )
	weight=$( $SED -nre 's/.*([a-z0-9\.\-\=]{1,}[_| ])+([weight=[0-9]{1,}).*/\2/p' <<< ${i} )
	backup=$( $SED -nre 's/.*([a-z0-9\.\-\=]{1,}[_| ])+(backup).*/\2/p' <<< ${i} )
	ip_a[$count]=$ip;
	[ "x$port" = "x80" ] && port="";
	[ -n "$port" ] && port=":${port}";
	[ "${weight#weight=}" == "0" ] && [ -n "${backup}" ] && weight=;
		[ -z ${weight#weight=} ] && [ -z "$backup" ] && {
        	log "For $ip value for weight not found" ;
        	toActionLog "For $ip value for weight not found" ;
        	return 0;
      	} || {
        weight_a[$count]=${weight#weight=} ;
        backup_a[$count]=${backup} ;
        port_a[$count]=${port} ;
      	}

      ((count++))
    done
    IFS=$OLD_IFS
  fi

  count=1 ;
  for c_ip in ${ip_a[@]}
  do
    OLD_VAL=$($SED '/^\s*#/d' ${NGINX_CONFIG} | tr '\n' ' ' | $SED -nre  "s/.*server\s+${c_ip}[^;.\n]*?weight=([0-9a-z ]{1,})[\s|\;| ].*/\1/p" )
    OLD_BACKUP=$(grep -o "${c_ip}${port_a[$count]}[a-z0-9= ]*;" ${NGINX_CONFIG} |grep -o "backup")
    [ -z "$OLD_VAL" ] && [ -z "$OLD_BACKUP" ] && {
      log "For $c_ip old value for weight not found" ;
      toActionLog "For $c_ip old value for weight not found" ;
      return 0;
    }
    old_val[$count]=$OLD_VAL ;
    old_backup[$count]=$OLD_BACKUP ;
    ((count++)) ;
  done

  for ((count=1; count <= ${#ip_a[@]}; count++))
  do
        log "IP: ${ip_a[$count]}${port_a[$count]}  WEIGHT: ${weight_a[$count]}  OLD: ${old_val[$count]} ${old_backup}"
        new_opts=;
        [ -z ${weight_a[$count]} ] && new_opts="${new_opts} ${backup_a[$count]}" || new_opts="${new_opts} weight=${weight_a[$count]} ${backup_a[$count]}"
        $SED -i -re "s/${ip_a[$count]}[a-z0-9:= ]*;/${ip_a[$count]}${port_a[$count]} ${new_opts};/" $NGINX_CONFIG ;
   done
  reloadServiceSilent $SERVICE ;

}

function swapHostsHTD(){
    VERBOSE=1
    local temp=$(getopt -o s:,d:,p: -l source:,destination:,port:  -- $@ 2>/dev/null );
    [[ $? != 0 ]] && die -q "Terminating...";
    eval set -- "$temp";
    while [ $# -gt 0 ]
    do
        case "$1" in
            -s | --source )
                HOST_SOURCE=$2;
                shift;
                ;;
	    -d | --destination )
                HOST_DESTINATION=$2;
                shift;
                ;;
	    -p | --port )
                PORT_DESTINATION=$2;
                shift;
                ;;
            --)
                shift;
                break;
                ;;
        esac;
        shift;
    done;
    [ -z "$HOST_SOURCE" ] && { writeJSONResponseErr "result=>4065" "message=>SOURCE required" ; return 65; }
    [ -z "$HOST_DESTINATION" ] && { writeJSONResponseErr "result=>4066" "message=>DESTINATION required" ; return 66; }

    log "Source: ${HOST_SOURCE}, Destination: ${HOST_DESTINATION}" ;
    [ -n "$PORT_DESTINATION" ] && { log "Port: ${PORT_DESTINATION}"; }

    if [ -n "$PORT_DESTINATION" ]; then
      [ ${PORT_DESTINATION} != "80" ] && PORT_SERVER=":${PORT_DESTINATION}";
      $SED -i -re "s/server\s*${HOST_SOURCE}\:?[[:digit:]]+?(\s)/server ${HOST_DESTINATION}${PORT_SERVER}\1/" $NGINX_CONFIG;
    else
      $SED -i -re "s/server\s*${HOST_SOURCE}([ |:])/server ${HOST_DESTINATION}\1/" $NGINX_CONFIG;
    fi

#    MAP_SOURCE=$( $SED -e 's/\./\\\\./g' <<< $HOST_SOURCE )
#    MAP_DESTINATION=$( $SED -e 's/\./\\\\./g' <<< $HOST_DESTINATION )
#    $SED -i -re "s/${MAP_SOURCE}/${MAP_DESTINATION}/" $NGINX_CONFIG;

    $SED -i -re "s/^\s*${HOST_SOURCE}\s*/${HOST_DESTINATION} /" ${UPSTREAM_COMMON_CONFIG} ;
    if [ -n "$PORT_DESTINATION" ]; then
      [ ${PORT_DESTINATION} != "80" ] && PORT_COMMON=" port=${PORT_DESTINATION}";
      if $GREP -qE "${HOST_DESTINATION}.*[[:space:]]port=" ${UPSTREAM_COMMON_CONFIG} ; then
        $SED -i "/${HOST_DESTINATION}/{s/[[:space:]]port=[[:digit:]]\+/${PORT_COMMON}/}" ${UPSTREAM_COMMON_CONFIG};
      else
        $SED -i "/${HOST_DESTINATION}/{s/${HOST_DESTINATION}/${HOST_DESTINATION}${PORT_COMMON}/}" ${UPSTREAM_COMMON_CONFIG};
      fi
    fi
    writeJSONResponseOut "result=>0" "message=>Ok";
}

function removeHostHTD(){
    VERBOSE=1
    local temp=$(getopt -o h: -l host:  -- $@ 2>/dev/null );
    [[ $? != 0 ]] && die -q "Terminating...";
    eval set -- "$temp";
    while [ $# -gt 0 ]
    do
        case "$1" in
            -h | --host)
                HOST_REMOVE=$2;
                shift;
                ;;
            --)
                shift;
                break;
                ;;
        esac;
        shift;
    done;
    [ -z "$HOST_REMOVE" ] && { writeJSONResponseErr "result=>4065" "message=>SOURCE required" ; return 65; }
    log "Host: ${HOST_REMOVE}" ;
    MAP_HREMOVE=$( $SED -e 's/\./\\\\./g' <<< $HOST_REMOVE )
    $PERL -i -p0e "s/server\s${HOST_REMOVE}:?([0-9]+)?\s*[:a-z0-9\=]{0,}\s*;//g" $NGINX_CONFIG;
    $PERL -i -p0e "s/~${MAP_HREMOVE}\\\\:[0-9]+\\$\s*common;//g" $NGINX_CONFIG;

    $SED -i "/${HOST_REMOVE}\s*/d" ${UPSTREAM_COMMON_CONFIG} ;
    writeJSONResponseOut "result=>0" "message=>Ok";
}

function addNodeHTD(){
    VERBOSE=1
    local temp=$(getopt -o h:,p: -l host:,port:  -- $@ 2>/dev/null );
    [[ $? != 0 ]] && die -q "Terminating...";
    eval set -- "$temp";
    while [ $# -gt 0 ]
    do
        case "$1" in
            -h | --host)
                HOST_ADD=$2;
                shift;
                ;;
            -p | --port)
                PORT_ADD=$2;
                shift;
                ;;
            --)
                shift;
                break;
                ;;
        esac;
        shift;
    done;
    [ -z "$HOST_ADD" ] && { writeJSONResponseErr "result=>4065" "message=>SOURCE required" ; return 65; }
    log "Host: ${HOST_ADD}" ;
    [ -n "$PORT_ADD" ] && { log "Port: ${PORT_ADD}" ; }
    if $GREP -vE "^\s*#" $NGINX_CONFIG | $GREP -q ${HOST_ADD} ; then
        writeJSONResponseOut "result=>0" "message=>Ok";
        return 0 ;
    fi

    regex_line=$(generateMapRegexFromIp ${HOST_ADD})
    if [ -n "$PORT_ADD" ] && [ "${PORT_ADD}" != "80" ] ; then
      $SED -i "/### UPSTREAMPROTO for common ###/{s/}\s*###/server ${HOST_ADD}:${PORT_ADD} weight=1; } ###/}"  $NGINX_CONFIG;
    else
      $SED -i "/### UPSTREAMPROTO for common ###/{s/}\s*###/server ${HOST_ADD} weight=1; } ###/}"  $NGINX_CONFIG;
    fi
    OLDIFS=$IFS
    IFS=$'\n'
    for line in $( $GREP pair $NGINX_TCPMAP_CONFIG  | $GREP -v '!--' )
    do
    	cat $NGINX_TCPMAP_TEMPLATE > $TMP_TEMPLATE;
      fend_port=$($GREP "frontend_port=" <<<  $line | cut -d\" -f2)
      bend_port=$($GREP "backend_port=" <<< $line | cut -d\" -f4)
      IFS=$OLDIFS
	    LINE=$( $GREP -n "listen ${fend_port}"  ${NGINX_CONFIG} | tail -n 1 | $AWK -F: '{print $1}' ) ;
      $SED -i -re  "${LINE},/### UPSTREAMPROTO/{s/###/server ${HOST_ADD}:${bend_port}\; ###/}" $NGINX_CONFIG;
    done
    IFS=$OLDIFS
    if [ -n "$PORT_ADD" ] && [ "${PORT_ADD}" != "80" ] ; then
      echo "${HOST_ADD} port=${PORT_ADD} weight=1" >> $UPSTREAM_COMMON_CONFIG ;
    else
      echo "${HOST_ADD} weight=1" >> $UPSTREAM_COMMON_CONFIG ;
    fi
    writeJSONResponseOut "result=>0" "message=>Ok";
}

function removeStickyHTD(){
  [ ! -e ${STICKY_CONF} ] && { touch ${STICKY_CONF}; chmod 666 ${STICKY_CONF}; }
  perl -i -p0e "s/sticky\s*[:a-zA-Z0-9\s\/\=\.\_]{0,}\s*;//g" $NGINX_CONFIG;
  writeJSONResponseOut "result=>0" "message=>Ok";
}

function addNginxSticky(){
  [ ! -e ${STICKY_CONF} ] && { touch ${STICKY_CONF}; chmod 666 ${STICKY_CONF}; }
  if $GREP -vE "^\s*#" $NGINX_CONFIG | $GREP -q 'sticky' ; then
    writeJSONResponseOut "result=>0" "message=>Ok";
    return 0 ;
  fi

  if [ -f ${STICKY_CONF} -a -n "$(cat ${STICKY_CONF})" ]; then
        STICKY_STRING=$(cat ${STICKY_CONF} |sed -e 's|/|\\/|g' -e 's/"/\\"/g' );
        [ -z "$(grep "name=" ${STICKY_CONF})" ] && STICKY_STRING="name=${STICKY_NAME} ${STICKY_STRING}";
        $SED -i -re "/### UPSTREAMPROTO for common ###/{s/(keepalive.*)?}\s*###/sticky ${STICKY_STRING}; \1} ###/}" $NGINX_CONFIG;
        $SED -i  "/### DEFUPPROTO for common ###/{s/###/sticky ${STICKY_STRING}; ###/}"  $NGINX_CONFIG;
  else

        [ "$STICKY_NAME" ] && ADD_NAME="name=${STICKY_NAME}" ;
        $SED -i -re "/### UPSTREAMPROTO for common ###/{s/(keepalive.*)}\s*###/sticky ${ADD_NAME} path=\/; \1} ###/}" $NGINX_CONFIG;
        $SED -i  "/### DEFUPPROTO for common ###/{s/###/sticky ${ADD_NAME} path=\/; ###/}"  $NGINX_CONFIG;
        writeJSONResponseOut "result=>0" "message=>Ok";
  fi
}

function swapNginxProxyProtocol(){
  protocol=$1
  if [ "x${protocol}" == "xhttps" ]; then
        $SED -i -re "/(^|\s)proxy_pass\s/s/proxy_pass\s+http:\/\//proxy_pass https:\/\//" $NGINX_CONFIG;
        [ -f "$NGINX_SSL_CONFIG" ] && \
          $SED -i -re "/(^|\s)proxy_pass\s/s/proxy_pass\s+http:\/\//proxy_pass https:\/\//" $NGINX_SSL_CONFIG;
        [ -f "$NGINX_SSL_CONFIG.disabled" ] && \
          $SED -i -re "/(^|\s)proxy_pass\s/s/proxy_pass\s+http:\/\//proxy_pass https:\/\//" "$NGINX_SSL_CONFIG.disabled";
  else
        $SED -i -re "/(^|\s)proxy_pass\s/s/proxy_pass\s+https:\/\//proxy_pass http:\/\//" $NGINX_CONFIG;
        [ -f "$NGINX_SSL_CONFIG" ] && \
          $SED -i -re "/(^|\s)proxy_pass\s/s/proxy_pass\s+https:\/\//proxy_pass http:\/\//" $NGINX_SSL_CONFIG;
        [ -f "$NGINX_SSL_CONFIG.disabled" ] && \
          $SED -i -re "/(^|\s)proxy_pass\s/s/proxy_pass\s+https:\/\//proxy_pass http:\/\//" "$NGINX_SSL_CONFIG.disabled";
  fi
  writeJSONResponseOut "result=>0" "message=>Ok";
}

function setNginxProxyTimeout(){
    VERBOSE=1
    local temp=$(getopt -o t: -l timeout: -- $@ 2>/dev/null );
    [[ $? != 0 ]] && die -q "Terminating...";
    eval set -- "$temp";
    while [ $# -gt 0 ]
    do
        case "$1" in
            -t | --timeout)
                TIMEOUT_SET=$2;
                shift;
                ;;
            --)
                shift;
                break;
                ;;
        esac;
        shift;
    done;
    [ -z "$TIMEOUT_SET" ] && { writeJSONResponseErr "result=>4099" "message=>Missing param" ; return 99; }
    log "Timeout: ${TIMEOUT_SET}" ;

    $SED -i -re "/(^|\s)proxy_connect_timeout\s/s/proxy_connect_timeout\s+[0-9]+s/proxy_connect_timeout ${TIMEOUT_SET}s/" $NGINX_CONFIG;
    writeJSONResponseOut "result=>0" "message=>Ok";
}

function setConfigVariables(){
  local temp=`getopt -o -n: -l stickyname: -- "$@" 2>>"$JEM_CALLS_LOG"`
  [[ $? != 0 ]] && die -q "Terminating...";
  eval set -- "$temp"
  while true ; do
    case "${1}" in
      -n | --stickyname )
        shift;
        STICKY_NAME="$1";
        shift;
        ;;
      --)
        shift;
        break;
        ;;
    esac;
  done;

  [ ! -z "$STICKY_NAME" ] && {
    if $GREP -q "STICKY_NAME" ${META_FILE} ; then
      $SED -i -re "s/STICKY_NAME\s*=.*/STICKY_NAME=${STICKY_NAME}/g" ${META_FILE};
    else
      echo -e "\nSTICKY_NAME=${STICKY_NAME}" >> ${META_FILE};
    fi;
  }
  $SED -i -re '/^$/d' ${META_FILE};
  return 0;
}

function setSSLforLB(){
    [ -f "$NGINX_SSL_CONFIG" ] && {
	$SED -i -re 's/^\s*include\s*conf.d\/ssl.upstreams.inc/#include conf.d\/ssl.upstreams.inc/' $NGINX_SSL_CONFIG ;
	grep -q 'set $group common;' $NGINX_SSL_CONFIG ||  sed -i -e '/SRVGROUP/i\\t\tset \$group common;' $NGINX_SSL_CONFIG;
    }
    [ -f "${NGINX_SSL_CONFIG}.disabled" ] && {
        $SED -i -re 's/^\s*include\s*conf.d\/ssl.upstreams.inc/#include conf.d\/ssl.upstreams.inc/' "${NGINX_SSL_CONFIG}.disabled" ;
        grep -q 'set $group common;' "${NGINX_SSL_CONFIG}.disabled" ||  sed -i -e '/SRVGROUP/i\\t\tset \$group common;' "${NGINX_SSL_CONFIG}.disabled";
    }
}

function unsetSSLforLB(){
    [ -f "$NGINX_SSL_CONFIG" ] &&
        $SED -i -re 's/^\s*#\s*include\s*conf.d\/ssl.upstreams.inc/include conf.d\/ssl.upstreams.inc/'  $NGINX_SSL_CONFIG ;
    [ -f "${NGINX_SSL_CONFIG}.disabled" ] &&
        $SED -i -re 's/^\s*#\s*include\s*conf.d\/ssl.upstreams.inc/include conf.d\/ssl.upstreams.inc/' "${NGINX_SSL_CONFIG}.disabled" ;
    return 0;
}

function applyNoBackendsConfig(){
    if [ "$(cat "$UPSTREAM_COMMON_CONFIG" | wc -l)" == "0" ]; then
       if [ -f "${NGINX_NOBACKENDS_CONFIG_TEMPLATE}" ]; then
           cp -f "${NGINX_NOBACKENDS_CONFIG_TEMPLATE}" "${NGINX_CONFIG}";
       fi
    fi
}

function rebuildCommon(){
    if ! grep -q -E 'upstream\s+default_upstream' "$NGINX_CONFIG" ; then
        reloadConfigs;
    fi
    clearGroupFromNginxConfig common && \
    clearTCPPortMappings && \
    addGroupToUpsreamCommon && \
    addGroupToDefaultUpsreamSection common && \
    addGroupToMappingsSection common && \
    addTCPPortMappings && \
    unsetSSLforLB && \
    applyNoBackendsConfig && \
    local RETVAL="0" || local RETVAL="1" ;
    { reloadServiceSilent $SERVICE || restartServiceSilent $SERVICE; }
    enableService $SERVICE > /dev/null 2>&1
    return ${RETVAL}
}

function rebuildCommonTrafD(){
  NGINX_CONFIG_TEMPLATE="/etc/nginx/templates/nginx.conf.traf.distrib.tpl";
  reloadConfigs;
  clearGroupFromNginxConfig common && \
  clearTCPPortMappings && \
  addGroupToUpsreamCommon && \
  addTCPPortMappings && \
  enableService $SERVICE > /dev/null 2>&1 && \
  setSSLforLB && \
  reloadServiceSilent $SERVICE && \
  return 0 || return 1;
}

function addHostToGroup(){
    toActionLog "- Add host $host to group $group";
    echo "$host" >> "${UPSTREAMS_LOCATION_PATH}/${group}";
}

function buildCluster(){
    local groupname="$1";
    if ! $GREP -q upstream "$NGINX_CONFIG" ; then
      reloadConfigs;
    fi
    clearTCPPortMappings && \
    addGroupToMappingsSection common && \
    addGroupToUpsreamSection "$groupname" && \
    addGroupToMappingsSection "$groupname" && \
    addGroupToDefaultUpsreamSection "$groupname" && \
    addRangeToDhcpdConfig "$groupname" && \
    clearGroupFromNginxConfig common && \
    removeGroupIpsFromCommon "$groupname" && \
    result=0 || result=1 ;
    if [ "$result" -eq 0 ] ; then
      toActionLog "- Group "$groupname" is added to cluster"
    else
      toActionLog "- Error was while adding group"
    fi
    removeDublicates;
    restartServiceSilent $SERVICE;
    enableService $SERVICE > /dev/null 2>&1;
    toActionLog "- amount of dhcp leases $(getAmountOfDhcpdLeases)";
    restartServiceSilent dhcpd;
    makeSureNodesReceivedIpsFromDhcpd;
    return $result;
}

function unbuildCluster(){
    local groupname="$1";
    [ ! -f "$UPSTREAMS_LOCATION_PATH/$groupname" ] && { return 0; }
    moveIpsFromGroupToCommon "$groupname" && \
    clearGroupFromNginxConfig "$groupname" && \
    removeRangeFromDhcpd "$groupname" && \
    clearGroupFromNginxConfig common && \
    rm "$UPSTREAMS_LOCATION_PATH/$groupname" && \
    result=0 || result=1 ;
    toActionLog "- Group $groupname is removed from cluster and added to common list"
    removeDublicates;
    reloadServiceSilent $SERVICE;
    restartServiceSilent dhcpd;
    return $result;
}


function rebuildTCPPortMappings(){
  clearTCPPortMappings
  addTCPPortMappings
}

