#!/bin/bash
#
# cs_precheck_for_ceph
#
# (c) 2011-2017 SUSE Linux GmbH, Germany.
# (c) 2018-2019 SUSE LLC
# Author: L.Pinne.
# GNU General Public License v2. No warranty.
# http://www.gnu.org/licenses/gpl.html
#
# Version: 2019-11-28 15:35
#
# shellcheck disable=SC1090,SC2128
#

EXE="$0"

for CFG in "/etc/ClusterTools2/cs_precheck_for_ceph" "/etc/ClusterTools2/cs_show_supportconfig"; do
	test -s $CFG && source $CFG
done

test -z "${TEMP}" &&\
	TEMP="/dev/shm/cltl.$RANDOM"

# TODO use common library with cs_sum_base_config and cs_show_supportconfig

test -z "${CEPH_BOOTARG}" &&\
	CEPH_BOOTARG="
elevator=deadline
cgroup_disable=memory
transparent_hugepage=never
intel_idle.max_cstate=0
processor.max_cstate=0
"

# TODO LACP active/active bonding
# TODO net.ipv4.tcp_low_latency net.ipv4.tcp_timestamps net.ipv4.conf.all.rp_filter
# TODO use bash array to hold variables' identifiers and values
test -z "${CEPH_SYSCTL}" &&\
	CEPH_SYSCTL="
vm.dirty_bytes
vm.dirty_background_bytes
vm.pagecache_limit_mb
vm.pagecache_limit_ignore_dirty
vm.swappiness
vm.zone_reclaim_mode
fs.aio-max-nr
fs.file-max
kernel.sem
kernel.shmall
kernel.shmmax
net.core.rmem_max
net.core.wmem_max
net.core.netdev_max_backlog
net.ipv4.tcp_rmem
net.ipv4.tcp_wmem
net.ipv4.tcp_max_syn_backlog
net.ipv4.tcp_max_tw_buckets
net.ipv4.tcp_tw_reuse
net.ipv4.tcp_fin_timeout
net.ipv4.conf.all.rp_filter
net.ipv4.tcp_slow_start_after_idle
net.core.somaxconn
"

# TODO Try slightly larger IO queues ( /sys/.../queue/nr_requests )?
# TODO ulimits: 1 socket (open file), 2 threads (processes) for every client and every OSD
# TODO /etc/security/limits.conf?
# TODO XFS mkfs -i size=2048
# TODO   inode64,noatime,logbufs=8,logbsize=256
# TODO ceph.conf osd max backfill = 1, osd recovery max active = 1, osd max scrubs = 1
# TODO rbd cache = true, rbd cache size = 32 MiB, rbd cache max dirty = 0
# TODO rbd cache = true, rbd cache size = 32 MiB, rbd cache max dirty = 24 MiB
# TODO rbd cache writethrough until flush = true


test -z "${CEPH_SVC_YES}" &&\
	CEPH_SVC_YES="
boot.sysstat
boot.clock
ntp
mcelog
smartd
"

# TODO Try disabling irq_balancer, particularly for ixgbe NIC
# TODO firewall
test -z "${CEPH_SVC_OFF}" &&\
	CEPH_SVC_OFF="
boot.kdump
auditd
splash
splash_early
fbset
alsasound
boot.multipath
multipathd
"

test -z "${CEPH_VERS_MIN}" &&\
	CEPH_VERS_MIN="
kernel-default:3.0.101-0.7.15.1
kernel-xen:3.0.101-0.7.15.1
glibc:2.11.3-17.56.2
libgcc_s1:4.7.2_20130108-0.17.2
libstdc++6:4.7.2_20130108-0.17.2
xfsprogs:3.1.8-0.5.1
"
test -z "${CEPH_PTRN}" &&\
	CEPH_PTRN="
base
Basis-Devel
"
test -z "${CEPH_PKG_YES}" &&\
	CEPH_PKG_YES="
rear
sysstat
supportutils
xfsprogs
xfsdump
"


function help() {
	echo "usage:	$(basename "$EXE")"
	echo "	$(basename "$EXE") [OPTION]"
	echo
	echo " --help		show help."
	echo " --version	show version."
	echo " --base 	show base config."
	echo " --apps 	show CEPH apps config."
	echo
	test $UID -gt 0 && echo "please call as root."
}


function echo_msgsep(){
	echo
	echo "============================================================ ${1:0
:14} ==="
}


function prck_hardware(){

	echo "INFO: start $EXE $FUNCNAME"
	echo_msgsep "$FUNCNAME"

	echo -n "cpu "
	/usr/bin/grep "model.name" /proc/cpuinfo | sort -u
	echo -n "cpu MHz:	"
	/usr/bin/awk '$2=="MHz" {print $4}' /proc/cpuinfo | sort -u | tr "\n" " "
	# TODO number of sockets
	# TODO number of SSD, SATA, SCSI disks
	# TODO network adpators, 1Gb/s 10 Gb/s 40GB/s, Infiniband
	# TODO bondings active/active
	# TODO NIC module parameters?
	echo

	/usr/bin/grep -e "MemTotal:" -e "SwapTotal:" -e "VmallocTotal:" /proc/meminfo

	echo
	echo "INFO: normal_end $EXE $FUNCNAME"
}


