#!/bin/bash
#
# Script to collect information for report.
#
# Copyright (c) 1999-2017, Parallels International GmbH
# Copyright (c) 2017-2019 Virtuozzo International GmbH. All rights reserved.
#
# This file is part of vzreport. It's free software; you can redistribute
# it and/or modify it under the terms of 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.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301, USA.
#
# Our contact details: Virtuozzo International GmbH, Vordergasse 59, 8200
# Schaffhausen, Switzerland.
#

export PATH=/sbin:/usr/sbin:/bin:/usr/bin:/usr/local/bin
unset LD_LIBRARY_PATH

function report_progress()
{
	local pval="$1"
	if [ $SHOW_DIAGNOSTICS -ne 0 ]; then
		echo "Progress: $pval"
	fi
}


SHOW_DIAGNOSTICS=0
VERIFY_RPM=1
while getopts "dp" optchar ; do
	case "$optchar" in

	d)
		SHOW_DIAGNOSTICS=1
	;;
	p)
		VERIFY_RPM=0
	;;
	*)
		exit 1
	;;
	esac
done
shift_val=$(($OPTIND-1))
shift $shift_val


REPORT_DIR=$1
if [ -z "$REPORT_DIR" ]; then
	TMPREP_DIR=`mktemp -d /tmp/vzreport.manual.XXXXXX`
	if [ $? -ne 0 ]; then
		exit 1
	fi

	REPORT_DIR="$TMPREP_DIR/files"
	mkdir $REPORT_DIR
	if [ $? -ne 0 ]; then
		exit 1;
	fi
fi

if [ ! -d $REPORT_DIR ]; then
	echo "$0: No output dir" >&2
	exit 2
elif [ -n "$TMPREP_DIR" ]; then
	# XXX: This line should be first in the output.
	# It is used in the Dispatcher to get report file and cleanup temporary dir
	echo "Collecting report in $TMPREP_DIR/vzreport.tgz"
fi

function write_report()
{
	cd $REPORT_DIR
	tar -zchf ../vzreport.tgz * $ETCFILES $VZETCDIRS 2>&1 |grep -vE 'Removing leading|No such file or directory'
	cd - > /dev/null 2>&1
	rm -rf $REPORT_DIR

	if [ -n "$TMPREP_DIR" ]; then
		echo "Report was collected in $TMPREP_DIR/vzreport.tgz"
	fi
}

function alert()
{
	write_report
	# exit with zero because otherwise report will not be attached
	exit 0
}

# set alarm for 2 minutes
#
# Dispatcher has the hardcoded 3 minutes timeout for this script. Better to do
# not gather part of the information than do not have it at all - thus we fire
# own alarm in 2 minutes in order to have 1 minute for packing result.
PID_ALARM=0
if [ -n "$VZREPORT_COLLECTOR_TIMEOUT" ] && [ $VZREPORT_COLLECTOR_TIMEOUT -gt 60 ]; then
	timeout=$((VZREPORT_COLLECTOR_TIMEOUT - 60))
	trap alert USR1

	( sleep $timeout && kill -USR1 $$ && pkill -TERM -P $$ ) &
	PID_ALARM=$!
fi

##############################################
#########   STAGE 1
function read_files() {
	local files dir
	local src="${1}"
	local dst="${2}"
	[ ! -e "${src}" ] && return
	if [ -d "${src}" ]; then
	       # exclude /proc/slm/groups/*/event which hangs on read
               files=`find "${src}" -type f | grep -v "event"` 
	else
               files="${src}"
	fi
	for file in ${files}; do
               dir=`dirname "${dst}/${file}"`
               [ ! -d "${dir}" ] && mkdir -p "${dir}"
               cat "${file}" > "${dst}/${file}"
	done
}

PROCFILES="/proc/vz /proc/cpuinfo /proc/meminfo /proc/user_beancounters
/proc/partitions /proc/mounts /proc/slabinfo /proc/cmdline /proc/loadavg
/proc/interrupts /proc/mdstat
/proc/sys/net/ipv4/conf/default/rp_filter
/proc/sys/net/ipv4/conf/all/rp_filter /proc/scsi
/proc/parallels"

SYSFILES="/sys/block/sd*/device/model /sys/block/sd*/size /sys/kernel/debug/cleancache/succ_gets"

