#!/bin/bash
#
# cs_prepare_esxvm
#
# (c) 2014-2017 SUSE Linux GmbH, Germany.
# (c) 2018-2019 SUSE LLC
# Author: L.Pinne.
# GNU General Public License v2. No warranty.
#
# Version: 2019-11-28 14:25
#
# shellcheck disable=SC1090,SC2128
#

EXE=${0}

CFG="/etc/ClusterTools2/cs_prep_esxvm"
test -s $CFG && source $CFG

TMP="/tmp/$RANDOM"
ERR="/dev/null"
SFX=$(basename "$EXE")

test -z "${BAK}" &&\
	BAK="/var/adm/backup/${SFX}-$(date +%Y%m%d-%H%M%S)"
test -z "${SRVC_OK}" &&\
	SRVC_OK="
ntp
sshd
boot.sysstat
vmware-tools
"
# TODO vmware-tools?
test -z "${SRVC_NO}" &&\
	SRVC_NO="
boot.multipath
multipathd
microcode.ctl
irqbalance
alsasound
smartd
mcelog
fbset
openais
splash
splash_early
ipmi
ipmievd
powerd
auditd
SuSEfirewall2_setup
SuSEfirewall2_init
"
test -z "${RLVL_OK}" &&\
	RLVL_OK="3"
# TODO SLES for SAP?
test -z "${PROD_OK}" &&\
	PROD_OK="
SUSE_SLES
"
# TODO SLES for VMware?
test -z "${PROD_NO}" &&\
	PROD_NO="
"
test -z "${PTRN_OK}" &&\
	PTRN_OK="
Minimal
base
"
test -z "${PTRN_NO}" &&\
	PTRN_NO="
apparmor
gnome
kde
Dom0
Dom0_KVM
kvm_server
xen_server
"
test -z "${PCKG_OK}" &&\
	PCKG_OK="
sysstat
kernel-default-base
kernel-default
"
test -z "${PCLT_OK}" &&\
	PCLT_OK="
corosync
cluster-glue
crmsh
libcorosync4
libglue2
libpacemaker3
ldirectord
openais
libopenais3
pacemaker
resource-agents
ocfs2-kmp-default
ocfs2-tools
ocfs2-tools-o2cb
drbd
drbd-pacemaker
drbd-kmp-default
drbd-utils
drbd-udev
drbd-bash-completion
sbd
ClusterTools2
"
# TODO with and w/o cluster
test -z "${PCKG_NO}" &&\
	PCKG_NO="
"
test -z "${ELVT_OK}" &&\
	ELVT_OK="noop"
test -z "${ELVT_NO}" &&\
	ELVT_NO="cfq"
test -z "${SYCT_OK}" &&\
	SYCT_OK="
vm.swappiness=40
vm.dirty_bytes=1073741824
vm.dirty_background_bytes=134217728
vm.zone_reclaim_mode=0
"


function filesystem() {
 case $1 in
 apply)
	# TODO /etc/fstab noatime,data=writeback
	# TODO tune2fs -c0 -i0
	# TODO xfs, ocfs2

	echo nop
 ;;
 list)
	# TODO xfs, ocfs2

	echo "=== $FUNCNAME ==="

	echo
	grep -v "^#" /etc/fstab

	# TODO autofs
	# echo
	# grep -v "^#" /etc/auto.*
	echo
	mount | awk '{print $1}' | while read -r; do
		echo -e "\n$REPLY"
		tune2fs -l "$REPLY" 2>$ERR |\
		awk '( $0~/Block size:/ || $0~/Maximum mount count:/ || $0~/Check interval:/ || $0~/Filesystem features:/ || $0~/Filesystem flags:/ || $0~/Block count:/ || $0~/Inode count:/ ) { print }'
	done 
	# TODO hide pseudo filesystems
 ;;
 esac
}


function ntpconf() {
 case $1 in
 apply)
	# TODO error handling, useful rerturn codes $RC
	FIL="/etc/ntp.conf"
	cp -a $FIL ${FIL}."$SFX" || exit 1
	# TODO
	cat >$FIL <<EOF
# /etc/ntp.conf
tinker panic 0
EOF
	sed -e s/^server.127.127.1.0//g -e s/^fudge.127.127.1.0//g <${FIL}."$SFX" >>$FIL

	/etc/init.d/ntp restart
	/sbin/chkconfig on
 ;;
 list)
	echo "=== $FUNCNAME ==="

	FIL="/etc/ntp.conf"
	echo -n "chkconfig: "
	/sbin/chkconfig ntp
	echo
	/etc/init.d/ntp status
	echo
	/usr/sbin/ntpq -p
	echo
	echo "$FIL:"
	grep -v "^#" $FIL | tr -s "\n"
	echo
 ;;
 esac
}


