#!/bin/bash
######################################################################
#
#  Name: boomINDISEXP
#
#  Description: Export active/closed indications details via boom server REST API. 
#               Provide semicolon separated output of 32 indication attributes 
#               written to $OUTFILE.
#
#
#  Author: Solmsdorf (equensWorldline)
#  Version: 1.00
#  Date: 13.12.2023
#
#  Input parameter: $1: i=ac|cl
#                  [$2: h=<host> | a=<appl> | g=<group> | o=<object> | t=<text>]
#                  [$3: h=<host> | a=<appl> | g=<group> | o=<object> | t=<text>]
#                  [$4: h=<host> | a=<appl> | g=<group> | o=<object> | t=<text>]
#                  [$5: h=<host> | a=<appl> | g=<group> | o=<object> | t=<text>]
#                  [$6: h=<host> | a=<appl> | g=<group> | o=<object> | t=<text>]
#
#  Return: 0 = Success
#          0 = Warning (filtered closed Indication not found)
#          1 = Error (>>curl -k ${SRV_URL}:${SRV_PORT}/json/fullindi_list?search.value=ID-3-${id}<< failed (response ${stat})!)
#          2 = Error (>>curl -k ${SRV_URL}:${SRV_PORT}/json/fullindi_list?search.value=ID-3-${id}<< failed (size not available)!)
#
#
#  Note: requires boom user relating configuration inside boom_env_ssl.cnf
#
#  Indication Attributes Layout (example call /json/fullindi.lst):
#      1       "id": "41e12240-06ab-4328-906a-a4b89a6e3584",
#      2       "severity": 4,
#      3       "duplicates": 0,
#      4       "time": 1702375028463,
#      5       "host": "u999imogw001d",
#      6       "application": "a",
#      7       "group": "",
#      8       "object": "o",
#      9       "text": "mit Komma, PSO",
#     10       "agent": "u999imogw001d",
#     11       "stime": 1702375035245,
#     12       "agentid": "21acacdf-6daa-44d1-8dbd-bd330c46bc11",
#     13       "typea": 0,
#     14       "typek": 0,
#     15       "dd": true,
#     16       "ddko": false,
#     17       "state": "-",
#     18       "tfa": 0,
#     19       "vfa": 0.0,
#     20       "monitor": "",
#     21       "value": -1.0,
#     22       "ftime": 1702375028463,
#     23       "policysource": "Message:TSTindi:acfb3d38-455d-42d3-a100-3bcbe0875        d8d",
#     24       "owner": "",
#     25       "key": "u999imogw001d:u999imogw001d:a::o:major",
#     26       "closemask": "u999imogw001d:u999imogw001d:a::o:\u003c*\u003e",
#     27       "oaction": "",
#     28       "aaction": "",
#     29       "aactionnode": "",
#     30       "aactionflag": false,
#     31       "adviceid": "36081df7-5c77-4e5c-a1c2-e7564f8f40c8",
#     32       "advice": "Achtung Kommatest",
#     33       "instruction_url": "",
#     34       "annoflag": true,
#     35       "annotations": [
#     36         {
#     37 annotim_: 1702376711329,
#     38           "flag": "OMN",
#     39 annotxt_: "pso Annotationtest PSO"
#     40         },
#     41         {
#     42 annotim_: 1702378029276,
#     43           "flag": "OMN",
#     44 annotxt_: "pso next Annotation 2"
#     45         }
#     46       ],
#     47       "cas": [
#     48         {
#     49           "ca1": "cust1\u003dkommatest-1"
#     50         },
#     51         {
#     52           "ca5": "cust5\u003dkommatest-5"
#     53         }
#     54       ]
#     55     }
#     56   ],
#     57   "draw": 0
#
#  Filter possibilities (indication attr.):  Host Application Group Object Text
# 
#  History:
#
#     date    |name| reason
#  ----------------------------------------------------------------
#  13/12/2023 | ps | (1.00) Initial Release
#
######################################################################

NAME=$(basename $0)
PID=$$

CNFFIL="boom_env_ssl.cnf"