function prck_kernel(){

	echo "INFO: start $EXE $FUNCNAME"
	echo_msgsep "$FUNCNAME"

	# TODO show recommeded version from HANA_VERS_MIN
	echo -n "Kernel: "; /bin/uname -s -r -v -m -p -i -o
	echo

	FIL="/proc/cmdline"
	for f in $FIL; do
		for a in ${CEPH_BOOTARG} ${CEPH_BOOTARG}; do
			FOUND=$(grep "$a" "$f")
			test -z "${FOUND}" && FOUND="NA"
			echo "${f}: ${a} :	${FOUND}"
		done
	done
	echo

	FOUND=$(/bin/dmesg | /usr/bin/grep -e governor -e cpupower -e cpuidle |\
			tr -s "\n" " ")
	test -z "${FOUND}" && FOUND="NA"
	echo "CPU frequency scaling:      ${FOUND}"
	echo
	OPT="/sys/devices/system/cpu/cpu*/cpufreq/scaling_governor /sys/kernel/mm/transparent_hugepage/enabled /sys/.*/scheduler"
	f="/etc/init.d/boot.local"
	for a in $OPT ; do
		FOUND=$(grep "$a" $f 2>/dev/null)
		test -z "${FOUND}" && FOUND="NA"
		echo "${f}: ${a} :      ${FOUND}"
	done
	echo
	f="/sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages"
	FOUND=$(cat $f 2>/dev/null)
	test -z "${FOUND}" && FOUND="NA"
	echo "$f (=0):	${FOUND}"
	f="/sys/devices/system/cpu/cpuidle/current_driver"
	FOUND=$(cat $f 2>/dev/null)
	test -z "${FOUND}" && FOUND="NA"
	echo "$f (acpi_idle):  ${FOUND}"
	echo

	# TODO loop
	echo -n "Elevator: deadline: "
	find /sys/devices/ -name "scheduler" -exec grep "\[deadline\]" {} \; | wc -l
	echo -n "Elevator: noop: "
	find /sys/devices/ -name "scheduler" -exec grep "\[noop\]" {} \; | wc -l
	echo -n "Elevator: cfq (=0): "
	find /sys/devices/ -name "scheduler" -exec grep "\[cfq\]" {} \; | wc -l
	echo
	echo -n "Elevator: mq-deadline: "
	find /sys/devices/ -name "scheduler" -exec grep "\[mq-deadline\]" {} \; | wc -l
	echo -n "Elevator: none: "
	find /sys/devices/ -name "scheduler" -exec grep "\[none\]" {} \; | wc -l
	echo -n "Elevator: kyber: "
	find /sys/devices/ -name "scheduler" -exec grep "\[kyber\]" {} \; | wc -l
	echo -n "Elevator: bfq: "
	find /sys/devices/ -name "scheduler" -exec grep "\[bfq\]" {} \; | wc -l
	echo

	FOUND=$(lsmod | grep -e dog -e wdt | cut -d" " -f1 | tr "\n" " ")
	test -z "${FOUND}" && FOUND="NA"
	echo "Watchdog loaded:  ${FOUND}"

	echo
	echo "INFO: normal_end $EXE $FUNCNAME"
}


function prck_network(){

	echo "INFO: start $EXE $FUNCNAME"
	echo_msgsep "$FUNCNAME"

	# TODO fix output
	a="bond"
	FOUND=$(ip -o a s | grep $a)
	test -z "${FOUND}" && FOUND="NA"
	### echo "network ${a} :      ${FOUND}"
	# TODO look for 10gbit/s
	# TODO look for MTU=9000
	# TODO look for irq balancing
	# TODO network hardware see prck_hardware
	# TODO iptables -L?

	echo
	echo "INFO: normal_end $EXE $FUNCNAME"
}


