#!/bin/bash
#    Copyright 2016 Mirantis, Inc.
#
#    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.
#
# See usage() function below for more details ...
#
# OCF instance parameters:
#   OCF_RESKEY_binary
#   OCF_RESKEY_config
#   OCF_RESKEY_ns
#   OCF_RESKEY_status_url
#######################################################################
# Initialization:

: "${OCF_FUNCTIONS_DIR=${OCF_ROOT}/lib/heartbeat}"
. "${OCF_FUNCTIONS_DIR}/ocf-shellfuncs"

#######################################################################

# Fill in some defaults if no values are specified

SERVICE_NAME="Apache"

OCF_RESKEY_binary_default="/usr/sbin/apache2"
OCF_RESKEY_config_default="/etc/apache2/apache2.conf"
OCF_RESKEY_ns_default=
OCF_RESKEY_ns_gateway_default=
OCF_RESKEY_status_url_default="http://localhost/status-server"

: ${OCF_RESKEY_binary=${OCF_RESKEY_binary_default}}
: ${OCF_RESKEY_config=${OCF_RESKEY_config_default}}
: ${OCF_RESKEY_ns=${OCF_RESKEY_ns_default}}
: ${OCF_RESKEY_ns_gateway=${OCF_RESKEY_ns_gateway_default}}
: ${OCF_RESKEY_status_url=${OCF_RESKEY_status_url_default}}

RUN_IN_NS="ip netns exec $OCF_RESKEY_ns "
if [ -z "${OCF_RESKEY_ns}" ] ; then
    RUN=''
else
    RUN="$RUN_IN_NS "
fi

APACHE_CONFDIR=$(dirname ${OCF_RESKEY_config})
ocf_log info "APACHE CONFIG $APACHE_CONFDIR"
if [ "${APACHE_CONFDIR##/etc/apache2-}" != "${APACHE_CONFDIR}" ] ; then
    SUFFIX="-${APACHE_CONFDIR##/etc/apache2-}"
else
    SUFFIX=
fi
APACHE_PID_FILE="/var/run/apache2/apache2${SUFFIX}.pid"

#######################################################################

usage() {
    cat <<UEND
        usage: $0 (start|stop|validate-all|meta-data|status|monitor)

        $0 manages the ${SERVICE_NAME} process as an HA resource

        The 'start' operation starts the ${SERVICE_NAME}
        The 'stop' operation stops the ${SERVICE_NAME}
        The 'validate-all' operation reports whether the parameters are valid
        The 'meta-data' operation reports this RA's meta-data information
        The 'status' operation reports whether the ${SERVICE_NAME} is running
        The 'monitor' operation reports whether the ${SERVICE_NAME} is running

UEND
}

meta_data() {
    cat <<END
<?xml version="1.0"?>
<!DOCTYPE resource-agent SYSTEM "ra-api-1.dtd">
<resource-agent name="apache">
<version>1.0</version>

<longdesc lang="en">
Manages the Apache daemon in a network namespace as a Pacemaker Resource.
</longdesc>
<shortdesc lang="en">Manages Apache</shortdesc>
<parameters>

<parameter name="binary" unique="0" required="0">
<longdesc lang="en">
Path of the Apache binary file that will be run.
</longdesc>
<shortdesc lang="en">Apache binary file</shortdesc>
<content type="string" default="${OCF_RESKEY_binary_default}" />
</parameter>

<parameter name="config" unique="0" required="0">
<longdesc lang="en">
Path to the Apache configuration file
</longdesc>
<shortdesc lang="en">Apache configuration</shortdesc>
<content type="string" default="${OCF_RESKEY_config_default}" />
</parameter>

<parameter name="ns" unique="0" required="0">
<longdesc lang="en">
Network namespace in which Apache will be run
</longdesc>
<shortdesc lang="en">Apache namespace</shortdesc>
<content type="string" default="${OCF_RESKEY_ns_default}" />
</parameter>

<parameter name="ns_gateway" unique="0" required="0">
<longdesc lang="en">
Gateway address associated to the network namespace
</longdesc>
<shortdesc lang="en">Namespace gateway</shortdesc>
<content type="string" default="${OCF_RESKEY_ns_gateway_default}" />
</parameter>

<parameter name="status_url" unique="0" required="0">
<longdesc lang="en">
The status URL for checking Apache
</longdesc>
<shortdesc lang="en">Apache status URL</shortdesc>
<content type="string" default="${OCF_RESKEY_status_url_default}" />
</parameter>

</parameters>

<actions>
<action name="start" timeout="20" />
<action name="stop" timeout="20" />
<action name="status" timeout="20" />
<action name="monitor" timeout="30" interval="20" />
<action name="validate-all" timeout="5" />
<action name="meta-data" timeout="5" />
</actions>
</resource-agent>
END
}

#######################################################################
# Functions invoked by resource manager actions

service_validate() {
    local rc

    check_binary "$OCF_RESKEY_binary"

    if [[ ! -f $OCF_RESKEY_config ]]; then
        ocf_log err "Config $OCF_RESKEY_config doesn't exist"
        return "$OCF_ERR_INSTALLED"
    fi

    if ! ip netns pids "$OCF_RESKEY_ns" > /dev/null 2>&1; then
        # The network namespace needs to be created first
        ocf_run ip netns add $OCF_RESKEY_ns
        rc=$?
        if [ $rc -ne 0 ]; then
            ocf_log err "Namespace $OCF_RESKEY_ns can't be created (error=$rc)"
            return "$OCF_ERR_INSTALLED"
        fi
        # This is required for Linux kernels >= 3.19. Previously the
        # net.ipv4.ip_nonlocal_bind setting was global to all namespaces but
        # starting with this version, it is per namespace.
        ocf_run ${RUN_IN_NS} /sbin/sysctl -w net.ipv4.ip_nonlocal_bind=1
        ocf_run ${RUN_IN_NS} ip link set up dev lo
    fi

    mkdir -p "$(dirname ${APACHE_PID_FILE})"

    true
}