for i in $PROCFILES $SYSFILES; do
	read_files "${i}" "${REPORT_DIR}"
done

##############################################
#########   STAGE 2

# vzstat launch 1
echo "==== first launch ====" >"$REPORT_DIR/vzstat"
vzstat -tvm	>>"$REPORT_DIR/vzstat" 2>&1

date		>"$REPORT_DIR/date" 2>&1
uname -a	>"$REPORT_DIR/uname" 2>&1
uptime		>"$REPORT_DIR/uptime" 2>&1
lspci		>"$REPORT_DIR/lspci" 2>&1
lspci -n -v	>"$REPORT_DIR/lspci_ext" 2>&1
free		>"$REPORT_DIR/free" 2>&1
vzlist -a	>"$REPORT_DIR/vzlist" 2>&1
mount		>"$REPORT_DIR/mount" 2>&1
dmesg		>"$REPORT_DIR/dmesg" 2>&1
sysctl -a	>"$REPORT_DIR/sysctl_a" 2>&1
chkconfig --list	>"$REPORT_DIR/chkconfig_list" 2>&1
lvdisplay 2>/dev/null | grep "Logical volume" > /dev/null
[ $? -eq 0 ] && pvdisplay	>"$REPORT_DIR/pvdisplay" 2>/dev/null

echo "==== df -h ====" >"$REPORT_DIR/df"
df -h		>>"$REPORT_DIR/df" 2>&1
echo -e "\n==== df -i ====" >>"$REPORT_DIR/df"
df -i		>>"$REPORT_DIR/df" 2>&1

lsmod		>"$REPORT_DIR/lsmod" 2>&1

# Templates information

# Old std templates
vzpkgls		>"$REPORT_DIR/vzpkgls" 2>&1
vzpkgls -c	>"$REPORT_DIR/vzpkgls_c" 2>&1

# EZ templates
vzpkg list	>"$REPORT_DIR/ez_templates" 2>&1

vzpkg list -O| awk '{print $1}'| \
while read os; \
        do echo -e "\n$os:"; \
        vzpkg info -q "$os" repositories mirrorlist; \
done            >>"$REPORT_DIR/template_repos" 2>&1

find /vz/template/*/*/x86{,_64}/ -mindepth 1 -maxdepth 1 -ls \
	> "$REPORT_DIR/template_packages" 2>&1

if [ -x /usr/sbin/dmidecode ]; then
    /usr/sbin/dmidecode > "$REPORT_DIR/dmidecode" 2>&1
fi

report_progress 10

progress=10
it=0
echo -n >"$REPORT_DIR/vmstat" 2>&1
vmstat 2 5 2>&1 | while read x; do
	echo "$x" >>"$REPORT_DIR/vmstat" 2>&1
	
	if [ $it -lt 2 ]; then
		it=$((it+1))
	else
		progress=$((progress+7))
		if [ $progress -gt 45 ]; then
			progress=45
		fi
		report_progress "$progress"
	fi
done
report_progress 45

vzlicview		 >"$REPORT_DIR/vzlicview" 2>&1

# List of processes
ps alx			 >"$REPORT_DIR/ps" 2>&1

if [ -x /usr/bin/numactl ]; then
	numactl --hardware >"$REPORT_DIR/numa_hw" 2>&1
fi

# Networking information
ip addr list	>"$REPORT_DIR/ifconfig" 2>&1
ip route list table all	>"$REPORT_DIR/routes" 2>&1
netstat -a	>"$REPORT_DIR/netstat" 2>&1
ip rule ls	>"$REPORT_DIR/ip_rule_ls" 2>&1
arp -an		>"$REPORT_DIR/arp" 2>&1
vztactl class_list >"$REPORT_DIR/ta_class_list" 2>&1
for i in `cat "$REPORT_DIR/ifconfig" | awk -F : '{print $2}' | \
	egrep "^[[:space:]]*eth[[:digit:]]+$"`
do
tc class list dev $i >"$REPORT_DIR/ta_list_dev_$i" 2>&1
tc qdisc list dev $i >>"$REPORT_DIR/ta_list_dev_$i" 2>&1
done

echo "==== iptables -L -v ====" >"$REPORT_DIR/iptables"
iptables -L -v		>>"$REPORT_DIR/iptables"
echo -e "\n==== iptables -L -v -t nat ====" >>"$REPORT_DIR/iptables"
iptables -L -v -t nat	>>"$REPORT_DIR/iptables"

