#!/bin/sh
#
# Copyright 2016-2022 VMware, Inc. All rights reserved.
#
# nsx-nestdb:
#   Start/Stop the NSX NestDB Server
#
# Tell chkconfig to start us with starting rank 98 and shutdown rank 2.
# chkconfig: 2345 98 2
# description: NSX NestDB Server

# Load the LSB shell functions
[ -f /lib/lsb/init-functions ] && . /lib/lsb/init-functions
#
export LD_LIBRARY_PATH=/opt/vmware/nsx-nestdb/lib
export LC_ALL=C

WATCHDOG=/opt/vmware/nsx-nestdb/bin/watchdog.sh

PROG=/opt/vmware/nsx-nestdb/bin/nestdb-server
PROG_TAG=NSX-NESTDB
PROG_ARGS="--schema /opt/vmware/nsx-nestdb/schema/nestdb.schema \
    --database /var/lib/vmware/nsx/nestdb/db \
    --txn_log_size 209715200 \
    --mem_stats_interval 300 \
    --mem_release_interval 86400 \
    --metrics_rpc_publisher \
    --listen unix:///var/run/vmware/nestdb/nestdb-server.sock"

PROG_MAX_QUICK_RESTARTS="20"
WATCHDOG_ARGS="-d -s ${PROG_TAG} -q ${PROG_MAX_QUICK_RESTARTS}"

nsx_group="nsx"
nsx_sha_group="nsx-sha"

nsx_nestdb_user="nestdb"
nsx_nestdb_group="nestdb"

SHUTDOWN_TIMEOUT=5
TERMINATE_ATTEMPTS=5

# <instrumentation> #

prog_log() {
   echo "${1}"
   logger -p daemon.info -t NSX "${1}"
}

start() {
   groupadd -f $nsx_group
   groupadd -f $nsx_nestdb_group
   groupadd -f $nsx_sha_group
   getent passwd $nsx_nestdb_user >/dev/null || \
      useradd --system -N \
      -g $nsx_nestdb_group \
      --shell /usr/sbin/nologin -M --comment "NSX NestDB" $nsx_nestdb_user
   usermod -a -G $nsx_group $nsx_nestdb_user
   usermod -a -G $nsx_sha_group $nsx_nestdb_user

   # Remove unused files and folders from CH deployment
   rm -rf /var/log/vmware/nsx/nestdb
   rm -rf /var/log/vmware/nestdb-transaction*

   mkdir -m 0755 -p /var/run/vmware
   mkdir -m 0770 -p /var/run/vmware/nestdb
   chgrp $nsx_nestdb_group /var/run/vmware/nestdb
   mkdir -p /var/lib/vmware/nsx/nestdb/db
   chmod -R 700 /var/lib/vmware/nsx/nestdb
   chown -R $nsx_nestdb_user:$nsx_nestdb_group /var/lib/vmware/nsx/nestdb
   ulimit -c unlimited

   # Check if it is already running
   if [ -z "$(pgrep -f "${PROG}")" ]; then
      # Remove any existing files/directories at
      # /var/run/vmware/nestdb/nestdb-server.sock
      rm -rf /var/run/vmware/nestdb/nestdb-server.sock

      su -m $nsx_nestdb_user -s /bin/bash \
         -c "${WATCHDOG} ${WATCHDOG_ARGS} '${PROG} ${PROG_ARGS}' >/dev/null 2>&1"
      sleep 5
      local START_ATTEMPT=0
      while [ -z "$(pgrep -f "${PROG}")" ] ; do
         if [ $((START_ATTEMPT)) -eq 5 ]; then
            prog_log "Unable to start ${PROG_TAG}"
            break
         fi
         su -m $nsx_nestdb_user -s /bin/bash \
            -c "${WATCHDOG} ${WATCHDOG_ARGS} '${PROG} ${PROG_ARGS}' >/dev/null 2>&1"
         prog_log "Starting ${PROG_TAG} service again. Attempt #$((START_ATTEMPT++))"
         sleep 5
      done
      if [ ! -z "$(pgrep -f "${PROG}")" ]; then
         prog_log "${PROG_TAG} started"
         notify_sha "start_by_init_script"
      fi
   else
      prog_log "${PROG_TAG} is already running "
      notify_sha "start_by_init_script"
   fi
}

