#! /bin/sh
#
# Copyright 2006-2010 Red Hat, Inc. and/or its affiliates.
#
# Licensed to you under the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.  See the files README and
# LICENSE_GPL_v2 which accompany this distribution.
#

# chkconfig: 2345 99 00
#
### BEGIN INIT INFO
# Provides: vdsmd
# Required-Start: $syslog $network
# Should-Start: $time
# Required-Stop: $syslog
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Description: init script for the VDS management server
# Short-Description: init script for the VDS management server
### END INIT INFO

VDSM_BIN=/usr/share/vdsm//vdsm
CONF_FILE=/etc/vdsm/vdsm.conf
GETCONFITEM=/usr/share/vdsm//get-conf-item
prog=vdsm
PIDFILE=/var/run/vdsm//vdsmd.pid
RESPAWNPIDFILE=/var/run/vdsm//respawn.pid
CORE_DUMP_PATH=/var/log/core/core.%p.%t.dump
DOM_METADATA_BACKUP_DIR=/var/log/vdsm/backup
CORE_PATTERN=/proc/sys/kernel/core_pattern
NEEDED_SERVICES="iscsid multipathd"
CONFLICTING_SERVICES="libvirt-guests libvirtd"

LCONF=/etc/libvirt/libvirtd.conf
QCONF=/etc/libvirt/qemu.conf
LDCONF=/etc/sysconfig/libvirtd

is_coredump=`$GETCONFITEM $CONF_FILE vars core_dump_enable false | tr A-Z a-z`
[ $is_coredump != true ] && is_coredump=false

. /etc/init.d/functions

log_failure_msg() { echo -n "$@"; failure "$@"; echo; }
log_success_msg() { echo -n "$@"; success "$@"; echo; }

check_port_taken() {
    local MANAGEMENT_PORT MANAGEMENT_IP
    MANAGEMENT_PORT=`$GETCONFITEM $CONF_FILE addresses management_port ""`
    if [ -z "$MANAGEMENT_PORT" ]; then
        log_failure_msg "$prog: management_port not found in $CONF_FILE"
        return 1
    fi
    MANAGEMENT_IP=`$GETCONFITEM $CONF_FILE addresses management_ip 0.0.0.0`
    netstat -ntl | grep -q "$MANAGEMENT_IP:$MANAGEMENT_PORT"
    RETVAL=$?
    if [ "$RETVAL" -eq 0 ]; then
        log_failure_msg "$prog: port $MANAGEMENT_PORT already bound"
        return 1
    fi
    return 0
}

mk_data_center() {
    local dc
    dc=`$GETCONFITEM $CONF_FILE irs repository /rhev/`
    /bin/mkdir -p "$dc"
    /bin/chown vdsm.kvm "$dc"
}

mk_dom_backup() {
    /bin/mkdir -p ${DOM_METADATA_BACKUP_DIR} > /dev/null 2>&1
    /bin/chown vdsm.kvm ${DOM_METADATA_BACKUP_DIR} > /dev/null 2>&1
}

mk_upgrade_path() {
    if ! [ -d "/data/updates" ]; then
        /bin/mkdir -p /data/updates > /dev/null 2>&1
        /bin/chmod 755 /data/updates > /dev/null 2>&1
    fi
}

mk_core_path() {
    core_path=/var/log/core
    if ! [ -d $core_path ]; then
        /bin/mkdir -p $core_path > /dev/null 2>&1
    fi
    /bin/chmod a+tw $core_path > /dev/null 2>&1
}

get_libvirt_conf_item() {
    local cfile key

    cfile=$1
    key=$2
    /bin/grep "^\s*$key\s*=" "$cfile" | \
            /usr/bin/tail -1 | /bin/sed "s/\s*$key\s*=\s*//;s/\s*\(#.*\)\?$//"
}

test_conflicting_conf() {
    local listen_tcp auth_tcp ssl

    ssl=`$GETCONFITEM $CONF_FILE vars ssl true | tr A-Z a-z`
    [ "$ssl" == true ] && return 0

    listen_tcp="`get_libvirt_conf_item $LCONF listen_tcp`"
    auth_tcp="`get_libvirt_conf_item $LCONF auth_tcp`"
    spice_tls="`get_libvirt_conf_item $QCONF spice_tls`"

    if [ "$listen_tcp" == 1 -a "$auth_tcp" == '"none"' -a "$spice_tls" == 0 ];
    then
        return 0
    else
        echo "conflicting vdsm and libvirt tls configuration."
        echo "vdsm.conf with ssl=False requires libvirt with:"
        echo "listen_tcp=1, auth_tcp=\"none\" and spice_tls=0."
        return 1
    fi
}