service_status() {
    local rc
    local pid

    if [ ! -f "$APACHE_PID_FILE" ]; then
        ocf_log info "Apache is not running"
        return "$OCF_NOT_RUNNING"
    else
        pid=$(cat "$APACHE_PID_FILE")
    fi

    if [ -n "${pid}" ]; then
        ocf_run -warn kill -s 0 "$pid"
        rc=$?
        if [ $rc -ne 0 ]; then
            ocf_log info "Old PID file found, but Apache process isn't running"
            return "$OCF_NOT_RUNNING"
        fi
    else
        ocf_log err "PID file ${APACHE_PID_FILE} is empty!"
        return "$OCF_ERR_GENERIC"
    fi

    if [ -n "${OCF_RESKEY_status_url}" ] ; then
        if ! $RUN_IN_NS /usr/bin/curl -sL -w "%{http_code}" -XGET "${OCF_RESKEY_status_url}" -o /dev/null | grep -q 200; then
            return "$OCF_ERR_GENERIC"
        fi
    fi

    return "$OCF_SUCCESS"
}

service_monitor() {
    local rc
    service_status
    rc=$?
    return $rc
}

service_start() {
    local rc

    service_monitor
    rc=$?
    if [ $rc -eq "$OCF_SUCCESS" ]; then
        ocf_log info "${SERVICE_NAME} is already running"
        return "$OCF_SUCCESS"
    fi

    ocf_run ${RUN} bash -c "/usr/local/bin/configure_arp_filter_for_vip"
    rc=$?
    if [ $rc -ne 0 ]; then
      # Try another time to configure ARP filter and adding source-based routes
      # because the VIPs are probably not yet ready hence the sleep.
      sleep 5
      ocf_run ${RUN} bash -c "/usr/local/bin/configure_arp_filter_for_vip"
    fi

    if [ -n "${OCF_RESKEY_ns_gateway}" ] ; then
        ocf_run ${RUN} ip route replace default via ${OCF_RESKEY_ns_gateway}
    fi

    # The envvars script uses the variable APACHE_CONFDIR to support multiple
    # apache2 instances
    export APACHE_CONFDIR
    ocf_run ${RUN} bash -c ". /etc/apache2/envvars && ${OCF_RESKEY_binary} -k start -f $OCF_RESKEY_config"

    # Spin waiting for the server to come up
    while true; do
        service_monitor
        rc=$?
        [ $rc -eq "$OCF_SUCCESS" ] && break
        if [ $rc -ne "$OCF_NOT_RUNNING" ]; then
            ocf_log err "${SERVICE_NAME} start failed"
            exit "$OCF_ERR_GENERIC"
        fi
        sleep 3
    done

    ocf_log info "${SERVICE_NAME} started"
    return "$OCF_SUCCESS"
}

service_stop() {
    local rc
    local pid

    service_monitor
    rc=$?
    if [ $rc -eq "$OCF_NOT_RUNNING" ]; then
        ocf_log info "${SERVICE_NAME} is already stopped"
        return "$OCF_SUCCESS"
    fi

    # Try SIGTERM
    pid=$(cat "$APACHE_PID_FILE")
    ocf_run kill -s TERM "$pid"
    rc=$?
    if [ $rc -ne 0 ]; then
        ocf_log err "${SERVICE_NAME} couldn't be stopped"
        exit "$OCF_ERR_GENERIC"
    fi

    # stop waiting
    shutdown_timeout=15
    if [ -n "$OCF_RESKEY_CRM_meta_timeout" ]; then
        shutdown_timeout=$(( (OCF_RESKEY_CRM_meta_timeout/1000)-5 ))
    fi
    count=0
    while [ $count -lt $shutdown_timeout ]; do
        service_monitor
        rc=$?
        if [ $rc -eq "$OCF_NOT_RUNNING" ]; then
            break
        fi
        count=$(( count + 1))
        sleep 1
        ocf_log debug "${SERVICE_NAME} still hasn't stopped yet. Waiting ..."
    done

    service_monitor
    rc=$?
    if [ "${rc}" -ne "${OCF_NOT_RUNNING}" ]; then
        # SIGTERM didn't help either, try SIGKILL
        ocf_log info "${SERVICE_NAME} failed to stop after ${shutdown_timeout}s using SIGTERM. Trying SIGKILL ..."
        ocf_run kill -s KILL "${pid}"
    fi

    ocf_log info "${SERVICE_NAME} stopped"
    rm -f "${APACHE_PID_FILE}"

    return "${OCF_SUCCESS}"
}

#######################################################################

case "$1" in
  meta-data)    meta_data
                exit "$OCF_SUCCESS";;
  usage|help)   usage
                exit "$OCF_SUCCESS";;
esac

# Anything except meta-data and help must pass validation
service_validate || exit $?

# What kind of method was invoked?
case "$1" in
  start)        service_start;;
  stop)         service_stop;;
  status)       service_status;;
  monitor)      service_monitor;;
  validate-all) ;;
  *)            usage
                exit "$OCF_ERR_UNIMPLEMENTED";;
esac
