#!/bin/bash
################################################################################
# Filename: ./src/base/include/qrm-server-functions
# 
# The contents of this file are subject to the Qlusters Public License
# Version 1.1 (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.openqrm.org/content/view/33/69/.
# 
# Software distributed under the License is distributed on an AS IS basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
# the specific language governing rights and limitations under the License.
# 
# The Original Code is openQRM.
# 
# The Initial Developer of the Original Code is Qlusters Corporation. Portions
# created by Qlusters Corporation are Copyright (C) 2003-2005. All Rights
# Reserved.
# 
# Contributor(s):
# 
# Description:
# 
# Comments:
################################################################################
# Qrm server functions
# 

################## General Server Functions #######################

# function to add a system to the pxe configuration
function qrm_add_to_pxe() {
	MAC_ADDRESS=$1
        RES_ID=$2
        AGENT_TYPE=$3
       IP_ADDRESS=$4
 		if [ -z $MAC_ADDRESS ]; then 
		  qrm_error "qrm_add_to_pxe: Mac Address can't be null"
		  exit 1
		fi
		if [ -z $RES_ID ]; then 
		  qrm_error "qrm_add_to_pxe: ID of resource can't be null"
		  exit 1
		fi
		if [ -z $AGENT_TYPE ]; then 
		  qrm_error "qrm_add_to_pxe: Agent type can't be null"
		  exit 1
		fi
        CURRENT=`pwd`
	RES_NAME="$AGENT_TYPE$RES_ID"
        qrm_get_server_config 1>/dev/null
        if [ "$MAC_ADDRESS" == "00:00:00:00:00:00" ]; then
                echo "System with 00:00:00:00:00:00 mac address detected. Not adding to pxe-config"
                return 1
        else
                echo $MAC_ADDRESS
                echo $RES_ID
		echo $RES_NAME
                PXECONF=`echo "01-$MAC_ADDRESS" | sed "s/:/-/g" | tr [:upper:] [:lower:]`
                PXENETBOOT="$PXECONF.netboot"
                cat > $QRM_SERVER_BASE_DIR/qrm/tftpboot/pxelinux.cfg/$PXENETBOOT << EOF
default linux

label linux
kernel boot/$RES_NAME-kernel
append ramdisk_size=32768 apm=off initrd=boot/$RES_NAME-initrd ${AGENT_TYPE}id=$RES_ID mac=$MAC_ADDRESS qrm=$QRM_SERVER_IP_ADDRESS
ipappend 3
EOF
                chmod 777 $QRM_SERVER_BASE_DIR/qrm/tftpboot/pxelinux.cfg/$PXENETBOOT
                # link to netboot pxe config file
                cd $QRM_SERVER_BASE_DIR/qrm/tftpboot/pxelinux.cfg/
                ln -sf $PXENETBOOT $PXECONF
                # link kernel + initrd
                cd $QRM_SERVER_BASE_DIR/qrm/tftpboot/boot
                /bin/ln -sf vmlinuz-qrm $RES_NAME-kernel
                /bin/ln -sf initrd-qrm.img $RES_NAME-initrd
                cd $CURRENT
                # running new-node hook
                $QRM_SERVER_HOOKS new_node \"$RES_ID\" \"$IP_ADDRESS\" \"$MAC_ADDRESS\"
                return 0
        fi
}



function qrm_remove_node() {
	CS_ID=$1
	MAC_ADDRESS=$2
	IP_ADDRESS=$3
	CS_TO_REMOVE=node$CS_ID
	PXECONF=`echo "01-$MAC_ADDRESS" | sed "s/:/-/g" | tr [:upper:] [:lower:]`
	PXENETBOOT="$PXECONF.netboot"
	/bin/rm $QRM_SERVER_BASE_DIR/qrm/tftpboot/pxelinux.cfg/$PXECONF $QRM_SERVER_BASE_DIR/qrm/tftpboot/pxelinux.cfg/$PXENETBOOT
	/bin/rm -f $QRM_BOOT_IMAGE_DIR/$CS_TO_REMOVE-*
	# running node-remove hook
	$QRM_SERVER_HOOKS remove_node \"$CS_ID\" \"$IP_ADDRESS\" \"$MAC_ADDRESS\"
}