vznetcfg net list >"$REPORT_DIR/vznetcfg_list" 2>&1
vznetcfg if list >>"$REPORT_DIR/vznetcfg_list" 2>&1

# vzstat launch 2
echo -e "\n==== second launch ====" >>"$REPORT_DIR/vzstat"
vzstat -tvm	>>"$REPORT_DIR/vzstat" 2>&1

readykernel info >"$REPORT_DIR/readykernel_info" 2>&1
kpatch list >"$REPORT_DIR/kpatch_list" 2>&1

report_progress 60

##############################################
#########   STAGE 3

function list_logs()
{
	local logfiles="$1"

	for i in $logfiles
	do
		if [ -f "$i" ]; then
			local dstdir="$REPORT_DIR/`dirname $i`"
			if [ ! -d $dstdir ]; then
				mkdir -p $dstdir
			fi
			tail -n 5000 "$i" >"$REPORT_DIR/$i" 2>&1
		fi
	done
}

VE0LOGFILES=`ls /var/log/{dmesg,boot.log,ploop.log*,vzctl.log*,vztt.log*,vzkeytools/vzkeytools.log,messages*,kpatch.log*,anaconda/*.log,anaconda/syslog,phaul*.log,do-upgrade-vz7.log,sa/*}`

list_logs "$VE0LOGFILES"
find /var/log -name "wtmp*" | xargs -n1 last -a -f >"$REPORT_DIR/last" 2>&1