function elevator() {
 case $1 in
 apply)
	# TODO $RC
	# TODO also look into udev rules	

	FIL="/etc/sysconfig/bootloader"
	cp -a $FIL ${FIL}."$SFX" || exit 1
	sed s/showopts\"$/showopts\ elevator=noop\ cgroup_disable=memory\"/ <${FIL}."$SFX" >$FIL

	FIL="/boot/grub/menu.lst"
	cp -a $FIL ${FIL}."$SFX" || exit 1
	sed s/showopts\ vga=/showopts\ elevator=noop\ cgroup_disable=memory\ vga=/ <${FIL}."$SFX" >$FIL

	find /sys -name "scheduler" | while read -r; do
		if [ -d "${REPLY%/queue/scheduler}"/mq ]; then
			echo none >"$REPLY"
		else
			echo noop >"$REPLY"
		fi
	done
 ;;
 list)
	echo "=== $FUNCNAME ==="
	
	FIL="/etc/sysconfig/bootloader"
	echo "$FIL:"
	grep "DEFAULT_APPEND=" $FIL
	echo

	FIL="/boot/grub/menu.lst"
	echo "$FIL:"
	grep showopts $FIL | grep -v x11failsafe | grep -v "vmlinuz.*xen"
	echo
	find /sys -name "scheduler" | while read -r; do echo -n "$REPLY: "; cat "$REPLY"; done | grep -v "none$"
	echo
 ;;
 esac
}


function inittab() {
 case $1 in
 apply)
	# TODO $RC
	FIL="/etc/inittab"
	/sbin/runlevel >/root/runlevel."$SFX"
	cp -a $FIL ${FIL}."$SFX" || exit 1
	sed s/^id:.:initdefault:/id:${RLVL_OK}:initdefault:/ <${FIL}."$SFX" >$FIL

	/sbin/telinit ${RLVL_OK}
 ;;
 list)
	echo "=== $FUNCNAME ==="

	FIL="/etc/inittab"

	echo -n "runlevel: "
	/sbin/runlevel
	echo	
	echo "$FIL:"
	grep "^id:.:" $FIL
	echo
 ;;
 esac
}


function services() {
	# TODO $RC
 case $1 in
 apply)
	/sbin/chkconfig -A >/root/chkconfig-a."$SFX" || exit 1
	for f in $SRVC_NO; do
		/sbin/chkconfig "$f" off
		/etc/init.d/"$f" stop
	done
	for f in $SRVC_OK; do
		/sbin/chkconfig "$f" on
		/etc/init.d/"$f" start
	done
 ;;
 list)
	echo "=== $FUNCNAME ==="

	for f in $SRVC_NO; do
                /sbin/chkconfig "$f"
        done
	echo
	for f in $SRVC_OK; do
                /sbin/chkconfig "$f"
        done
	echo
 ;;
 esac
}


function syscontr() {
 case $1 in
 apply)
	# TODO $RC

	/sbin/sysctl -e -a >/root/sysctl-a."$SFX" 2>$ERR

	FIL="/etc/sysctl.conf"
	cp -a $FIL ${FIL}."$SFX" || exit 1

	# TODO TID if RAM > 4GB
	# TODO numa and cstate settings
	# TODO transparent hugepages, hugepages
	# TODO use SYCT_OK
	cat >$FIL <<EOF
# /etc/sysctl.conf
vm.swappiness = 40
#vm.dirty_bytes = 1073741824 
#vm.dirty_background_bytes = 134217728
vm.dirty_ratio = 20
vm.dirty_background_ratio = 5
#
EOF
	grep -v "vm.dirty.*ratio" ${FIL}."$SFX" |\
	grep -v "vm.dirty.*bytes" |\
	grep -v "vm.swappiness" >>$FIL

	/sbin/sysctl -e -p $FIL 2>$ERR 1>&2
 ;;
 list)
	echo "=== $FUNCNAME ==="

	FIL="/etc/sysctl.conf"
	for f in $SYCT_OK ; do
		S=$( echo "$f" | awk -F= '{print $1}' )
		/sbin/sysctl "$S"
	done
	echo
	echo "$FIL:"
	grep -v "^#" $FIL
	echo
 ;;
 esac
}


function products() {
 case $1 in
 apply)
	echo nop
 ;;
 list)
	echo "=== $FUNCNAME ==="
	mkdir $TMP && cd $TMP || exit 1
	# outputs looks like sam-20140505-12:43.report
	FIL="cs_sam.report"
	SOPT="--no-rpm-verify --no-rpm-verify-md5 --skip-unmatched-prod"	
	# TODO sam how to handle user input prompt?
	# TODO sam Unsatisfied dependencies:
	sam "$SOPT" >$FIL 2>$ERR
	awk '$1=="Product:"{print "prod: "$2,"vers: "$3,"sp: "$4,"arch: "$6}
		$1=="Baseproduct:"{print "base_prod: "$2}
		$1=="Packages"&&$2=="not" {print "3rd-party: "$6} 
		$1=="Unsupported"&&$2=="SUSE/Novell" {print "unsupported: "$4}' $FIL 
	# TODO add-on products
	cd "$OLDPWD" || exit 1
	rm -rf $TMP
 ;;
 esac
}


