#! /bin/sh
# Copyright (c) 2010, Novell Inc.
#
# Author: adrian@suse.de
#
# /etc/init.d/obsstoragesetup
#   and its symbolic link
# /usr/sbin/rcobsstoragesetup
#
### BEGIN INIT INFO
# Provides:          obsstoragesetup
# Start-Before:      mysql
# Required-Start:    $network boot.lvm
# Required-Stop:     $null
# Default-Start:     3 5
# Default-Stop:      0 1 2 4 6
# Description:       Finds the storage device to be used for OBS server and/or worker
### END INIT INFO

. /etc/rc.status

# package or appliance defaults
if [ -e /etc/sysconfig/obs-server ]; then
  source /etc/sysconfig/obs-server
else
  source /etc/sysconfig/obs-worker
fi

# instance defaults
if [ -e /etc/buildhost.config ]; then
  source /etc/buildhost.config
fi

if [ "$OBS_STORAGE_AUTOSETUP" != "yes" ]; then
   echo "OBS Storage Autosetup is not enabled in sysconfig, skipping!"
   exit 0
fi

# Determine the base and follow a runlevel link name.
base=${0##*/}
link=${base#*[SK][0-9][0-9]}

backenddir=/srv/obs

if [ -z "$OBS_WORKER_DIRECTORY" ]; then
	OBS_WORKER_DIRECTORY="/var/cache/obs/worker"
fi

rc_reset
case "$1" in
	start)
		# configure sshd if wanted
		if [ ! -e /root/.ssh/authorized_keys ]; then
			if [ -n "$OBS_ROOT_SSHD_KEY_URL" ]; then
				echo "Enabling sshd as requested"
				[ -e /root/.ssh ] || mkdir /root/.ssh
				curl $OBS_ROOT_SSHD_KEY_URL > /root/.ssh/authorized_keys
				insserv sshd
				rcsshd start
			fi
		fi

		# support usage of lvm on md devices
                if [ -x /sbin/mdadm ]; then
			/sbin/mdadm --assemble --scan
			/etc/init.d/boot.lvm start
		fi

		if [ "$OBS_SETUP_WORKER_PARTITIONS" = "take_all" ]; then
			if [ -e /dev/OBS/server ]; then
				echo "ERROR: A OBS server partition exists, aborting, do no take all space!"
			else
				echo "Collect all LVM partitions for the worker"
				# remove everything first
				vgreduce --removemissing --force OBS
				vgremove -f OBS
				pvremove `pvdisplay  | grep "PV Name" | awk '{ print $3 }'`

				# Find unpartitioned disks and create LVM partition on them
				for disk in `hwinfo --disk | grep "Device File:" |\
					cut -f2 -d: | cut -f2 -d" "`;do
				        count=0
				        used=
				        for i in `sfdisk -l $disk 2>/dev/null | tr -d '*' | grep ^/ |\
				                sed -e s"@\s\+@:@g" | cut -f1,5,6 -d:`;do
				                blocks=`echo $i | cut -f2 -d:`
				                if [ $blocks = "0" ];then
				                        count=`expr $count + 1`
				                        continue
				                fi
				                used=1
				        done
				        if [ $count -eq 4 ] || [ -z "$used" ];then
						echo ",,8e,-" | sfdisk $disk >/dev/null 2>&1
				        fi
				done

				# Collect all LVM partitions
				DEVICES=""
				for i in `sfdisk -l 2>/dev/null | tr -d '*' | grep ^/ |\
					sed -e s"@\s\+@:@g" | cut -f1,5,6 -d:`;do
					device=`echo $i | cut -f1 -d:`
					blocks=`echo $i | cut -f2 -d:`
					partid=`echo $i | cut -f3 -d:`
					if [ $blocks = "0" ];then
					        continue 
					fi
					if [ $partid = "8e" ];then
						pvcreate $device && DEVICES="$DEVICES $device"
					fi
				done
				vgcreate OBS $DEVICES
				vgscan
			fi
		fi

		if [ "$OBS_SETUP_WORKER_PARTITIONS" = "take_all" -o "$OBS_SETUP_WORKER_PARTITIONS" = "use_obs_vg" ]; then
			if [ "$OBS_WORKER_INSTANCES" -gt 0 ]; then
				# got config setting from sysconfig or PXE server
				NUM="$OBS_WORKER_INSTANCES"
			else
				# auto detect max possible instances
				# start one build backend per CPU by default
				NUM=`ls -d /sys/devices/system/cpu/cpu[0-9]* | wc -l`

				# but be sure that we have at least 512MB per instance
				MEMORY=`sed -n 's/^MemTotal:[ ]*\(.*\).kB$/\1/p' /proc/meminfo`
				if [ $MEMORY -lt $(( $NUM * 512 * 1024 )) ]; then
					NUM=$(( $MEMORY / ( 1024 * 512 ) ))
					NUM=$(( $NUM - 1 ))  # for Dom0
				fi
			fi
			if [ ! "0$NUM" -gt 0 ]; then
				echo "ERROR: OBS worker instances are 0, either misconfiguration or not enough resources"
				exit 1
			fi

			# Look for PV devices in OBS VG
			pvs=""
			for i in `vgdisplay -v OBS 2>/dev/null | grep "PV Name" | awk '{ print $3 }'`; do
				if [ -L $i ]; then
					pvs="$pvs `readlink -f $i`"
				else
					pvs="$pvs $i"
				fi
			done
			pvs=( $pvs )
			pv_count_real=${#pvs[@]}

			if [ -z "$OBS_WORKER_CACHE_SIZE" ]; then
				# 25 GB sounds like a good default for cache.
				OBS_WORKER_CACHE_SIZE=$(( 25 * 1024 ))
			fi

			if [ -z "$OBS_WORKER_SWAP_SIZE" ]; then
				OBS_WORKER_SWAP_SIZE=512
			fi

			if [ -z "$OBS_WORKER_ROOT_SIZE" ]; then
				VG_SIZE=`vgdisplay OBS --units M | grep "Free  PE" | sed 's,.* \([^ ]*\)\..*MB$,\1,'`
				VG_SIZE=$(( $VG_SIZE - $OBS_WORKER_CACHE_SIZE - ( $NUM * $OBS_WORKER_SWAP_SIZE ) ))
				OBS_WORKER_ROOT_SIZE=$(( $VG_SIZE / $NUM ))
				if test $OBS_WORKER_ROOT_SIZE -lt $(( 4 * 1024 )); then
					echo "ERROR: Not enough space for worker root LVs, just $OBS_WORKER_ROOT_SIZE MB, but at least 4 GB needed."
					exit 1
				fi
			fi

			if test "$NUM" -ge "$pv_count_real"; then
				# More or equal build instances than disks
				# create LV's and try to distribute them on PV's best as possible
				o1=0
				o2=1
				if [ $pv_count_real -gt 1 ]; then
					pv_count=$(( ($pv_count_real / 2) * 2 )) # be sure that we have an equal number
				elif [ $pv_count_real -eq 1 ]; then
					o2=0
					pv_count=1
				else
					# FIXME: error handling
					echo "BUG"
				fi
				offset=1
				if [ "$pv_count_real" == "$pv_count" ]; then
				  offset=0
				fi

				pv_idx=0
				I="0"
				while test "$NUM" -gt "$I"; do
					I=$(( $I + 1 ))
		
					lvcreate -n worker_root_${I} -L ${OBS_WORKER_ROOT_SIZE}M OBS ${pvs[$(( $pv_idx + $o1 + $offset ))]} || exit
		                        lvcreate -n worker_swap_${I} -L ${OBS_WORKER_SWAP_SIZE}M OBS ${pvs[$(( $pv_idx + $o2 + $offset ))]} || exit
		                        pv_idx=$(( $pv_idx + 2 ))
		                        if [ $pv_idx -ge $pv_count ]; then
						pv_idx=0
						# swap offset, so that swap and root partitions are not on same device
						a=$o1
						o1=$o2
						o2=$a
		                        fi
				done
			else
				# More disks than build instances
				# Use striping to boost IO performance
				I="0"
				# FIXME: this is a bit too simple, it can't handle float numbers
				disks_per_instance=$(( $pv_count_real / $NUM ))
				while test "$NUM" -gt "$I"; do
					I=$(( $I + 1 ))
	
					DEVS=""
					J="0"
					while test "$disks_per_instance" -lt "$J"; do
						J=$(( $J + 1 ))
						DEVS="$DEVS ${pvs[$(( $I * $disks_per_instance + $J ))]}"
					done
	
					lvcreate -n worker_root_${I} -L ${OBS_WORKER_ROOT_SIZE}M OBS $DEVS || exit
		                        lvcreate -n worker_swap_${I} -L ${OBS_WORKER_SWAP_SIZE}M OBS $DEVS || exit
				done
			fi

			# Create cache partition on remaining space
			lvcreate -n cache -l 100%FREE OBS || exit
			mkfs -text4 /dev/OBS/cache || exit
		fi

		echo "Looking for OBS Server LVM Volume"
                if [ -e /dev/OBS/server ]; then
			[ -L /obs ] && rm /obs
			[ -d /obs ] || { rm -f /obs; mkdir /obs ; }
			[ -e /obs/repos ] || { ln -sf /obs/repos /srv/obs/repos ; }
			mount /dev/OBS/server /obs
			[ -d /obs/log ] || mkdir /obs/log
			[ -d /obs/run ] || mkdir /obs/run
			chown obsrun.obsrun /obs/{,log,run}
		else
			echo "Setup local storage"
               		if [ -e /obs -a ! -L /obs ]; then
	       			echo "/obs already exists !"
               		        exit 1
               		fi
			ln -sf $backenddir /obs
                fi

		# Only used if there is a local BSConfig
		if [ -e /usr/lib/obs/server/BSConfig.pm ]; then
# We try out more entropy generators atm, so lets always enable signing for now
#                if [ -e /dev/hw_random -o -e /dev/hwrng ]; then
			# create default gpg key if not existing
			if [ ! -e /obs/obs-default-gpg.asc ] && grep -q "^our \$keyfile.*/obs/obs-default-gpg.asc.;$" /usr/lib/obs/server/BSConfig.pm; then
				echo -n Generating OBS default GPG key ....
				mkdir -p /obs/gnupg/phrases
				chmod -R 0700 /obs/gnupg
				cat >/tmp/obs-gpg.$$ <<EOF
				     %echo Generating a default OBS instance key
				     Key-Type: DSA
				     Key-Length: 1024
				     Subkey-Type: ELG-E
				     Subkey-Length: 1024
				     Name-Real: private OBS
				     Name-Comment: key without passphrase
				     Name-Email: defaultkey@localobs
				     Expire-Date: 0
				     %pubring /obs/gnupg/pubring.gpg
				     %secring /obs/gnupg/secring.gpg
				     %commit
				     %echo done
EOF
				gpg2 --homedir /obs/gnupg --batch --gen-key /tmp/obs-gpg.$$
				gpg2 --homedir /obs/gnupg --export -a > /obs/obs-default-gpg.asc
				# empty file just for accepting the key
				touch "/obs/gnupg/phrases/defaultkey@localobs"
			fi
			# to update sign.conf also after an appliance update
			if [ -e /obs/obs-default-gpg.asc ] && ! grep -q "^user" /etc/sign.conf; then
				# extend signd config
				echo "user: defaultkey@localobs"   >> /etc/sign.conf
				echo "server: 127.0.0.1"           >> /etc/sign.conf
				echo "allowuser: obsrun"           >> /etc/sign.conf
				echo "allow: 127.0.0.1"            >> /etc/sign.conf
				echo "phrases: /obs/gnupg/phrases" >> /etc/sign.conf
				echo done
				rm /tmp/obs-gpg.$$
				sed -i 's,^# \(our $sign =.*\),\1,' /usr/lib/obs/server/BSConfig.pm
				sed -i 's,^# \(our $forceprojectkeys =.*\),\1,' /usr/lib/obs/server/BSConfig.pm
			fi
#		fi
			if [ ! -e /obs/obs-default-gpg.asc ] ; then
			    sed -i 's,^\(our $sign =.*\),# \1,' /usr/lib/obs/server/BSConfig.pm
			    sed -i 's,^\(our $forceprojectkeys =.*\),# \1,' /usr/lib/obs/server/BSConfig.pm
			fi
		fi

		echo "Looking for OBS Worker Cache LVM Volume"
                if [ -e /dev/OBS/cache ]; then
			mkdir -p $OBS_WORKER_DIRECTORY
			mount /dev/OBS/cache $OBS_WORKER_DIRECTORY
			mkdir -p $OBS_WORKER_DIRECTORY/cache
                fi

		echo "Setting up OBS Workers according to LVM Volumes"
		if [ ! -e /etc/buildhost.config.presets ]; then
 	           mv /etc/buildhost.config /etc/buildhost.config.presets
                fi
		
		echo "### autoconfigured values by obsstoragesetup init script"  > /etc/buildhost.config
		echo "OBS_WORKER_DIRECTORY=\"$OBS_WORKER_DIRECTORY/\"" >> /etc/buildhost.config
		echo "OBS_CACHE_DIR=\"$OBS_WORKER_DIRECTORY/cache\""   >> /etc/buildhost.config
		CACHEMB=`df -m /$OBS_CACHE_DIR | tail -n 1 | sed -n 's,^/dev/[^ ]*[ ]*\([^ ]*\).*,\1,p'`
		[ -z "$CACHEMB" ] && CACHEMB=`df -m /$OBS_CACHE_DIR | tail -n 1 | sed -n 's,[^ ]*[ ]*\([^ ]*\).*,\1,p'`
		[ -n "$CACHEMB" ] && echo "OBS_CACHE_SIZE=\"$(( $CACHEMB / 2 ))\"" >> /etc/buildhost.config
		if [ -e /sys/hypervisor/type ] && grep -q xen /sys/hypervisor/type; then
			echo "Found XEN virtualization"
			RUN_VIRT=1
		else
			if grep ^flags /proc/cpuinfo | egrep -q " (svm|vmx) " && modprobe kvm; then
				echo "Found KVM virtualization"
				RUN_VIRT=1
		                # support virtio
		                # FIXME: find a better way to do this without getting remove by kiwi again.
		                if ! grep -q "^INITRD_MODULES=.*virtio_pci virtio_blk" /etc/sysconfig/kernel; then
		                   sed -i 's,^INITRD_MODULES="\(.*\)",INITRD_MODULES="\1 binfmt_misc virtio_pci virtio_blk",' /etc/sysconfig/kernel
		                   mkinitrd || unset RUN_VIRT
		                fi
			else
				echo "*** NO virtualization found, BUILDING IN UNSECURE ENVIROMENT ***"
				unset RUN_VIRT
			fi
		fi
		OBS_WORKER_INSTANCES="0"
		for i in /dev/OBS/worker_root* ; do
			name="${i##*/worker_}"
			swap="/dev/OBS/worker_swap${i#/dev/OBS/worker_root}"
			[ -e $swap ] || continue
			if [ -n "$RUN_VIRT" ]; then
				#prepare xen or kvm setup
				mkdir -p "$OBS_WORKER_DIRECTORY/$name"
				ln -sf "$i"    "$OBS_WORKER_DIRECTORY/$name/root"
				ln -sf "$swap" "$OBS_WORKER_DIRECTORY/$name/swap"
			else
				#plain chroot build
				mkdir -p "$OBS_WORKER_DIRECTORY/$name"
				mount $i "$OBS_WORKER_DIRECTORY/$name"
			fi
			OBS_WORKER_INSTANCES=$(( $OBS_WORKER_INSTANCES + 1 ))
		done
                if [ "$OBS_WORKER_INSTANCES" -gt 0 ]; then
			echo "OBS_WORKER_INSTANCES=\"$OBS_WORKER_INSTANCES\"" >> /etc/buildhost.config
			# How many parallel jobs make sense ?
			NUM=`ls -d /sys/devices/system/cpu/cpu[0-9]* | wc -l`
			echo "OBS_JOBS=\"$(( $NUM / ( $OBS_WORKER_INSTANCES - 1 ) ))\"" >> /etc/buildhost.config
			# Memory which can be used
			TOTALMEM=$(( `free | sed -n 's/^Mem:[ ]*\([^ ]*\).*/\1/p'` / 1024 ))
			INSTANCEMEM=$(( $TOTALMEM / ( 2 * $OBS_WORKER_INSTANCES ) ))
			echo "OBS_INSTANCE_MEMORY=\"$INSTANCEMEM\"" >> /etc/buildhost.config
			# append user presets
			echo "" >> /etc/buildhost.config
			echo "### preconfigured values from /etc/buildhost.config.presets" >> /etc/buildhost.config
			if [ -e /etc/buildhost.config.presets ]; then
				cat /etc/buildhost.config.presets >> /etc/buildhost.config
			fi
		else
			echo "WARNING: No OBS workers are configured on this system, no package will built."
			if [ -e /etc/buildhost.config.presets ]; then
				mv /etc/buildhost.config.presets /etc/buildhost.config
			fi
		fi

		# offer hook to make random special things in your setup
		if [ -n "$OBS_WORKER_SCRIPT_URL" ]; then
			echo "Running special script for this worker from $OBS_WORKER_SCRIPT_URL"
			curl $OBS_WORKER_SCRIPT_URL > /tmp/osbworkerscript.$$
			chmod 0755 /tmp/osbworkerscript.$$
			/tmp/osbworkerscript.$$
			rm /tmp/osbworkerscript.$$
		fi
		rc_status -v
	;;
	stop)
                # nothing to do
		rc_status -v
	;;
	restart)
                # nothing to do
		rc_status
	;;
	try-restart)
                # nothing to do
		rc_status
	;;
	reload)
                # nothing to do
		rc_status
	;;
	status)
		# nothing to do
		rc_status -v
	;;
	*)
		echo "Usage: $0 {start|stop|status|try-restart|restart|reload}"
		exit 1
	;;
esac
rc_exit