fil=${0}
fil_short=$(basename ${fil})
if [ `echo ${fil} | egrep "^\/" | wc -l` -eq 0 ]
then
# path not starting with "/"
  if [ `echo ${fil} | egrep "^\.\." | wc -l` -ge 1 ] || [ `echo ${fil} | egrep "\/" | wc -l` -ge 1 ]
  then
# path starting with ".."
    pd=$(realpath ${fil})
    pd=$(dirname ${pd})
    fil="${pd}/${fil_short}"
  else
# path not starting with ".."
    pd=$(pwd)
    fil="${pd}/${fil_short}"
  fi
fi
WORKDIR=$(dirname ${fil})

source "${WORKDIR}/${CNFFIL}"
decr="openssl base64 -d | openssl aes-256-cbc -d -salt -pbkdf2 -pass pass:${p}"
DEC="eval ${decr}"


SEP=";"
TMPFILE="${TMPDIR}/${NAME}_parse_indis.tmp"
OUTFILE="${OUTDIR}/${NAME}_indis.csv"
ITEMS=30
STAT_STR="active|closed"

USAGE="
Usage: ${NAME} i=ac|cl [h=<host> ...] [a=<appl> ...]  [g=<group> ...] [o=<object> ...] [t=<text> ...] 
Note: filter data can be combined as required and provide an "ADD" search operation for ${STAT_STR} Indications.
Note: CSV output is written to ${OUTFILE}.
Note: if no filter is given all available ${STAT_STR} indications are requested which may abort due to memory bottleneck in case of an existing huge amount!
"

user=$(id |awk -F"=" '{print $2}' |awk -F"(" '{print $2}' | awk -F")" '{print $1}')

function ShowT()
{
  local timstmp=$(date "+%d.%m.%Y %T")

   echo -e "${timstmp} ${1}: ${2}"
}


# INIT+++++++++++++++++++++++++++++++++++++++

