#!/bin/bash
#----------------------------------------------------------------------
# NGCP logs parser
#----------------------------------------------------------------------
set -e
set -u


# Constants
config_file='/etc/ngcp-logs/ngcp-logs.conf'
ngcp_logs_dir='/var/log/ngcp/'
ngcp_old_logs_dir="${ngcp_logs_dir}/old/"
ngcp_roles_file='/etc/default/ngcp-roles'
min_length=4
opts_format='h\?d:f:t:n:l:ice'
return_code=0
ngcp_hostname="$(ngcp-hostname)"

proxy='kamailio-proxy'
lb='kamailio-lb'
sems='sems-b2b'
rtpengine='rtp'
b2b='sems-b2b'
api='api'
panel='panel'

_opt_string=false
_opt_date=false
_opt_from=false
_opt_to=false
_opt_node=()
_opt_all=false
_opt_local=false
_opt_inactive=false
_opt_case=false
_opt_extended_search=false
_opt_log=()

#RED='\033[0;31m'
GREEN='\033[1;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color


# Reading configuration from config file
if [ -n "$config_file" ] && [ -r "$config_file" ] ; then
  # shellcheck disable=SC1090
  . $config_file
fi


# Reading information from ngcp-roles
if ! [ -r "${ngcp_roles_file}" ] ; then
  echo -e "Error: can't read file ${ngcp_roles_file} !" >&2
  exit 1
fi

# shellcheck disable=SC1090
. "${ngcp_roles_file}"

if [ -z "${NGCP_TYPE}" ] ; then
  echo -e "Error: missing NGCP_TYPE in ${ngcp_roles_file}, cannot continue!" >&2
  exit 1
fi

if [ -z "${NGCP_IS_PROXY}" ] ; then
  echo -e "Error: missing NGCP_IS_PROXY in ${ngcp_roles_file}, cannot continue!" >&2
  exit 1
fi

if [ -z "${NGCP_IS_LB}" ] ; then
  echo -e "Error: missing NGCP_IS_LB in ${ngcp_roles_file}, cannot continue!" >&2
  exit 1
fi


# helper function
usage() {
  echo -e "$0 - Search a string in NGCP logs. Useful to retrieve kamailio and sems logs given a call-id.

  Usage: $0 [OPTION...] <string>

    -h,   Display this help text and exit program
    -l,   Log to grep: proxy|lb|sems|b2b|pbx|rtp|api|panel (default: proxy)
    -n,   Node name of the nodes where to grep: prx01a|lb01a|... (default: local node). Use 'all' to grep in all the nodes
    -i,   Search the string also on inactive nodes (default disabled)
    -d,   Exact date of log in the format YYYYMMDD (default today)
    -f,   From date of the log in the format YYYYMMDD (not in use at the moment)
    -t,   From date of the log in the format YYYYMMDD (not in use at the moment)
    -c,   Ignore case distinctions (attention: it could increase the required grep time)
    -e,   Extend the search to log file of the day if no -d option is used and to the following day if -d option is used

  Note: use the '+' separator if you want to specify multiple nodes or logs (for example prx01a+lb01a for nodes or proxy+lb+rtp for logs).
        Or repeat the same option more then one time (for example -n prx01a -n lb01a for nodes or -l proxy -l lb -l rtp for logs).
  "
}