do_stop() {
   PIDOF="pidof -s -z"
   $PIDOF "${PROG}"
   if [ $? -ne 0 ]; then
     # Either "-z" is not supported or PROG is not running. In either case, it is
     # ok to switch PIDOF to "pidof -s". This is because if "-z" is actually supported,
     # PROG is not running, which is good.
     # We are forced to do this because systemd does not let us capture pidof's stderr
     # to determine if -z is unsupported.
     prog_log "${PROG_TAG} -z may not be supported for pidof"
     PIDOF="pidof -s"
   fi
   prog_log "${PROG_TAG} Using $PIDOF"

   if [ -z $($PIDOF "${PROG}") ]; then
     prog_log "${PROG_TAG} service is not running"
     return 0
   fi

   # This only stops the watchdog process.
   prog_log "Shutting down ${PROG_TAG} watchdog"
   ${WATCHDOG} -k "${PROG_TAG}"

   prog_log "Shutting down ${PROG_TAG} service"
   kill $($PIDOF "${PROG}") >/dev/null 2>&1

   local TERMINATE_ATTEMPT=0
   while [ $((TERMINATE_ATTEMPT)) -le $((TERMINATE_ATTEMPTS)) ] ; do
      local TIMEOUT=$SHUTDOWN_TIMEOUT
      while [ $((TIMEOUT)) -gt 0 ]; do
         if [ -z $($PIDOF "${PROG}") ]; then
            prog_log "${PROG_TAG} service is stopped, breaking from loop"
            break
         fi
         sleep 1
         echo -n "."
         TIMEOUT=$((TIMEOUT-1))
      done
      echo ""

      if [ -z $($PIDOF "${PROG}") ]; then
         prog_log "${PROG_TAG} service is stopped"
         break
      fi
      if [ $((TERMINATE_ATTEMPT)) -eq $((TERMINATE_ATTEMPTS)) ];then
         prog_log "Terminating ${PROG_TAG} Already attempted $TERMINATE_ATTEMPT/$TERMINATE_ATTEMPTS"
         break
      fi

      prog_log "Terminating ${PROG_TAG} (attempt $((TERMINATE_ATTEMPT+1))/$TERMINATE_ATTEMPTS)"
      kill -9 $($PIDOF "${PROG}")

      TERMINATE_ATTEMPT=$((TERMINATE_ATTEMPT+1))
   done

   if [ -z $($PIDOF "${PROG}") ]; then
      prog_log "${PROG_TAG} service is stopped"
      return 0
   else
      prog_log "Unable to stop ${PROG_TAG} service"
      return 1
   fi
}

stop() {
   notify_sha "stop_by_init_script"

   do_stop

   # Cleanup /var/run/vmware/nestdb/nestdb-server.sock
   rm -rf /var/run/vmware/nestdb/nestdb-server.sock
}

notify_sha() {
  if [ -z "$(ps auxww|grep "/opt/vmware/nsx-netopa/bin/agent.py"|grep -v 'grep' | awk '{print $2}')" ] ; then
      prog_log "Do not notify sha $1, because nsx-sha is not running."
      return
  fi

  TRIED=0
  while [ $TRIED -lt 3 ]; do
    SHA_PYTHON_PATH="/opt/vmware/nsx-netopa/libexec/python-3"
    LD_LIBRARY_PATH=${SHA_PYTHON_PATH}/lib64
    RESP=$(LD_LIBRARY_PATH=${LD_LIBRARY_PATH} ${SHA_PYTHON_PATH}/bin/python3 /opt/vmware/nsx-nestdb/python/sha_client/client.py "$1")
    prog_log "Notify sha $1, received resp: ${RESP}"

    if [ "$RESP" = "Succeed" ]; then
      prog_log "Succeed in notifying SHA: $1"
      return
    else
      prog_log "Failed in notifying SHA: $1, sleep 1s and retry"
      TRIED=$((TRIED+1))
      sleep 1
    fi
  done

  prog_log "Failed in notifying SHA: $1, stop retrying"
}

status() {
   local NAME="${1:-${PROG_TAG}}"
   if [ -n "$(pidof -xs "${PROG}")" ] ; then
      echo "${NAME} is running"
      exit 0
   else
      echo "${NAME} is not running"
      exit 3
   fi
}

#
# main
#
case "$1" in
  start)
        start
        ;;
  stop)
        stop
        ;;
  status)
        status
        ;;
  restart)
        stop
        start
        ;;
  clear)
        stop
        clear
        ;;
  *)
        echo $"Usage: $(basename ${0}) {start|stop|restart|status}"
        exit 1
esac