shutdown_conflicting_srv() {
    local srv

    for srv in $CONFLICTING_SERVICES
    do
        /sbin/chkconfig $srv off
        if /sbin/service $srv status > /dev/null 2>&1;
        then
            if [ "$srv" == "libvirt-guests" ]; then
                /bin/rm -f /var/lock/subsys/libvirt-guests
            else
                /sbin/service $srv stop
            fi
        fi
    done
    return 0
}

start_needed_srv() {
    local srv
    local ret_val

    for srv in $NEEDED_SERVICES
    do
        if ! /sbin/service $srv status > /dev/null 2>&1;
        then
            echo "Starting $srv..."
            /sbin/service $srv start
            ret_val=$?
            if [ $ret_val -ne 0 ]
            then
                log_failure_msg "$prog: Dependent $srv failed to start"
                return $ret_val
            fi
        fi
    done

    /sbin/service iscsid force-start
}

test_lo() {
    if ! LC_ALL=C /sbin/ifconfig lo | /bin/grep -q UP;
    then
        log_failure_msg "VDSMD: lo interface is down, can't run !"
        echo "VDSMD: lo interface is down, can't run !" > /dev/kmsg
        return 1
    fi
    return 0
}

free_space() {
    local path=$1
    df -P "$path" | awk '{print $4}'| tail -1
}

test_space() {
    local MIN_SPACE_KB=10000

    if [ "`free_space /var/log/vdsm`" -lt "$MIN_SPACE_KB" ]; then
        log_failure_msg "$prog: low log space"
        return 1
    fi
    return 0
}

bond_dev_available() {
    local BOND_DEVS=$(/bin/cat /sys/class/net/bonding_masters)
    local x

    [[ -z "$BOND_DEVS" ]] && return 1

    for x in $BOND_DEVS; do
        [[ "$x" == "$1" ]] && return 0
    done

    return 1
}

load_needed_modules() {
    local b

    /sbin/modprobe tun
    /sbin/modprobe bonding
    # RHEV-M currently assumes that all bonding devices pre-exist
    for b in bond{0,1,2,3,4}; do
        if ! bond_dev_available $b; then
            echo +$b > /sys/class/net/bonding_masters 2>/dev/null
        fi
    done
    /sbin/modprobe 8021q
}

test_already_running()
{
    if pidofproc -p $RESPAWNPIDFILE >/dev/null || \
       pidofproc -p $PIDFILE $VDSM_BIN >/dev/null; then
        log_success_msg "$prog: already running"
        return 0
    fi
    return 1
}