function qrm_remove_vm() {
	VM_ID=$1
	MAC_ADDRESS=$2
	IP_ADDRESS=$3
	VM_TO_REMOVE=vm$VM_ID
	PXECONF=`echo "01-$MAC_ADDRESS" | sed "s/:/-/g" | tr [:upper:] [:lower:]`
	PXENETBOOT="$PXECONF.netboot"
	has_vm=`cat $QRM_SERVER_BASE_DIR/qrm/tftpboot/pxelinux.cfg/$PXECONF | grep  vmid=`
	if [ "$has_vm" != "" ]; then
		/bin/rm -f  $QRM_SERVER_BASE_DIR/qrm/tftpboot/pxelinux.cfg/$PXECONF $QRM_SERVER_BASE_DIR/qrm/tftpboot/pxelinux.cfg/$PXENETBOOT
		/bin/rm -f $QRM_BOOT_IMAGE_DIR/$VM_TO_REMOVE-*
	else
        qrm_error "qrm_remove_vm: Could not find $QRM_SERVER_BASE_DIR/qrm/tftpboot/pxelinux.cfg/$PXECONF for virtual machine $VM_ID"
	fi
	
}



# switch local + netboot + xen
function qrm_setboot() {
        local BOOT_FROM=$1
        local CS_ID=$2
        local MAC_ADDRESS=$3
        local CS_TYPE=$4
        CURRENT=`pwd`
        PXECONF=`echo "01-$MAC_ADDRESS" | sed "s/:/-/g" | tr [:upper:] [:lower:]`
        PXENETBOOT="$PXECONF.netboot"
        if grep -w "${CS_TYPE}id=$CS_ID" $QRM_SERVER_BASE_DIR/qrm/tftpboot/pxelinux.cfg/$PXENETBOOT 1>/dev/null 2>&1; then
                cd $QRM_SERVER_BASE_DIR/qrm/tftpboot/pxelinux.cfg/
                case "$BOOT_FROM" in
                        local)
                                ln -sf localboot $PXECONF
                                ;;
                        net)
                                ln -sf $PXENETBOOT $PXECONF
                                ;;
                        *)
                                qrm_error "qrm_setboot: No such command $BOOT_FROM for setting boot"
                                return 1
                                ;;
                esac
                cd $CURRENT
                return 0
        else
                qrm_error "qrm_setboot: Can't find pxe file with specified ${CS_TYPE}id $CS_ID"
                return 1;
        fi
}