# cmdline handling
if [ $# = 0 ]; then
  echo -e "Try '$0 -h' for more information" >&2
  exit 1
fi

_options=""


while getopts "$opts_format" OPT
do
  case $OPT in
    h|\?)
      usage
      exit 0
      ;;

    d)
      if [[ $OPTARG =~ ^[0-9]{8}$ ]] ; then
        _opt_date=$OPTARG
      else
        echo -e "Please insert a valid date in the form YYYYMMDD" >&2
        exit 1
      fi
      ;;

    f)
      if [[ $OPTARG =~ ^[0-9]{8}$ ]] ; then
        _opt_from=$OPTARG
        _options="${_options} -f $$OPTARG"
      else
        echo -e "Please insert a valid from date in the form YYYYMMDD" >&2
        exit 1
      fi
      ;;

    t)
      if [[ $OPTARG =~ ^[0-9]{8}$ ]] ; then
        _opt_to=$OPTARG
        _options="${_options} -t $OPTARG"
      else
        echo -e "Please insert a valid to date in the form YYYYMMDD" >&2
        exit 1
      fi
      ;;

    n)
      OLDIFS=$IFS
      IFS='+'
      for node in $OPTARG ; do
        if [[ $node =~ ^all$ ]] ; then
          _opt_all=true
          _opt_local=true
        elif [[ $node =~ ^sp[1-2]$|^prx[0-9]{2}[ab]?$|^prx$|^lb[0-9]{2}[ab]?$|^lb$|^slb[0-9]{2}[ab]?$|^slb$|^web[0-9]{2}[ab]?$|^web$|^db[0-9]{2}[ab]?$|^db$ ]] ; then
          eval "_opt_node[${#_opt_node[@]}]=$node"
        else
          echo -e "Node name '$node' not allowed" >&2
          exit 1
        fi
      done
      IFS=$OLDIFS
      if ! $_opt_all && [ ${#_opt_node[@]} -eq 0 ] ; then
        echo -e "Please insert the target node(s)" >&2
        exit 1;
      fi
      ;;

    i)
      _opt_inactive=true
      _options="${_options} -i"
      ;;

    c)
      _opt_case=true
      _options="${_options} -c"
      ;;

    l)
      OLDIFS=$IFS
      IFS='+'
      for log in $OPTARG ; do
        if [[ $log =~ ^proxy$|^p$|^lb$|^l$|^sems$|^s$|^b2b$|^b$|^pbx$|^x$|^rtp$|^r$|^api$|^a$|^panel$|^n$ ]] ; then
          eval "_opt_log[${#_opt_log[@]}]=$log"
        else
          echo -e "Log name '$log' not allowed" >&2
          exit 1
        fi
      done
      _options="${_options} -l $OPTARG"
      IFS=$OLDIFS
      if [ ${#_opt_log[@]} -eq 0 ] ; then
        echo -e "Please insert at least one log among proxy|lb|sems|b2b|rtp|api|panel" >&2
        exit 1;
      fi
      ;;

    e)
      _opt_extended_search=true
      _options="${_options} -e"
      ;;

  esac
done

shift $((OPTIND - 1))
if [ $# -gt 0 ] ; then
  _opt_string=$1
else
  echo -e "Please insert a string to search" >&2
  exit 1
fi


#======================================================================
main()
{
  # String validation
  if [ "${_opt_string}" = false ] ; then
    echo -e "Please insert a string to search" >&2
    exit 1
  fi

  if [ "${#_opt_string}" -lt "${min_length}" ] ; then
    echo -e "String '${_opt_string}' too short. Insert at least ${min_length} characters" >&2
    exit 1
  fi

  # If node list is empty, use the default (localhost)
  if [ ${#_opt_node[@]} -lt 1 ] ; then
    _opt_local=true
  fi

  # If log list is empty, use the default (proxy)
  if [ ${#_opt_log[@]} -lt 1 ] ; then
    _opt_log=(proxy)
  fi


  declare -a script script2

  # Check if the date is today or false (look in the ngcp_logs_dir folder with grep)
  #  or if it is a previous day (look in the ngcp_old_logs_dir folder with zgrep)
  today=$(date +"%Y%m%d")
  if [ "${_opt_date}" = false ] || [ "$today" = "${_opt_date}" ] ; then
    script=("grep")
    log_folder="${ngcp_logs_dir}"
    log_date=".log"
    log_date_long=".log-${today}-*.log"
    _opt_date="${today}"

    script2=("zgrep")
    log_date_long2=".log-${today}-*.gz"
  else
    if [ "$today" -lt "${_opt_date}" ] ; then
      echo -e "Date in the future, please check the provided argument" >&2
      exit 1
    fi
    script=("zgrep")
    log_folder="${ngcp_old_logs_dir}"
    log_date=".log-${_opt_date}.gz"
    log_date_long=".log-${_opt_date}-*.gz"

    script2=("zgrep")
    tomorrow=$(date --date="${_opt_date} + 1 day" +"%Y%m%d")
    log_date_long2=".log-${tomorrow}-*.gz"
  fi

  #TODO: get the call date from the CDRs in mysql (mysql -e "select FROM_UNIXTIME(init_time) from accounting.cdr where call_id = '_opt_string';")  ---> check mysql injection

  if [ "${_opt_from}" != false ] && [ "$today" -lt "${_opt_from}" ] ; then
    echo -e "From date in the future, please check the provided argument" >&2
    exit 1
  fi

  if [ "${_opt_to}" != false ] && [ "$today" -lt "${_opt_to}" ] ; then
    echo -e "To date in the future, please check the provided argument" >&2
    exit 1
  fi
  #TODO: Use from and to date to search inside the logs


  # Check if ignore case distinctions is active or not
  if "$_opt_case" ; then
    script+=("-i")
  fi


  # If --all option specified the script need to use parallel ssh on the other nodes
  if "$_opt_all" || [ ${#_opt_node[@]} -gt 0 ] ; then

    if [ -z "${NGCP_NEIGHBOURS_PSSH:-}" ] ; then
      echo -e "Warning: \$NGCP_NEIGHBOURS_PSSH is not defined, checking local node only!" >&2

    else
      if "$_opt_all" ; then
        if ! parallel-ssh -H"${NGCP_NEIGHBOURS_PSSH}" -i "nice -n 19 ionice -c 3 $0 -d ${_opt_date} ${_options} ${_opt_string}" ; then
          return_code=1
        fi

      else
        # need to filter the hosts
        node_list=""
        for node in "${_opt_node[@]}"; do

          # If node is the localhost
          if [[ "${node}" = "${ngcp_hostname}" ]] ; then
            _opt_local=true
            continue
          fi

          for ssh_node in $(echo "${NGCP_NEIGHBOURS_PSSH}" | tr " " "\n"); do
            if [[ $ssh_node =~ ^$node.* ]] ; then
              node_list="${node_list} ${ssh_node}"
            fi
          done
        done

        if [ ${#node_list} -gt 0 ] ; then
          if ! parallel-ssh -H"${node_list}" -i "nice -n 19 ionice -c 3 $0 -d ${_opt_date} ${_options} ${_opt_string}" ; then
            return_code=1
          fi
        fi
      fi
    fi
  fi


  # Check if the grep should be performed on this node
  if ! "$_opt_local" ; then
    exit 0
  fi


  echo -e "\n${GREEN}************************ ${ngcp_hostname} ************************${NC}"


  # If the local node inactive skip it unless "_opt_inactive" is true
  if ! /usr/sbin/ngcp-check-active && ! "$_opt_inactive" ; then
    echo -e "\nNode skipped because inactive\n"
    exit 0
  fi

  parse_log

  echo -e "\n"
}

#----------------------------------------------------------------------
# Function to check the existence of the file and to execute the grep
grep_exec () {

  long_format_files=0
  if [ ! -f "$log_file" ] ; then
    dirtofind=$(dirname "$log_file_long")
    filestofind=$(set -f && basename "$log_file_long" && set +f)
    num_longfiles=$(set -f; find "${dirtofind}" -name "$filestofind" -type f | wc -l ; set +f)
    if [ "$num_longfiles" -eq 0 ]; then
      echo -e "The file '$log_file' does not exist"
      echo "No long log files found at $log_file_long"
    else
      long_format_files=1
    fi
  fi

  if [ "$_opt_extended_search" = true ]; then
    ex_command2=""
    if [ -n "${script2[*]}" ]; then
      dirtofind_extra=$(dirname "$log_file_long2")
      filestofind_extra=$(set -f && basename "$log_file_long2" && set +f)
      num_extra_files=$(set -f; find "${dirtofind_extra}" -name "$filestofind_extra" -type f | wc -l ; set +f)
      if [ "$num_extra_files" -gt 0 ]; then
        ex_command2=("${script2[@]}" -s "${_opt_string}" "${log_file_long2}")
      fi
    fi
  fi
  if [ $long_format_files -eq 0 ]; then
    ex_command=("${script[@]}" "${_opt_string}" "${log_file}")
  else
    read -r -a files_list < <(find "${dirtofind}" -name "$filestofind" -type f)
    ex_command=("${script[@]}" "${_opt_string}" "${files_list[@]}")
  fi
  echo -e "Executing the command: ${ex_command[*]}"
  if ! "${ex_command[@]}" ; then
    echo -e "No records found"
  fi

  # execute extra search if user provided -e option
  if [ "$_opt_extended_search" = true ]; then
    if [ -n "${ex_command2[*]}" ]; then
      echo -e "Executing extra command to check old files: ${ex_command2[*]}"
      if ! "${ex_command2[@]}" ; then
        echo -e "No records found in extra command"
      fi
    fi
  fi

  return 0
}

#----------------------------------------------------------------------
# Function to parse log files
parse_log()
{
  for log_file in "${_opt_log[@]}"; do
    case "${log_file}" in
      lb|l)
        # >>> Kamailio LB Log
        if [ "${NGCP_IS_LB}" == "yes" ] ; then
          echo -e "\n${YELLOW}${ngcp_hostname} >>> LB LOG${NC}"
          log_file="${log_folder}${lb}${log_date}"
          log_file_long="${log_folder}${lb}${log_date_long}"
          log_file_long2="${ngcp_old_logs_dir}${lb}${log_date_long2}"
          grep_exec
        fi
        ;;
      proxy|p)
        # >>> Kamailio PRX Log
        if [ "${NGCP_IS_PROXY}" == "yes" ] ; then
          echo -e "\n${YELLOW}${ngcp_hostname} >>> PROXY LOG${NC}"
          log_file="${log_folder}${proxy}${log_date}"
          log_file_long="${log_folder}${proxy}${log_date_long}"
          log_file_long2="${ngcp_old_logs_dir}${proxy}${log_date_long2}"
          grep_exec
        fi
        ;;
      sems|s)
        # >>> Sems Log
        if [ "${NGCP_IS_PROXY}" == "yes" ] ; then
          echo -e "\n${YELLOW}${ngcp_hostname} >>> SEMS LOG${NC}"
          log_file="${log_folder}${sems}${log_date}"
          log_file_long="${log_folder}${sems}${log_date_long}"
          log_file_long2="${ngcp_old_logs_dir}${sems}${log_date_long2}"
          grep_exec
        fi
        ;;
      b2b|b|pbx|x)
        # >>> Sems-B2B Log
        if [ "${NGCP_IS_PROXY}" == "yes" ] ; then
          echo -e "\n${YELLOW}${ngcp_hostname} >>> B2B LOG${NC}"
          log_file="${log_folder}${b2b}${log_date}"
          log_file_long="${log_folder}${b2b}${log_date_long}"
          log_file_long2="${ngcp_old_logs_dir}${b2b}${log_date_long2}"
          grep_exec
        fi
        ;;
      rtp|r)
        # >>> RtpEngine Log
        if [ "${NGCP_IS_RTP}" == "yes" ] ; then
          echo -e "\n${YELLOW}${ngcp_hostname} >>> RTP LOG${NC}"
          log_file="${log_folder}${rtpengine}${log_date}"
          log_file_long="${log_folder}${rtpengine}${log_date_long}"
          log_file_long2="${ngcp_old_logs_dir}${rtpengine}${log_date_long2}"
          grep_exec
        fi
        ;;
      api|a)
        # >>> Api Log
        if [ "${NGCP_IS_MGMT}" == "yes" ] ; then
          echo -e "\n${YELLOW}${ngcp_hostname} >>> API LOG${NC}"
          log_file="${log_folder}${api}${log_date}"
          log_file_long="${log_folder}${api}${log_date_long}"
          log_file_long2="${ngcp_old_logs_dir}${api}${log_date_long2}"
          grep_exec
        fi
        ;;
      panel|n)
        # >>> Panel Log
        if [ "${NGCP_IS_MGMT}" == "yes" ] ; then
          echo -e "\n${YELLOW}${ngcp_hostname} >>> PANEL LOG${NC}"
          log_file="${log_folder}${panel}${log_date}"
          log_file_long="${log_folder}${panel}${log_date_long}"
          log_file_long2="${ngcp_old_logs_dir}${panel}${log_date_long2}"
          grep_exec
        fi
        ;;
    esac
  done
}

#----------------------------------------------------------------------

main
exit "${return_code}"