# configure libvirt to vdsm's needs
configure_libvirt()
{
    local force_reconfigure="$1"
    local BY_VDSM="# by vdsm"
    local ts=/etc/pki/vdsm

    remove_vdsm_conf() {
        sed -i --copy "/$BY_VDSM/d" "$1"
    }

    set_if_default() {
        local cfile key val
        cfile="$1"
        key="$2"
        val="$3"

        /bin/grep -q "^\s*$key\s*=" "$cfile" || \
          echo "$key=$val $BY_VDSM" >> "$cfile"
    }

    if [ -f /etc/rhev-hypervisor-release ];
    then
        . /usr/libexec/ovirt-functions
    else
        ovirt_store_config() { :; }
    fi

    local lconf qconf ldconf
    local ssl=`$GETCONFITEM $CONF_FILE vars ssl true | tr A-Z a-z`

    lconf="$2"
    qconf="$3"
    ldconf="$4"

    if [ "$force_reconfigure" == force ];then
        remove_vdsm_conf $lconf
        remove_vdsm_conf $qconf
        remove_vdsm_conf $ldconf
    fi

    # do not configure ovirt nodes before registration
    if [ -f /etc/rhev-hypervisor-release -a \
      ! -f $ts/certs/vdsmcert.pem ]
    then
        log_failure_msg "$prog: Missing certificates, $prog not registered "
        return 6
    fi

    # do not reconfigure, return 0, so that vdsm start can continue.
    if grep -q "$BY_VDSM" $lconf && grep -q "$BY_VDSM" $qconf
    then
        log_success_msg $"$prog: libvirt already configured for vdsm "
        return 0
    fi

    echo $"Configuring libvirt for vdsm..."

    # Set the default values for libvirt and qemu
    set_if_default $lconf listen_addr \"0\"
    set_if_default $lconf unix_sock_group \"kvm\"
    set_if_default $lconf unix_sock_rw_perms \"0770\"
    set_if_default $lconf auth_unix_rw \"sasl\"
    set_if_default $qconf dynamic_ownership 0
    if [[ "$ssl" == true ]]; then
        set_if_default $qconf spice_tls 1
    else
        set_if_default $qconf spice_tls 0
    fi
    set_if_default $ldconf LIBVIRTD_ARGS --listen
    set_if_default $ldconf DAEMON_COREFILE_LIMIT unlimited
    set_if_default $lconf save_image_format \"lzop\"
    # FIXME until we are confident with libvirt integration, let us have a verbose log
    set_if_default $lconf log_outputs \"1:file:/var/log/libvirtd.log\"
    set_if_default $lconf log_filters "\"1:libvirt 3:event 3:json 1:util 1:qemu\""

    # If the ssl flag is set, update the libvirt and qemu configuration files
    # with the location for certificates and permissions.
    if [ -f $ts/certs/cacert.pem -a \
         -f $ts/certs/vdsmcert.pem -a \
         -f $ts/keys/vdsmkey.pem -a \
         "$ssl" == true ];
    then
        set_if_default $lconf ca_file \"$ts/certs/cacert.pem\"
        set_if_default $lconf cert_file \"$ts/certs/vdsmcert.pem\"
        set_if_default $lconf key_file \"$ts/keys/vdsmkey.pem\"
        set_if_default $qconf spice_tls_x509_cert_dir \"$ts/libvirt-spice\"
    else
        set_if_default $lconf auth_tcp \"none\"
        set_if_default $lconf listen_tcp 1
        set_if_default $lconf listen_tls 0
    fi

    local lnetwork=/etc/libvirt/qemu/networks/autostart/default.xml
    rm -f $lnetwork

    local llogr=/etc/logrotate.d/libvirtd
    local stanza=`mktemp`
    /bin/cat > "$stanza" <<EOF
# vdsm
/var/log/libvirtd.log {
    rotate 100
    missingok
    copytruncate
    size 15M
    compress
    compresscmd /usr/bin/xz
    uncompresscmd /usr/bin/unxz
    compressext .xz
}
# end vdsm
EOF
    /bin/sed -e "/# vdsm/,/# end vdsm/d" "$llogr" >> "$stanza"
    local oldmod=`/usr/bin/stat --format=%a "$llogr"`
    /bin/mv "$stanza" "$llogr"
    if [ -n "$oldmod" ]; then
       /bin/chmod "$oldmod" "$llogr"
    fi
    restorecon "$llogr"

    ovirt_store_config "$lconf" "$qconf" "$ldconf" "$llogr"

    # vdsm makes extensive use of nfs-exported images
    /usr/sbin/semanage  boolean -m -S targeted -F /dev/stdin  << _EOF
virt_use_nfs=1
_EOF
    /usr/sbin/setsebool virt_use_nfs on

    /sbin/initctl restart libvirtd 2>/dev/null || :
}

configure_sysctl()
{
    local VDSM_SYSCTL_BGN VDSM_SYSCTL_END VDSM_SYSCTL_VER
    VDSM_SYSCTL_BGN="# VDSM section begin"
    VDSM_SYSCTL_END="# VDSM section end"
    VDSM_SYSCTL_VER="$VDSM_SYSCTL_BGN (v.1)"
    grep -q "$VDSM_SYSCTL_VER" /etc/sysctl.conf
    if [ $? -ne 0 ]; then
        echo $"Configuring sysctl for vdsm..."
        sed -e "/vm.dirty_ratio/d" \
            -e "/vm.dirty_background_ratio/d" \
            -e "/$VDSM_SYSCTL_BGN/,/$VDSM_SYSCTL_END/d" \
            /etc/sysctl.conf > /etc/sysctl.conf.vdsm
        cat >> /etc/sysctl.conf.vdsm <<EOF
$VDSM_SYSCTL_VER
vm.dirty_ratio = 5
vm.dirty_background_ratio = 2
$VDSM_SYSCTL_END
EOF
        mv -f /etc/sysctl.conf.vdsm /etc/sysctl.conf
        sysctl -q -p /etc/sysctl.conf
    fi
}

RETVAL=0

reconfigure() {
    local args="$@"
    case $# in
        0)
            args="force $LCONF $QCONF $LDCONF"
            ;;
        1)
            # Use default paths
            args="$args $LCONF $QCONF $LDCONF"
            ;;
    esac
    configure_libvirt $args
    configure_sysctl $args
}