# function to return the user-configuration from the qrm.ini
function qrm_get_server_config() {
	CONFIG_FILE=$QRM_SERVER_BASE_DIR/qrm/etc/qrm.ini
	CONFIG=`cat $CONFIG_FILE | sed -n "/^\[Config\]/,/^\[/s/^\([^\[;].\+\)=\(.\+\)/\1='\2'/p"`
	INSTALL_PLUGINS=`cat $CONFIG_FILE | sed -n "/^\[Plugins\]/,/^\[/s/^[^\[;]\+=\(.\+\)/\1/p"`
	INSTALL_PLUGINS=`echo $INSTALL_PLUGINS | sed -e "s/\n//g"`
	for v in $CONFIG; do
		NAME=QRM_`echo ${v/=*/} | tr [:lower:] [:upper:]`
		eval "export $NAME=${v/*=}"
		echo "$NAME=${v/*=}"
	done

	ENABLED_PLUGINS=
	for PLUGIN in $QRM_SERVER_BASE_DIR/qrm/plugins/*; do
		PLUGIN=`basename $PLUGIN`
		CONFIG_FILE=$QRM_SERVER_BASE_DIR/qrm/plugins/$PLUGIN/etc/$PLUGIN.ini
		if [ -f $CONFIG_FILE ]; then
			ENABLED=`cat $CONFIG_FILE | sed -n "/^\[Plugin\]/,/^\[/s/^\([^\[;].\+\)=\(.\+\)/\1=\2/p" | grep -i '^enabled=' | tr [:lower:] [:upper:]`
			if [ "${ENABLED/*=/}" == "TRUE" ] || [ "${ENABLED/*=/}" == "YES" ]; then
				ENABLED_PLUGINS="${ENABLED_PLUGINS:+"$ENABLED_PLUGINS "}$PLUGIN"
			fi
		fi
	done

	for PLUGIN in $QRM_SERVER_BASE_DIR/qrm/plugins/*; do
		PLUGIN=`basename $PLUGIN`
		PREFIX=$(echo $PLUGIN | tr '[:lower:]-' '[:upper:]_')
		CONFIG_FILE=$QRM_SERVER_BASE_DIR/qrm/plugins/$PLUGIN/etc/$PLUGIN.ini	
		if [ -f $CONFIG_FILE ]; then
			CONFIG=`cat $CONFIG_FILE | sed -n "/^\[Config\]/,/^\[/s/^\([^\[;].\+\)=\(.\+\)/\1='\2'/p"`
			for v in $CONFIG; do
				NAME=QRM_$PREFIX\_`echo ${v/=*/} | tr [:lower:] [:upper:]`
				eval "export $NAME=${v/*=}"
				echo "$NAME=${v/*=}"
			done
		fi
	done

	QRM_SERVER_IP_ADDRESS=`ip addr show | grep inet | grep -w "$QRM_SERVER_INTERFACE" | cut -d'/' -f 1 | awk '{ print $2 }' | head -n 1`
	QRM_SERVER_SUBNET_MASK=`ifconfig | grep -w "$QRM_SERVER_IP_ADDRESS" | cut -d':' -f 4 | awk {' print $1 '}`
	QRM_SERVER_BROADCAST_ADDRESS=`ipcalc -s -b $QRM_SERVER_IP_ADDRESS $QRM_SERVER_SUBNET_MASK | cut -d'=' -f 2`
	QRM_SERVER_NETWORK=`ipcalc -s -n $QRM_SERVER_IP_ADDRESS $QRM_SERVER_SUBNET_MASK | cut -d'=' -f 2`
	echo "ENABLED_PLUGINS=\"$ENABLED_PLUGINS\""
	echo "QRM_SERVER_BASE_DIR=\"$QRM_SERVER_BASE_DIR\""
	echo "QRM_SERVER_LOG_DIR=\"$QRM_SERVER_LOG_DIR\""
	echo "QRM_NODES_BASE_DIR=\"$QRM_NODES_BASE_DIR\""
	echo "QRM_NODES_LOG_DIR=\"$QRM_NODES_LOG_DIR\""
	echo "QRM_SERVER_IP_ADDRESS=\"$QRM_SERVER_IP_ADDRESS\""
	echo "QRM_SERVER_SUBNET_MASK=\"$QRM_SERVER_SUBNET_MASK\""
	echo "QRM_SERVER_BROADCAST_ADDRESS=\"$QRM_SERVER_BROADCAST_ADDRESS\""
	echo "QRM_SERVER_NETWORK=\"$QRM_SERVER_NETWORK\""
	export ENABLED_PLUGINS QRM_SERVER_IP_ADDRESS QRM_SERVER_SUBNET_MASK QRM_SERVER_BROADCAST_ADDRESS QRM_SERVER_NETWORK
}


# function to return the qrm server version
function qrm_get_server_version() {
	CONFIG_FILE=$QRM_SERVER_BASE_DIR/qrm/etc/qrm.ini
	CONFIG=`cat $CONFIG_FILE | sed -n "/^\[General\]/,/^\[/s/^\([^\[;].\+\)=\(.\+\)/\1='\2'/p"`
	for v in $CONFIG; do
		NAME=QRM_`echo ${v/=*/} | tr [:lower:] [:upper:]`
		eval "export $NAME=${v/*=}"
	done
	echo "$QRM_VERSION"
}


# function to assign a node to a boot-image
function qrm_assign() {
        res=0
        AGENT_TYPE=$1
        cd $QRM_SERVER_BASE_DIR/qrm/tftpboot/boot
    	if [ "$COUNTER" == "" ]; then
                qrm_error "qrm_assign: COUNTER variable is not defined !!! Cannot circle through node list"
                return 1
        fi

        if [ "$AGENT_TYPE" == "vm" ] ; then
                for ((i=1; i<=$COUNTER; i++)); do
                VMNR=$(eval echo \${VM_ID_$i})
                WHICHKERNEL=$(eval echo \${VM_KERNEL_$i})
                qnotice "Assigning virtual machine $VMNR to $WHICHKERNEL now"

               rm -f vm$VMNR-kernel vm$VMNR-initrd
                /bin/ln -sf vmlinuz-$WHICHKERNEL vm$VMNR-kernel
                /bin/ln -sf initrd-$WHICHKERNEL.img vm$VMNR-initrd

                if [ $? -gt 0 ]; then
                        res=$?
                        echo $VMNR
                fi
                done
        else 
			for ((i=1; i<=$COUNTER; i++)); do
		        NODENR=$(eval echo \${NODE_ID_$i})
		        WHICHKERNEL=$(eval echo \${NODE_KERNEL_$i})
		        qnotice "Assigning node $NODENR to $WHICHKERNEL now"
		
		       rm -f node$NODENR-kernel node$NODENR-initrd
		        /bin/ln -sf vmlinuz-$WHICHKERNEL node$NODENR-kernel
		        /bin/ln -sf initrd-$WHICHKERNEL.img node$NODENR-initrd
		
		        if [ $? -gt 0 ]; then
		                res=$?
		                echo $NODENR
		        fi
			done
	
		fi
		return $res
}





# lock function
function qrm_run_in_lock() {
	PROG=$@
	DEFAULTLOCK="/var/lock/subsys/`basename $PROG`"
	LOCK=${LOCK:-"$DEFAULTLOCK"}
	trap "" 2
	if ! lockfile -l 10 $LOCK; then
		qrm_error "qrm_run_in_lock: Could not create lock $LOCK"
		exit 2
	fi
	trap "/bin/rm -f $LOCK;exit 3" 2

	qdebug "running $PROG in lock $LOCK"
	$PROG
	rm -f $LOCK
}



# gets a predefined node id from the global config file
function qrm_get_predefined_node_id() {
	NEW_CS_ID="-1"
	if (( $# != 1 )); then
		echo $NEW_CS_ID
		return 6
	fi

	QRM_CS_MAC_ADDRESS="`echo $1 | tr A-Z a-z`"
# special case for nodes simulator!
#Here we detecet virtual node by seeing the first cell to be ff:qrm_id:xx:xx:xx:xx
#and taking the last cell as node id. Need to remove leading zero from numbers
#like 1,2,..9. Don't want them to be 01,02,....09
	res=`echo $QRM_CS_MAC_ADDRESS | awk {'if ($1 ~ "ff:..:..:..:..:..") print 0
else print 1}'`

	if [ "$res" == "0" ]; then