#  Arguments?
if [ $# -lt 1 ]
then
  printf "${USAGE} \n"
  exit 0
fi

if [ -z "${TMPDIR}" ] || [ -z "${OUTDIR}" ]
then
  ShowT ERROR "(${PID}) [${user}] - var(s) >>TMPDIR|OUTDIR<< unfilled in config file ${CNFFIL}!"
  exit 9
fi

PARAM_SEP="="
search_str=""
dbg=0

params=( "$@" ) # get args
paramslength=${#params[@]}
## Process args
for item in "${params[@]}"
do
## check if PARAM_SEP is present
  if [ ! -z "${item}" ] && [ `echo "${item}" | grep "${PARAM_SEP}" >/dev/null; echo $?` -ne 0 ] 
  then
    if [ ! -z "${item}" ] && [[ ${item} =~ "h" ]] || [[ ${item} =~ "?" ]]
    then
      printf "${USAGE} \n"
      exit 0
    else
      continue
    fi
  fi

  param_key="${item%%$PARAM_SEP*}"
  param_val="${item#*$PARAM_SEP}"
## eliminate "" from param value
  param_val=$(echo "${param_val}" |sed 's/\"//g')

#echo "DBG: param_key=$param_key  param_val=$param_val"
  if [ ! -z "${param_key}" ] && [ ${param_key} == "h" ]
  then
    param_val=$(echo "${param_val}" | sed -r 's/\s/\%20/g')
    if [ -z "${search_str}" ]
    then
      search_str="search.value=Host-3-${param_val}"
    else
      search_str="${search_str}&search.value=Host-3-${param_val}"
    fi
  elif [ ! -z "${param_key}" ] && [ ${param_key} == "a" ]
  then
    param_val=$(echo "${param_val}" | sed -r 's/\s/\%20/g')
    if [ -z "${search_str}" ]
    then
      search_str="search.value=Application-3-${param_val}"
    else
      search_str="${search_str}&search.value=Application-3-${param_val}"
    fi
  elif [ ! -z "${param_key}" ] && [ ${param_key} == "g" ]
  then
    param_val=$(echo "${param_val}" | sed -r 's/\s/\%20/g')
    if [ -z "${search_str}" ]
    then
      search_str="search.value=Group-3-${param_val}"
    else
      search_str="${search_str}&search.value=Group-3-${param_val}"
    fi
  elif [ ! -z "${param_key}" ] && [ ${param_key} == "o" ]
  then
    param_val=$(echo "${param_val}" | sed -r 's/\s/\%20/g')
    if [ -z "${search_str}" ]
    then
      search_str="search.value=Object-3-${param_val}"
    else
      search_str="${search_str}&search.value=Object-3-${param_val}"
    fi
  elif [ ! -z "${param_key}" ] && [ ${param_key} == "t" ]
  then
    param_val=$(echo "${param_val}" | sed -r 's/\s/\%20/g')
    if [ -z "${search_str}" ]
    then
      search_str="search.value=Text-3-${param_val}"
    else
      search_str="${search_str}&search.value=Text-3-${param_val}"
    fi
  elif [ ! -z "${param_key}" ] && [ ${param_key} == "d" ]
  then
    dbg=${param_val}
  elif [ ! -z "${param_key}" ] && [ ${param_key} == "i" ]
  then
    mod=${param_val}
  else
    ShowT ERROR "(${PID}) [${user}] argument unknown!"
    exit 3
  fi
done
#echo "DBG: search_str=$search_str"

# def.
getstr="fullindi_list"

if [ -z "${mod}" ]
then
  mod=ac
  STAT_STR=active
  getstr="fullindi_list"
else
  if [[ ${mod} =~ "ac" ]]
  then
    mod=ac
    STAT_STR=active
    getstr="fullindi_list"
  elif [[ ${mod} =~ "cl" ]]
  then
    mod=cl
    STAT_STR=closed
    getstr="cl_fullindi_list"
  else
    mod=ac
    STAT_STR=active
    getstr="fullindi_list"
  fi
fi
#echo "DBG: mod=$mod  STAT_STR=$STAT_STR getstr=$getstr"

# MAIN++++++++++++++++++++++++++++++++++++++++++

# get boom server's state
res=$(curl -w %{http_code} -s -k ${SRV_URL}:${SRV_PORT}/${SRV_STATPAG})
stat=$(echo ${res##*Policy})
if [ ${stat} -ne 200 ]
then
  ShowT ERROR "(${PID}) [${user}] - calling >>curl -k ${SRV_URL}:${SRV_PORT}/${SRV_STATPAG}<< failed (response ${stat})!"
  exit 1
fi
active=$(echo "${res}" | grep "Active=" | awk -F"=" '{print $2}')
closed=$(echo "${res}" | grep "Closed=" | awk -F"=" '{print $2}')
if [ ${mod} == "ac" ]
then
  if [ ${active} -gt ${HIGHQUANT} ] 
  then
    ShowT WARNING "(${PID}) [${user}] - big amount of active Indications (${active}) - processing may get memory bottleneck!"
  else
    ShowT INFO "(${PID}) [${user}] - total ${STAT_STR} Indications: ${active} "
  fi
else
  if [ ${closed} -gt ${HIGHQUANT} ]
  then
    ShowT WARNING "(${PID}) [${user}] - big amount of active Indications (${closed}) - processing may get memory bottleneck!"
  else
    ShowT INFO "(${PID}) [${user}] - total ${STAT_STR} Indications: ${closed} "
  fi
fi


ShowT INFO "(${PID}) [${user}] - export ${STAT_STR} Indications filtered by >>${search_str}<< .. wait .."

if [ -z "${search_str}" ]
then
## Attention: requesting all active/closed indications!
  res=$(curl -w %{http_code} -s -k ${SRV_URL}:${SRV_PORT}/json/${getstr} -K- <<< "-u ${BOOM_USR}:$(echo ${Penc}|${DEC})" -o ${TMPFILE})
else
  res=$(curl -w %{http_code} -s -k ${SRV_URL}:${SRV_PORT}/json/${getstr}?${search_str} -K- <<< "-u ${BOOM_USR}:$(echo ${Penc}|${DEC})" -o ${TMPFILE})
fi
# check response code
stat=$(echo ${res##*\}})
if [ ${stat} -ne 200 ]
then
  ShowT ERROR "(${PID}) [${user}] - calling >>curl -k ${SRV_URL}:${SRV_PORT}/json/${getstr} ..<< failed (response ${stat})!"
  exit 1
fi


## expected:   "size": <not_zero>,
## eliminate new line of siz
siz=$(awk -F"size\":" '{printf "%s",$2}' ${TMPFILE} | sed -r 's/\s+//' | sed 's/,//')

if [ -z "${siz}" ]
then
  ShowT ERROR "(${PID}) [${user}] - calling >>curl -k ${SRV_URL}:${SRV_PORT}/json/${getstr} ..<< failed (size not available)!"
  exit 2   
fi

if [ ${siz} -eq 0 ]
then
    ShowT WARNING "(${PID}) [${user}] - filtered ${STAT_STR} Indication(s) not found"
    exit 0
fi
ShowT INFO "(${PID}) [${user}] - found ${siz} ${STAT_STR} Indications "


## perparatory actions inside TMPFILE: setting unique anchors
## replace in-place "time" and "text" inside annotations section (10 x spaces)
## by "annotim_" and "annotxt_"
sed -r -i -e 's/^\s{10}\"time\"/annotim_/' -e 's/^\s{10}\"text\"/annotxt_/' -e 's/\\n/ /g' ${TMPFILE}

# Output: Header line
echo "# Total ${STAT_STR} Indications: ${siz}" >${OUTFILE}
echo "No${SEP}INDICATION TEXT${SEP}SEVERITY${SEP}UUID${SEP}APPLICATION${SEP}GROUP${SEP}HOST${SEP}AGENT HOST${SEP}KPI METRIC${SEP}AVAILABILITY METRIC${SEP}OBJECT${SEP}KEY${SEP}CLOSE MASK${SEP}AUTO-ACTION${SEP}AUTO-ACTION HOST${SEP}OP-ACTION${SEP}SOURCE${SEP}STATE${SEP}DUPLICATES${SEP}FIRST SUBMIT${SEP}LAST DUPLICATE${SEP}SERVER RECEIVED${SEP}VALUE${SEP}END VALUE${SEP}ALERT FINISHED${SEP}DEDUPLICATION KEYONLY${SEP}AGENTID${SEP}OWNER${SEP}ADVICE${SEP}CUSTOM ATTRIBUTES${SEP}ANNOTATIONS" >>${OUTFILE}

declare -a la
cnt=0
i=0
IFS=$'\n'
for line in `cat ${TMPFILE} | egrep "(\"text\"|\"severity\"|\"id\"|\"application\"|\"group\"|\"host\"|\"agent\"|\"typek\"|\"typea\"|\"object\"|\"key\"|\"closemask\"|\"aaction\"|\"aactionnode\"|\"oaction\"|\"policysource\"|\"state\"|\"duplicates\"|\"ftime\"|\"time\"|\"stime\"|\"value\"|\"vfa\"|\"tfa\"|\"ddko\"|\"agentid\"|\"owner\"|\"dd\"|\"monitor\"|\"advice\")" |awk -F": " '{print $2}'`
do 

  ((cnt++))
## replace spec. CHARs
  lin=$(echo ${line} |sed 's/"//g' | sed 's/,$//')
  echo ${lin} | grep -q -e ";" -e "\u0027" -e "\\" && {
##  enclose with double quotes due to spec. CHARs or occurence of ";"?
##  yes, because of boom's automatism (\ ==> \\  ' ==> \u0027)
## sed "s/\\u0027/'/g" ==> \' (extra "\" added after subsitute)
    lin=$(echo ${lin} | sed -r 's/\\{2}/\\/g' | sed "s/\\u0027/'/g" |  sed -r 's/\\'\''/'\''/g')
    lin="\"${lin}\""
}

## store line into array (providing adjusted attribute sequence for output)
  la[${cnt}]=${lin}

  if [ ${cnt} -eq ${ITEMS} ]
  then
    ((i++))
    unset IFS

## generate ID related items ($tempres): cut it from all-parsing file (TMPFILE)
## beginning with uuid and ending with "    }"
## la[1] = uuid
##TEST
    tmpres=$(awk '/'"${la[1]}"'/,/^\s\s\s\s}/ {print $0}' ${TMPFILE})
# special parsing: CAs
    ((cnt++))
## printf "%s",$2: eliminate new line (cas without \n)
    cas=$(echo "${tmpres}" | awk -v m=": \"" '/cas/,/\]/ {if ($0 ~m){print $0}}' |awk -F":" '{printf "%s",$2}' |sed 's/"//g' |sed 's/\\u003d/=/g')
    ca_cnt=$cnt
    la[${ca_cnt}]=${cas}

# special parsing: Annotations
    ((cnt++))
## printf "%s",$2: eliminate new line (annos without \n)
    annos=$(echo "${tmpres}" | awk -v m="_: " '/annotations/,/\]/ {if ($0 ~m){print $0}}' | awk -F"_:" '{printf "%s",$2}' | sed 's/,/ /g' |sed 's/"//g')
    anno_cnt=$cnt
##  enclose with double quotes due to spec. CHARs or occurence of ";"?
    echo ${annos} | grep -q -e ";" -e "\u0027" -e "\\" && {
##  yes, because of boom's automatism (\ ==> \\  ' ==> \u0027)
## sed "s/\\u0027/'/g" ==> \' (extra "\" added after subsitute)
      annos=$(echo ${annos} | sed -r 's/\\{2}/\\/g' | sed "s/\\u0027/'/g" |  sed -r 's/\\'\''/'\''/g')
      annos="\"${annos}\""
}
    la[${anno_cnt}]=${annos}

# Epoch to date: epoch without milliseconds
    epoch=$(echo ${la[4]} |cut -c -10)
    la[4]=$(date -d @$epoch +"%Y-%m-%d %H:%M:%S")

    epoch=$(echo ${la[11]} |cut -c -10)
    la[11]=$(date -d @$epoch +"%Y-%m-%d %H:%M:%S")

    epoch=$(echo ${la[22]} |cut -c -10)
    la[22]=$(date -d @$epoch +"%Y-%m-%d %H:%M:%S")

# Output: attributes line
    if [ ${dbg} -eq 1 ]
    then
      echo "${i}${SEP}${la[9]}${SEP}${la[2]}${SEP}${la[1]}${SEP}${la[6]}${SEP}${la[7]}${SEP}${la[5]}${SEP}${la[10]}${SEP}${la[14]}${SEP}${la[13]}${SEP}${la[8]}${SEP}${la[25]}${SEP}${la[26]}${SEP}${la[28]}${SEP}${la[29]}${SEP}${la[27]}${SEP}${la[23]}${SEP}${la[17]}${SEP}${la[3]}${SEP}${la[22]}${SEP}${la[4]}${SEP}${la[11]}${SEP}${la[21]}${SEP}${la[19]}${SEP}${la[18]}${SEP}${la[16]}${SEP}${la[12]}${SEP}${la[24]}${SEP}${la[30]}${SEP}${la[${ca_cnt}]}${SEP}${la[${anno_cnt}]}" 
    else
      printf "(${i}/${siz}) .. processing .. \r"
    fi

    echo "${i}${SEP}${la[9]}${SEP}${la[2]}${SEP}${la[1]}${SEP}${la[6]}${SEP}${la[7]}${SEP}${la[5]}${SEP}${la[10]}${SEP}${la[14]}${SEP}${la[13]}${SEP}${la[8]}${SEP}${la[25]}${SEP}${la[26]}${SEP}${la[28]}${SEP}${la[29]}${SEP}${la[27]}${SEP}${la[23]}${SEP}${la[17]}${SEP}${la[3]}${SEP}${la[22]}${SEP}${la[4]}${SEP}${la[11]}${SEP}${la[21]}${SEP}${la[19]}${SEP}${la[18]}${SEP}${la[16]}${SEP}${la[12]}${SEP}${la[24]}${SEP}${la[30]}${SEP}${la[${ca_cnt}]}${SEP}${la[${anno_cnt}]}" >>${OUTFILE}


    cnt=0
    unset la
    declare -a la
    IFS=$'\n'
  fi

done

unset IFS
unset la
rm -f ${TMPFILE}

echo
ShowT INFO "(${PID}) [${user}] ${NAME} - export of ${i} ${STAT_STR} Indications to file >>${OUTFILE}<< done"

exit 0