start_libvirtd() {
    local packaged target
    local startout

    packaged=`/bin/rpm -ql libvirt | /bin/grep libvirtd.upstart | \
                 /usr/bin/tail -1`
    target=/etc/init/libvirtd.conf

    if ! diff -q "$packaged" "$target" >/dev/null;
    then
        /bin/cp -p "$packaged" "$target" || return 1
        /sbin/initctl reload-configuration
    fi

    startout=`/sbin/initctl start libvirtd 2>&1`
    if [[ "$?" -eq 0 || "$startout" =~ .*already\ running.* ]];
    then
        return 0
    else
        echo "$startout" >&2
        return 1
    fi
}

start() {
    local ret_val
    python /usr/share/vdsm//hooks.pyc before_vdsm_start

    shutdown_conflicting_srv

    reconfigure noforce
    ret_val=$?
    if [ $ret_val -ne 0 ]
    then
        log_failure_msg "$prog: failed to reconfigure libvirt"
        return $ret_val
    fi

    start_needed_srv && start_libvirtd
    ret_val=$?
    if [ $ret_val -ne 0 ]
    then
       log_failure_msg "$prog: one of the dependent services did not start, error code $ret_val"
       return $ret_val
    fi

    /usr/share/vdsm//vdsm-restore-net-config
    load_needed_modules
    mk_data_center
    mk_upgrade_path
    mk_core_path
    mk_dom_backup
    /bin/chmod 1777 /dev/shm
    if [ $is_coredump == true ]; then
        export DAEMON_COREFILE_LIMIT=unlimited
        echo $CORE_DUMP_PATH > $CORE_PATTERN
    fi
    [ `ulimit -n` -lt 4096 ] && ulimit -n 4096

    test_already_running && return 0

    if ! (test_space && test_lo && \
          check_port_taken && \
          test_conflicting_conf); then
        return 1
    fi

    echo $"Starting up vdsm daemon: "
    local vdsm_nice=`$GETCONFITEM $CONF_FILE vars vdsm_nice -5`

    LIBVIRT_LOG_FILTERS=`$GETCONFITEM $CONF_FILE vars libvirt_log_filters "1:libvirt 1:remote"` \
    LIBVIRT_LOG_OUTPUTS=`$GETCONFITEM $CONF_FILE vars libvirt_log_outputs "1:file:/var/log/vdsm/libvirt.log"` \
    LC_ALL=C NICELEVEL=$vdsm_nice daemon --user=vdsm /usr/share/vdsm//respawn --minlifetime 10 --daemon --masterpid $RESPAWNPIDFILE $VDSM_BIN
    RETVAL=$?
    [ "$RETVAL" -eq 0 ] && log_success_msg $"$prog start" || log_failure_msg $"$prog start"
    [ "$RETVAL" -eq 0 ] && touch /var/lock/subsys/vdsmd
}

stop() {
    echo $"Shutting down vdsm daemon: "
    if killproc -p $RESPAWNPIDFILE; then
        log_success_msg $"$prog watchdog stop"
    fi
    if ! pidofproc -p $PIDFILE >/dev/null; then
        log_failure_msg "$prog: not running"
        RETVAL=0
    else
        killproc -p $PIDFILE -d 2
        RETVAL=$?
        [ "$RETVAL" -eq 0 ] && log_success_msg $"$prog stop" || log_failure_msg $"$prog stop"
        [ "$RETVAL" -eq 0 ] && rm -f /var/lock/subsys/vdsmd
    fi
    python /usr/share/vdsm//hooks.pyc after_vdsm_stop
    return $RETVAL
}

case "$1" in
     start)
        start
	RETVAL=$?
	;;
     stop)
        stop
	RETVAL=$?
	;;
     status)
	pidofproc -p $PIDFILE $VDSM_BIN >/dev/null
	RETVAL=$?
	if [ "$RETVAL" -eq 0 ]; then
	    echo "VDS daemon server is running"
	else
	    echo -n "VDS daemon is not running"
            if pidofproc -p $RESPAWNPIDFILE >/dev/null; then
                echo ", but its watchdog is"
            else
                echo
            fi
	fi
	;;
     condrestart)
	pidofproc -p $PIDFILE $VDSM_BIN >/dev/null
	RETVAL=$?
	if [ "$RETVAL" -eq 0 ]; then
	    $0 stop && $0 start;
	    RETVAL=$?;
	fi;
        ;;
     try-restart)
	$0 stop && $0 start
	RETVAL=$?
	;;
     restart|force-reload)
	$0 stop
	$0 start
	RETVAL=$?
	;;
    reconfigure)
        # Jump over 'reconfigure'
        shift 1
        reconfigure "$@"
	RETVAL=$?
    ;;
     *)
	echo "Usage: $0 {start|stop|status|restart|force-reload|try-restart}"
	RETVAL=2
esac

exit $RETVAL