#We need to convert last field of MAC address, which is hexeminal
#back to decimal id number
#First of all converting the last 4 fields back into decimal number
		h_num=(`echo $QRM_CS_MAC_ADDRESS | awk 'BEGIN {FS = ":"}; {print $3" "$4" "$5" "$6}'`)

		d_num=0
		n_num=0
		for field in ${h_num[*]}; do
			if [ $field == "ff" ]; then
#Do nothing. This field was paddet with ff to save the MAC format
				continue
			fi

			if [ $field == "fe" ]; then
#This filed converts to number 254
				n_num=`expr $n_num + 254 filed=$field`
				continue
			fi

#If we are here than it is time to do the real conversation
			tmp_num=`printf "%d" 0x$field`
			n_num=`expr $n_num + $tmp_num`
		done

		NEW_CS_ID=$n_num

		qdebug "Virtual node detected. Node MAC is $QRM_CS_MAC_ADDRESS and node ID is $NEWNODEID"
	elif [ -f $QLUSTERSNODESCONF ]; then
		qdebug "New node $QRM_CS_MAC_ADDRESS detected. Using the qrm-nodes config from $QLUSTERSNODESCONF"
		lockfile -1 $QRM_SERVER_LOG_DIR/confserver.lock
		CS_OWNER=`grep -i $QRM_CS_MAC_ADDRESS $QLUSTERSNODESCONF | head -n 1 | awk {' print $3 '}`
		/bin/rm -f  $QRM_SERVER_LOG_DIR/confserver.lock
		if [ "$CS_OWNER" != "$MYSELF" ]; then
			qdebug "$QRM_CS_MAC_ADDRESS is not my node ($MYSELF). It belongs to $CS_OWNER"
		else
			NEW_CS_ID=`grep -i $QRM_CS_MAC_ADDRESS $QLUSTERSNODESCONF | head -n 1 | awk {' print $1 '}`
		fi
	fi
	echo $NEW_CS_ID
}




function qrm_execute_script_policy() {
	POLICY=$1
	shift
	 
	ENV=$1
	shift
	 
	eval $ENV
	. $POLICY 
	
}

# check if a function is implemented 
function qrm_func_is_implemented() {
	declare -F $1 1>/dev/null
	return $?
}



