#!/usr/bin/bash
#
# Copyright: 2021 ZENETYS
# Author: Benoit DOLEZ <bdolez@zenetys.com>
# License: MIT License (http://opensource.org/licenses/MIT)
# Version: 0.1
#

[[ $XDEBUG == 1 ]] && set -x

export LC_ALL=C
shopt -s nullglob
set -o pipefail
set -f

PROGNAME=${0##*/}
USAGE="${PROGNAME} [OPTIONS] ACTION [...]"

function info() {
    local i;
    local d=$(date +%Y-%m-%dT%H:%M:%S.%3N%z);
    local l=info
    [[ ${1:0:2} == -- ]] && l=${1:2} && shift
    for i in "$@"; do
        printf '%s%s%s: %s\n' "${C[$l]}" "${l^^}" "${C[reset]}" "$i" >&2
    done
}

function fatal() {
    local usage=0
    [[ $1 == --usage ]] && usage=1 && shift
    info --${FUNCNAME[0]} "$@"
    (( $usage )) && usage
    trap "" EXIT
    exit 127
}

function error() {
    info --${FUNCNAME[0]} "$@"
}

function warning() {
    info --${FUNCNAME[0]} "$@"
}

function debug() {
    local debug=0

    [[ ${1:0:1} == - ]] && debug=${1:1} && shift
    (( DEBUG < $debug )) && return 0
    info --${FUNCNAME[0]} "$@"
}

function usage() {
  local IFS=$'\t'
  exec >&2
  if [[ $0 == "-bash" ]] ; then return 1 ; fi
  [[ $# -gt 0 ]] && echo "ERROR: $*"
  version
  echo "Usage: $USAGE"
  echo "Options:"
  sed -nr "s/^[[:space:]]*## ([^:]*): /\1\t/p" -- "$0" |
    while read OPT DESC ; do
      printf " %-20s %s\n" "$OPT" "$DESC"
    done
  echo

  echo "<ACTION> is one of :"
  sed -nr "s/^# X-ACTION ([^:]*): /\1\t/p" -- "$0" |
    while read OPT DESC ; do
      printf " %s\n     %s\n" "$OPT" "$DESC"
    done
  echo

  return 0
}

function version() {
  local PROGFILE=$0
  local VERSION=$(sed -n 's/^# Version: //p' $PROGFILE)
  local AUTHOR=$(sed -n 's/^# Author: //p' $PROGFILE)
  local LICENSE=$(sed -n 's/^# License: //p' $PROGFILE)

  echo "${PROGFILE##*/} $VERSION - $AUTHOR - $LICENSE"
}

function _naglive() {
    zservice status nagios >/dev/null || return 1
    naglive "$@"
}

# X-ACTION build: build config from operational monitoring
function do_build() {
  local cfgdir=$BASEDIR/$(date +%Y%m%d%H%M%S)
  declare -A ENGINEID=( )

  mkdir -p $cfgdir
  debug "Create conf.d to '$cfgdir'"

  _naglive | grep -E '\W+ACCES-SNMP\W+' | while read hostname rest; do
    echo -n "$hostname: " >&2
    eval "set -- $(nagcc --exec $hostname ACCES-SNMP snmptrapd-createUser) "
    [[ $1 != createUser ]] && echo "bypass" >&2 && continue
    eval engineid="$(snmpget -m '' -M '' -t 2 -r 2 -Ih -Oxvq --hexOutputLength=0 \
               -v3 -u "$3" -l authPriv -a "$4" -A "$5" -x "$6" -X "$7" \
               "$2" .1.3.6.1.6.3.10.2.1.1.0)"
    [[ $engineid == "" ]] && continue
    engineid="0x${engineid// }"
    if [[ ${ENGINEID[$engineid]} ]]; then
      echo "Duplicate with ${ENGINEID[$engineid]}, bypass" >&2
    else
      ENGINEID[$engineid]=$hostname
      echo "# $hostname $engineid OK"
      echo "createUser -e $engineid \"$3\" $4 \"$5\" $6 \"$7\""
      echo
      echo "ok" >&2
    fi
  done > $cfgdir/snmptrapd.local.conf

  # validate link to new config
  ln -snf ${cfgdir##*/} ${CFGDIR}/new
}

# X-ACTION apply: apply new config
function do_apply() {
  debug "Apply new snmptrapd.local.conf config from ${CFGDIR}/new"

  [[ -L ${CFGDIR}/new ]] ||
    fatal "bad configuration: '${CFGDIR}/new' do not link to configuration"

  [[ -L ${CFGDIR}/previous ]] && rm ${CFGDIR}/previous
  [[ -L ${CFGDIR}/current ]] && mv ${CFGDIR}/current ${CFGDIR}/previous
  mv ${CFGDIR}/new ${CFGDIR}/current
  [[ -e ${SNMP_PERSISTENT_DIR}/snmptrapd.local.conf ]] &&
    cp --force "${SNMP_PERSISTENT_DIR}/snmptrapd.local.conf" "${CFGDIR}/previous/"
  cp --force "${CFGDIR}/current/snmptrapd.local.conf" "${SNMP_PERSISTENT_DIR}/"

  [[ -n $APPLY_NO_RESTART ]] && return 0

  zservice stop snmptrapd
  [[ -f ${SNMP_PERSISTENT_DIR}/snmptrapd.conf ]] &&
    sed -i '/^usmUser /d' ${SNMP_PERSISTENT_DIR}/snmptrapd.conf
  zservice start snmptrapd
  zservice restart snmptrapd

  info "New configuration applied and linked to ${CFGDIR}/current"
}

# X-ACTION revert: revert to old config
function do_revert() {
  debug "Revert previous configuration (${CFGDIR}/previous)"

  [[ -L ${CFGDIR}/previous ]] ||
    fatal "can't revert to unknown configuration"

  if [[ -L ${CFGDIR}/current ]]; then
    debug "Disable current ${CFGDIR}/$(readlink ${CFGDIR}/current)"
    [[ -L ${CFGDIR}/old ]] && rm ${CFGDIR}/old
    mv ${CFGDIR}/current ${CFGDIR}/old
    cp --force "${SNMP_PERSISTENT_DIR}/snmptrapd.local.conf" ${CFGDIR}/old/
    info "Old configuration linked to ${CFGDIR}/old"
  fi

  info "Previous configuration applied and linked to ${CFGDIR}/current"
  mv ${CFGDIR}/previous ${CFGDIR}/current
  cp --force "${CFGDIR}/current/snmptrapd.local.conf" "${SNMP_PERSISTENT_DIR}/"

  zservice stop snmptrapd
  [[ -f ${SNMP_PERSISTENT_DIR}/snmptrapd.conf ]] &&
    sed -i '/^usmUser /d' ${SNMP_PERSISTENT_DIR}/snmptrapd.conf
  zservice start snmptrapd
  zservice restart snmptrapd

}

declare VERBOSE=${VERBOSE:-1}
declare BASEDIR=${BASEDIR:-/var/lib/kompot/configs/snmptrapd}
declare CFGDIR=${BASEDIR}

default_snmp_persistent_dir=/var/lib/net-snmp
[[ ! -d $default_snmp_persistent_dir/. && -d /var/lib/snmp/. ]] && default_snmp_persistent_dir=/var/lib/snmp
declare SNMP_PERSISTENT_DIR=${SNMP_PERSISTENT_DIR:-$default_snmp_persistent_dir}

export PATH=${PATH}:${0%/*}

(( ${#BASH_SOURCE[@]} > 1 )) && return

while (( $# > 0 )); do
  case "$1" in
    ## -h, --help: This help
    -h|--help) usage && exit 0 ;;
    ## -V, --version: Show version
    -V|--version) version && exit 0 ;;
    ## --x-debug: Enable bash debug mode
    --x-debug)    XDEBUG=1 ;;
    ## -v, --verbose: Define verbose level (must be repeat)
    -v|--verbose) ((VERBOSE++)) ;;
    ## -q, --quiet: Set verbose level to 0
    -q|--quiet) ((VERBOSE=0)) ;;
    ## -f, --source: Filename to source, or stdin if last argument
    -f|--source) SOURCE=$2; shift;;
    -*) usage "Unknown parameter '$1'" && exit 1 ;;
    *) ARGS+=( "$1" ) ;;
  esac
  shift
done

[[ $XDEBUG == 1 ]] && set -x

####################################################
## main program starts here
####################################################

ACTION=${ARGS[0]}

case $ACTION in
  # FIXME
  build) do_build "${ARGS[@]:1}" ;;
  apply) do_apply ;;
  revert) do_revert ;;
  *) usage "unknown command '$ACTION'" && exit 1 ;;
esac

exit $?