function prck_sysconf(){

	echo "INFO: start $EXE $FUNCNAME"
	echo_msgsep "$FUNCNAME"

	FIL="/boot/grub/menu.lst /etc/sysconfig/bootloader /boot/grub2/grub.cfg"
	for f in $FIL; do
		for a in ${CEPH_BOOTARG}; do
			FOUND=$(grep "$a" "$f" 2>/dev/null)
			test -z "${FOUND}" && FOUND="NA"
			echo "${f}: ${a} :	${FOUND}"
		done
	done
	echo

	OPT="/sys/devices/system/cpu/cpu*/cpufreq/scaling_governor /sys/kernel/mm/transparent_hugepage/enabled /sys/.*/scheduler"
	f="/etc/init.d/boot.local"
	for a in $OPT ; do
		FOUND=$(grep "$a" $f 2>/dev/null)
		test -z "${FOUND}" && FOUND="NA"
		echo "${f}: ${a} :      ${FOUND}"
	done
	echo

	f=/etc/sysctl.conf
	for a in ${CEPH_SYSCTL}; do
		FOUND=$(grep "$a" $f)
		test -z "${FOUND}" && FOUND="NA"
		echo "${f}: ${a} :	${FOUND}"
	done
	echo

	# TODO variable
	for f in bootloader kernel kdump; do
		g="/etc/sysconfig/$f"
		test -r $g || echo "${g}: NA"; continue
		echo "${g}:"
		grep -v "^#" $g | tr -s "\n" | grep -v "\"\"$"
		echo 
	done	
	echo
	
	CMD=/usr/bin/zcat
	INITRD=$(readlink /boot/initrd 2>/dev/null)
	if [ "$(file /boot/"$INITRD" | sed 's%.*:%%')" == " XZ compressed data" ]; then
		CMD=/usr/bin/xzcat
	fi
	FOUND=$($CMD /boot/"$INITRD" | cpio -itv 2>/dev/null |\
		colrm 43 54 | grep -e "dog.*.ko" -e "wdt.*.ko" |\
		awk '{print $6}' | tr "\n" " ")
	test -z "${FOUND}" && FOUND="NA"
	echo "Watchdog in initrd:      ${FOUND}"

	FOUND=$($CMD /boot/"$INITRD" | cpio -itv 2>/dev/null |\
		colrm 43 54 | grep -e "/etc/multipath.*"  -e lvm.conf |\
		awk '{print $6}' | tr "\n" " ")
	test -z "${FOUND}" && FOUND="NA"
	echo "MPIO, LVM in initrd:      ${FOUND}"

	echo
	echo "INFO: normal_end $EXE $FUNCNAME"
}


function prck_runlvl(){

	echo "INFO: start $EXE $FUNCNAME"
	echo_msgsep "$FUNCNAME"

	test -r /etc/inittab && FOUND=$(awk -F: '$1=="id" && $3=="initdefault" {print $2}' /etc/inittab) || FOUND=$(systemctl get-default)
	test -z "${FOUND}" && FOUND="NA"
	echo "default runlevel (3):	${FOUND}"
	echo

	for a in ${CEPH_SVC_YES}; do
		FOUND=$(chkconfig -A | grep "$a" | awk '{print $2}')
		test -z "${FOUND}" && FOUND="NA"
		echo "chkconfig ${a} ON:	${FOUND}"
	done
	for a in ${CEPH_SVC_OFF} ${CEPH_SVC_OFF}; do
		FOUND=$(chkconfig -A | grep "$a" | awk '{print $2}')
		test -z "${FOUND}" && FOUND="NA"
		echo "chkconfig ${a} OFF:	${FOUND}"
	done

	echo
	echo "INFO: normal_end $EXE $FUNCNAME"
}


function prck_basesoft(){

	echo "INFO: start $EXE $FUNCNAME"
	echo_msgsep "$FUNCNAME"

	echo
	zypper lr
	echo

	# TODO abort zypper, if server not reached
	INSTALLED_PTRN=$(zypper -n se -t pattern | awk '($1=="i" || $1=="i+") {print $3}')
	for a in ${CEPH_PTRN}; do
		FOUND=$(echo "$INSTALLED_PTRN" | tr " " "\n" | grep "$a")
		test -z "${FOUND}" && FOUND="NA"
		echo "pattern ${a}:       ${FOUND}"
	done
	echo

	RPMLIST=$(rpm -qa | sort)
	for a in ${CEPH_PKG_YES}; do
		FOUND=$(echo "$RPMLIST" | tr " " "\n" | grep "$a" | tr "\n" " ")
		test -z "${FOUND}" && FOUND="NA"
		echo "${a} :      ${FOUND}"
	done
	# TODO also glibc CEPH_VERS_MIN

	echo
	echo "INFO: normal_end $EXE $FUNCNAME"
}


function prck_apps() {

	echo "INFO: start $EXE $FUNCNAME"
	echo_msgsep "$FUNCNAME"

	# TODO check for CEPH
	echo
	echo "INFO: normal_end $EXE $FUNCNAME"
}


# main()
case $1 in
	-v|--version)
		echo -n "$(basename "$EXE") "
		head -11 "$EXE" | grep "^# Version: "
		exit
	;;
	-b|--base)
		echo "INFO: start $EXE"

		test $UID -gt 0 && echo "please call as root." && exit
		mkdir "$TEMP"
		cd "$TEMP" || exit
		prck_hardware
		prck_kernel
		prck_sysconf
		prck_runlvl
		prck_network
		prck_basesoft
		cd "$OLDPWD" || exit
		rm -rf "$TEMP"

		echo "INFO: normal_end $EXE"
		exit
	;;
	-a|--apps)
		echo "INFO: start $EXE"
		test $UID -gt 0 && echo "please call as root." && exit

		prck_apps

		echo "INFO: normal_end $EXE"
		exit
	;;
	*)
		help
		exit		
	;;
esac
#