#pings remote server
function qrm_ping(){
 if [ $# -lt 1 ]; then
    echo "Usage: qrm_ping.sh <host/ip>"
    exit
 fi

 host=$1

 if ! /bin/ping -c 1 -w 2 $host 1>/dev/null 2>&1; then
    #failure
    exit 1
 else
    #success
    exit 0
 fi
}


# function to send a qrm-event
function qrm_send_event() {
	q_statistics_servlet="http://${QRM_SERVER_IP_ADDRESS}:${QRM_EVENT_MONITOR_PORT}/monitor/submit.do"
	NID=$1
	NIP=$2
	QEVENT=$3
	CS_TYPE=$4
	MESSAGEDATA="command=node_event&event=$QEVENT&agent=$CS_TYPE&node_id=$NID&node_ip=$NIP"
	wget -q -O /dev/null "${q_statistics_servlet}?$MESSAGEDATA" &
}

# checks when a node gets unpingable
function qrm_checkoffline() {
        local IPADDRESS=$1
        local CS_ID=$2
        local QRMOPTION=$3
        local MAX_LOOP=24
        local CS_TYPE=$4

        case "$QRMOPTION" in
                        start)
                                loop=0
                                while [ $loop -lt $MAX_LOOP ]; do
                                        if ! ping -c 1 -w 1 $IPADDRESS 1>/dev/null 2>&1; then
                                                qrm_send_event $CS_ID $IPADDRESS off $CS_TYPE
                                                break
                                        fi
                                        ((loop++))
                                        sleep 5
                                done
                                ;;

                        stop)
                                for QRMCHECKOFFLINEPID in `ps auxww | grep "qrm_checkoffline $IPADDRESS $CS_ID start" | grep -v grep | awk {' print $2 '}`; do
                                        kill $QRMCHECKOFFLINEPID 1>/dev/null 2>&1
                                done
                                ;;
        esac

}




# read a given file, evaluating each line so that any variable reference is replaced by its value, and
# commands can be executed. Note: wildcard expansion is disabled
function interpolate() {
        local exp=${-##*f*}
        local old_ifs="$IFS"
        set -f
        IFS=
        cat $1 | while read LINE; do
                LINE="${LINE//\"/\\\"}"
                eval echo \"$LINE\"
        done
        if [ -z "$exp" ]; then
                set +f
        fi
        IFS="$old_ifs"
}

function interpolate_in_place() {
	local orig=$1
	local base=`basename $orig`
	local temp_dir=${QRM_TEMP:-/tmp}
	local tmp=`mktemp $temp_dir/$base.XXXXXX` || exit $?
	interpolate $orig > $tmp 
	[ -s $tmp ] || exit $?
	chmod `stat -c %a $orig` $tmp
	mv -f $tmp $orig
}



function wait_for_pattern_in_file() {
	PATTERN=$1
	TIMEOUT=$2
	PROGRESS_PATTERN=$3
	FILE=${4:-/var/log/messages}

	PIPEDIR=$(mktemp -d /tmp/pattern_wait.XXXXXX)
	PIPE="$PIPEDIR/$(echo /var/log/messages | sed 's#^/##; s#/#.#g')"
	rm -f $PIPE
	mknod $PIPE p
	tail -Fn1 $FILE >$PIPE &
	TAILPID=$!
	exec 5< $PIPE

	RES=0

	TIME_TO_PROGRESS=$((`date +%s` + TIMEOUT))

	# Repeatedly read from the pipe, process each line.
	while :; do
		while read -t 1 REPLY <&5; do
			# echo read $REPLY
			if [[ -z "${REPLY##*$PATTERN*}" ]]; then
				break 2;
			fi
			if [[ -z "${REPLY##*$PROGRESS_PATTERN*}" ]]; then
				# echo $REPLY shows progress
				TIME_TO_PROGRESS=$((`date +%s` + TIMEOUT)) #reset
			else
				if  [ `date +%s` -gt $TIME_TO_PROGRESS ]; then
					# echo Timed out
					RES=1
					break 2;
				fi
			fi
		done
		# echo after read
		# We get here whenever the pipe first blocks (returns EOF)
		# sleep 1                       # Don't loop too quickly
		if  [ `date +%s` -gt $TIME_TO_PROGRESS ]; then
			# echo Timed out
			RES=1
			break;
		fi
	done

	rm -rf $PIPEDIR
	return $RES
}