function patterns() {
 case $1 in
 apply)
	# TODO $RC
	# TODO check

	zypper -n se -t pattern | grep "^i" >/root/zypper-se-tpattern."$SFX" || exit 1
	for f in $PTRN_OK; do
			zypper -n in -t pattern "$f"
	done
 ;;
 list)
	echo "=== $FUNCNAME ==="
	zypper -n se -t pattern | grep "^i"
 ;;
 esac
}


function packages() {
 case $1 in
 apply)
	# TODO $RC
	# TODO check
	rpm -qa >/root/rpm-qa."$SFX" || exit 1
	zypper -n refs -r | exit 1
	# TODO -y ?
	# shellcheck disable=SC2086
	zypper -n -q --no-gpg-checks in -l --no-recommends $PCKG_OK
	# shellcheck disable=SC2086
	zypper -n -q --no-gpg-checks in -l --no-recommends $PCLT_OK
 ;;
 list)
	echo "=== $FUNCNAME ==="
	# shellcheck disable=SC2086
	zypper -n se $PCKG_OK 2>$ERR #| grep "|.package$" 
	# shellcheck disable=SC2086
	zypper -n se $PCLT_OK 2>$ERR #| grep "|.package$"
 ;;
 esac
}


function backup() {
	# TODO use functions check option
	# TODO error handling, useful return code
	mkdir $TMP
	cd $TMP || exit 1

	/sbin/sysctl -e -a >${TMP}/sysctl-a."$SFX" 2>$ERR
	/sbin/runlevel >${TMP}/runlevel."$SFX"
	rpm -qa >${TMP}/rpm-qa."$SFX"
	zypper se -t pattern | grep "^i" >${TMP}/zypper-se-tpattern."$SFX"
	cp -a /boot/grub/menu.lst ${TMP}/
	tar czf ${TMP}/etc.tgz /etc/ >$ERR 2>&1

	cd "$OLDPWD" || exit 1
	tar czf "${BAK}".tgz $TMP >$ERR 2>&1; RC=$? && rm -rf $TMP
	echo "RC: $RC"
	ls -l "${BAK}".tgz
}


function hardware() {
 case $1 in
 apply)
	echo nop
 ;;
 list)
	echo "=== $FUNCNAME ==="
	# TODO check vmxnet3, pvscsi, memcrtl
	# TODO check hwinfo for PV hardware and VMware BIOS
	# TODO check modules.d/*
	# TODO check NUMA topology, CPU count, RAM size
	# TODO LUNs visible, to decide if pvscsi useful or not? 
	echo "not implemented yet"
 ;;
 esac
}


function help() {
	echo "usage: $(basename "$EXE") [OPTION]"
	echo
	echo " --save		save current settings, call first of all"
	echo " --list		list current settings, not fully implemented"
	echo " --preview	preview recommended settings"
	echo " --apply	apply recommended settings, call only once"
	echo " --help		show help"
	echo " --version	show version"
	echo
	echo "This script could apply changes only once on each VM."
	exit
}


# main()

case $1 in
	-v|--version)
		echo -n "$(basename "$EXE") "
		head -11 "$EXE" | grep "^# Version: "
		exit
	;;
	-a|--apply)
		# TODO select functions
		# Makes no sense: products apply
		# patterns apply?
		packages apply
		ntpconf apply
		inittab apply	
		services apply
		elevator apply
		# TODO filesystem apply
		syscontr apply
	;;
	-l|--list)
		products list
		# patterns list
		packages list
		ntpconf list 
		inittab list
		services list
		elevator list
		# TODO filesystem list
		syscontr list
	;;
	-s|save)
		backup
	;;
	-p|preview)
		# TODO hardcoded settings?
		echo
		#echo "patterns:        $PROD_OK"
		#echo "patterns:	$PTRN_OK"
		echo "packages:	$PCKG_OK"
		echo "inittab:	$RLVL_OK"
		echo
		echo "services:	$SRVC_OK"
		echo "services_NOT:	$SRVC_NO"
		echo "elevator:	$ELVT_OK"
		# TODO filesystem
		echo
		echo "syscontr: $SYCT_OK"
	;;
	-t|--test)
		#products list
		#patterns list
		#packages list	
		#ntpconf list
		#inittab list
		#services list
		#elevator list
		filesystem list
		#syscontr list
	;;
	*)
		help
	;;
esac
#