ETCFILES_IN="/etc/vzstat.conf /etc/vzbackup.conf /etc/vzlmond.conf /etc/vzlmond.logrotate
/etc/vztt/vztt.conf /etc/fstab /etc/grub.conf /etc/modules.conf /etc/sysctl.conf
/etc/modprobe.conf /etc/*release* /etc/*version* /etc/sysconfig/iptables* /etc/rc.local
/etc/vzvpn/vzvpn.conf /etc/sysconfig/network-scripts/ifcfg-eth*
/etc/hosts /etc/resolv.conf /etc/shaman/shaman.conf"

for i in $ETCFILES_IN
do
	if [ -f "$i" ]; then
		ETCFILES="$ETCFILES $i"
	fi
done


VZETCDIRS_IN="/etc/vzkeytools /etc/vzredirect.d /etc/vz /etc/pstorage"

for i in $VZETCDIRS_IN
do
	if [ -d "$i" ]; then
		VZETCDIRS="$VZETCDIRS $i"
	fi
done

# Running Containers information
function report_ct_info() {
	local veid="${1}"
	local dst_dir="${2}"
	local dump_dir=`vzlist -H $veid -o private`/dump
	local d_dump_dir=""

	mkdir -p "$dst_dir"
	mkdir -p "$dst_dir/etc"
	for i in "/etc/hosts" "/etc/resolv.conf"
	do
		vzctl exec $veid cat $i >"$dst_dir/$i" 2>&1
	done
	vzctl exec $veid chkconfig --list >"$dst_dir/chkconfig_list" 2>&1

	[ ! -d $dump_dir ] && return
	for f in `find $dump_dir -name \*.log`; do
		d_dump_dir=$dst_dir/dump/`dirname $f 2>/dev/null | sed "s,$dump_dir,,g"`
		mkdir -p $d_dump_dir >/dev/null 2>&1
		tail -n 5000 $f > $d_dump_dir/`basename $f` 2>/dev/null
	done
}

CT_REPORT_DIR="$REPORT_DIR/Containers/"
mkdir -p "$CT_REPORT_DIR"
for i in `vzlist -a -H -o veid`
do
	report_ct_info $i "$CT_REPORT_DIR/$i"
done

# Collect libvirt data
LIBVIRT_REPORT_DIR="$REPORT_DIR/virsh/"
mkdir -p "$LIBVIRT_REPORT_DIR"

virsh list --all > "$LIBVIRT_REPORT_DIR/list"
virsh list --all | awk ' NF && NR>2 { system (" virsh dumpxml " $2 ) }' > "$LIBVIRT_REPORT_DIR/dumpxml"

virsh -c vz:///system list --all > "$LIBVIRT_REPORT_DIR/list_vz"
virsh -c vz:///system list --all | awk ' NF && NR>2 { system (" virsh dumpxml " $2 ) }' > "$LIBVIRT_REPORT_DIR/dumpxml_vz"

virsh net-list --all > "$LIBVIRT_REPORT_DIR/net-list"
virsh net-list --all | awk ' NF && NR>2 { system (" virsh net-dumpxml " $1 ) }' > "$LIBVIRT_REPORT_DIR/net-dumpxml"

virsh nodeinfo > "$LIBVIRT_REPORT_DIR/nodeinfo"
virsh nodecpumap > "$LIBVIRT_REPORT_DIR/nodecpumap"
virsh nodememstats > "$LIBVIRT_REPORT_DIR/nodememstats"
virsh node-memory-tune > "$LIBVIRT_REPORT_DIR/node-memory-tune"
virsh sysinfo > "$LIBVIRT_REPORT_DIR/sysinfo"
virsh capabilities > "$LIBVIRT_REPORT_DIR/capabilities"

virsh nwfilter-list > "$LIBVIRT_REPORT_DIR/nwfilter-list"
virsh nwfilter-list | awk ' NF && NR>2 { system (" virsh nwfilter-dumpxml " $1 ) }' > "$LIBVIRT_REPORT_DIR/nwfilter-dumpxml"

virsh nodedev-list | awk ' NF && NR>2 { system (" virsh nodedev-dumpxml " $1 ) }' > "$LIBVIRT_REPORT_DIR/nodedev-list"

virsh iface-list | awk ' NF && NR>2 { system (" virsh iface-dumpxml " $1 ) }' > "$LIBVIRT_REPORT_DIR/iface-list"

virsh pool-list | awk ' NF && NR>2 { system (" virsh pool-dumpxml " $1 ) }' > "$LIBVIRT_REPORT_DIR/pool-list"

# Collect vzagent data
VZAGENT_INFO_DIR="$REPORT_DIR/vzagent_info"
VZAGENT_INSTALL_DIR="/opt/vzagent"
mkdir -p $VZAGENT_INFO_DIR
if [ -x "$VZAGENT_INSTALL_DIR/bin/vzagentCollectReport.sh" ]; then
	$VZAGENT_INSTALL_DIR/bin/vzagentCollectReport.sh $VZAGENT_INFO_DIR 2>&1
fi

# cut vzagent data as we have PSBM limitation at 16 MB overall
for i in `find $VZAGENT_INFO_DIR -type f`
do
	tail -n 1000000 $i > $i.tmp
	mv $i.tmp $i
done

if [ -f /etc/parallels-release ]; then
	rpm -V parallels-server-bm-release >"$REPORT_DIR/vz_deps" 2>&1
else
	rpm -V virtuozzo-release >"$REPORT_DIR/vz_deps" 2>&1
fi

# RPM repositories configured

yum repolist -v >"$REPORT_DIR/repositories" 2>&1

# RPM database information
query_format="%{name}-%{version}-%{release}\t%{arch}\t\"%{vendor}\"\t%{installtime}\n"
rpm -qa --qf "$query_format" | sort >"$REPORT_DIR/rpm_list" 2>&1

# Consistency of Parallels packages
if [ $VERIFY_RPM -ne 0 ]; then
	cat "$REPORT_DIR/rpm_list" | egrep -i "parallels|swsoft" | awk '{print $1}' | \
		xargs rpm -V | egrep '^..5 | missing' >"$REPORT_DIR/rpm_missing" 2>&1
fi

report_progress 70

# pstorage information
[ -x /usr/bin/pstorage-hotplugd ] && \
	/usr/bin/pstorage-hotplugd -s > $REPORT_DIR/pstorage_hotplugd_s 2>&1
if [ -x /usr/bin/pstorage ]; then
	for cluster in `ls -d /etc/pstorage/clusters/* 2>/dev/null`; do
		mkdir -p $REPORT_DIR/etc/pstorage/clusters/`basename $cluster` > /dev/null 2>&1
		/usr/bin/pstorage -c `basename $cluster` list-services \
			> $REPORT_DIR/etc/pstorage/clusters/`basename $cluster`/services 2>&1
	done
fi

report_progress 75

##############################################
#########   STAGE 4 - archiving
[ $PID_ALARM -gt 0 ] && kill -9 $PID_ALARM
write_report
