dotCMS-Inspector version 3.x

Description

This is a simple tool to gather information about a running dotcms instance. This only works with dotcms 3.0 plus.

Usage: Must be run as root. 

We gather a lot of system information in order to be sure that the environment is set up properly for dotCMS and to catch any unexpected behavior that may be occurring that our log files wouldn’t capture.  This includes things like what OS version is running, system memory information, number and types of CPUS, disk information including free space, all running procs and their owners, network socket statuses, open files handles, and whether this OS is running in a VM or not.

Information gathered about dotCMS installation and running process is intended to be as broad as is reasonable so that in most instances we will only need to gather information at one point in the process (note that for troubleshooting hung processes or addressing historic incidents this will not be the case).   Gathering information on the java process, including smap, status, stat, and JVM info allows us to have a solid understanding of the parent process condition.  We also gather information on the memory being used specifically by dotCMS to check possible performance issues related to memory.  Index information, config files, and information about assets all are used to assess the condition of the core functions of dotCMS.  Finally we gather java dump and garbage collection information in order to have any debugging and error information that may be useful in conjunction with the dotcms log files.

Attachment: dotcms-inspector.sh (9 KB)

Code

${esc.h}!/bin/bash

${esc.h}${esc.h}${esc.h}${esc.h}${esc.h}${esc.h}${esc.h}${esc.h}${esc.h}${esc.h}${esc.h}${esc.h}${esc.h}${esc.h}${esc.h}${esc.h}${esc.h}
${esc.h}tool for dotcms-inspectoring sys, db, and dotcms specific info for analysis
${esc.h}v 0.1
${esc.h}Matt Yarbrough
${esc.h}dotCMS
${esc.h}${esc.h}${esc.h}${esc.h}${esc.h}${esc.h}${esc.h}${esc.h}${esc.h}${esc.h}${esc.h}${esc.h}${esc.h}${esc.h}${esc.h}${esc.h}${esc.h}${esc.h}${esc.h}
${esc.h}TODO: 	ability to turn off certain subscripts
${esc.h}	push publish config
${esc.h}	cachestats using setup from Chris McCracken or REST API
${esc.h}	db threads
${esc.h} 	db locks
${esc.h}	use mktemp for setup of log storage space
${esc.h}	/proc/${esc.d}{PID}/smaps, /proc/${esc.d}{PID}/status, and /proc/${esc.d}{PID}/stat

${esc.h}TEST COMMANDS
command -v awk >/dev/null 2>&1 || { echo >&2 "awk is not installed.  Ending."; exit 1; }	
command -v grep >/dev/null 2>&1 || { echo >&2 "grep is not installed.  Ending."; exit 1; }	
command -v lsof >/dev/null 2>&1 || skipFileHandles=true	
command -v uname >/dev/null 2>&1 || skipOS=true	
command -v cat >/dev/null 2>&1 || skipOS=true
command -v lscpu >/dev/null 2>&1 || skipCPU=true
command -v netstat >/dev/null 2>&1 || skipNetStat=true 
command -v lspci >/dev/null 2>&1 || skipVM=true
command -v tee >/dev/null 2>&1 || skipTee=true

for install in ${esc.d}(ps aux | grep java | grep dotserver | grep org.apache.catalina.startup.Bootstrap | awk '{ print ${esc.d}2 }'); do

DOTPROCPID=${esc.d}install
DOTOWNER=${esc.d}(ps aux | grep -v grep | grep ${esc.d}install | awk '{ print ${esc.d}1}')
echo "DOTOWNER: " ${esc.d}DOTOWNER

if [ ! -d logs-${esc.d}DOTPROCPID ]; then
    mkdir -p logs-${esc.d}DOTPROCPID;
fi;
getCatHome1=${esc.d}(ps ax | grep ${esc.d}install)
getCatHome2=${esc.d}{getCatHome1${esc.h}*\Dcatalina.home=}
getCatHome3=${esc.d}{getCatHome2%%-Djava.io.tmpdir*}
DOTHOME=${esc.d}( echo "${esc.d}{getCatHome3}" | sed -e "s/^\ *//g" -e "s/\ *${esc.d}//g")
${esc.h}ADMINUSER=${esc.d}2
${esc.h}ADMINPASS=${esc.d}3
HOSTNAME=${esc.d}(hostname)
TOMCATVERSION=${esc.d}{DOTHOME${esc.h}*\dotserver\/}
RUNDATE=${esc.d}(date +"%Y%m%d-%H%M%S")
LOGFOLDER=logs-${esc.d}DOTPROCPID
SYSLOGFILE=${esc.d}LOGFOLDER/di-system-${esc.d}HOSTNAME-${esc.d}RUNDATE.txt
DOTLOGFILE=${esc.d}LOGFOLDER/di-dotcms-${esc.d}HOSTNAME-${esc.d}RUNDATE.txt
${esc.h}DB info commented, we aren't using this information right now and I'd rather not be grabbing credentials unnecessarily
${esc.h}DBX=${esc.d}(grep username="*" ${esc.d}DOTHOME/webapps/ROOT/META-INF/context.xml | grep -v 'username="{your db user}"' | grep -v 'username="{your user}@{your server}"')
${esc.h}DBU1=${esc.d}{DBX${esc.h}*\"}
${esc.h}DBUSER=${esc.d}{DBU1%\"\ pa*}
${esc.h}DBP1=${esc.d}{DBX${esc.h}*word=\"}
${esc.h}DBPASS=${esc.d}{DBP1%\"\ maxAc*}

if [ skipTee != true ]
	then 
		exec &> >(tee -a "${esc.d}SYSLOGFILE")
	else
		exec > ${esc.d}SYSLOGFILE 2>&1
fi

echo "dotCMS Inspector Run: " ${esc.d}RUNDATE 
echo "JAVA_HOME: " ${esc.d}JAVA_HOME

${esc.h}SYSTEM FUNCTIONS
function getDB {
	${esc.h}what database are we?
	echo -e  "\n\n${esc.h}${esc.h}${esc.h}${esc.h}${esc.h} DATABASE INFORMATION ${esc.h}${esc.h}${esc.h}${esc.h}${esc.h}"
	PG="${esc.d}(grep '<\!--\ POSTGRESQL -->' ${esc.d}DOTHOME/webapps/ROOT/META-INF/context.xml)"
	MY="${esc.d}(grep '<\!--\ MYSQL UTF8 -->' ${esc.d}DOTHOME/webapps/ROOT/META-INF/context.xml)"

	cd utils
	if [ "${esc.d}PG" ]
	  then
	    DB="POSTGRESQL"
		echo "Postgres"
	elif [ "${esc.d}MY" ]
	  then
	    DB="MYSQL"
		echo "Mysql"
	else
	    DB="OTHER"
		echo "Database not supported in this script"
	fi

}

function getOS {
	${esc.h} Courtesy of user slm on stackexchange
	echo  -e "\n${esc.h} OS and Version Information:"  
	OS=`uname -s`
	REV=`uname -r`
	MACH=`uname -m`

	GetVersionFromFile()
	{
	    VERSION=`cat ${esc.d}1 | tr "\n" ' ' | sed s/.*VERSION.*=\ // `
	}

	if [ "${esc.d}{OS}" = "SunOS" ] ; then
	    OS=Solaris
	    ARCH=`uname -p` 
	    OSSTR="${esc.d}{OS} ${esc.d}{REV}(${esc.d}{ARCH} `uname -v`)"
	elif [ "${esc.d}{OS}" = "AIX" ] ; then
	    OSSTR="${esc.d}{OS} `oslevel` (`oslevel -r`)"
	elif [ "${esc.d}{OS}" = "Linux" ] ; then
	    KERNEL=`uname -r`
	    if [ -f /etc/redhat-release ] ; then
	        DIST='RedHat'
	        PSUEDONAME=`cat /etc/redhat-release | sed s/.*\(// | sed s/\)//`
	        REV=`cat /etc/redhat-release | sed s/.*release\ // | sed s/\ .*//`
	    elif [ -f /etc/SuSE-release ] ; then
	        DIST=`cat /etc/SuSE-release | tr "\n" ' '| sed s/VERSION.*//`
	        REV=`cat /etc/SuSE-release | tr "\n" ' ' | sed s/.*=\ //`
	    elif [ -f /etc/mandrake-release ] ; then
	        DIST='Mandrake'
	        PSUEDONAME=`cat /etc/mandrake-release | sed s/.*\(// | sed s/\)//`
	        REV=`cat /etc/mandrake-release | sed s/.*release\ // | sed s/\ .*//`
	    elif [ -f /etc/debian_version ] ; then
	        DIST="Debian `cat /etc/debian_version`"
	        REV=""

	    fi
	    if [ -f /etc/UnitedLinux-release ] ; then
	        DIST="${esc.d}{DIST}[`cat /etc/UnitedLinux-release | tr "\n" ' ' | sed s/VERSION.*//`]"
	    fi

	    OSSTR="${esc.d}{OS} ${esc.d}{DIST} ${esc.d}{REV}(${esc.d}{PSUEDONAME} ${esc.d}{KERNEL} ${esc.d}{MACH})"

	fi

	echo ${esc.d}{OSSTR}
	

}

function getMemory {
	${esc.h}grep 'Mem|Cache|Swap' /proc/meminfo
	${esc.h}/proc/${esc.d}{PID}/smaps, /proc/${esc.d}{PID}/status, and /proc/${esc.d}{PID}/stat iterate
	echo -e "\n${esc.h} Total Memory:"  
	grep MemTotal /proc/meminfo | awk '{print ${esc.d}2}'
	echo -e "\n${esc.h} Cache:"
	grep Cache /proc/meminfo | awk '{print ${esc.d}2}'
	echo -e "\n${esc.h} Swap:"
	grep Swap /proc/meminfo | awk '{print ${esc.d}2}'
}

function getCPU {
	echo -e "\n${esc.h} CPU Info:" 
	lscpu
	top -bn 1 | awk 'BEGIN{FS="[ \t%]+"} NR==3{ print 100-${esc.d}8 }'
}

function getDiskInfo {
	echo -e "\n${esc.h} Disk Info:"   
	df -h
}

function getProcs {
	echo -e "\n${esc.h} PS Output:"   
	ps aux	
}

function getNetstat {
	echo -e "\n${esc.h} Network Sockets:"   
	netstat -tulpn
}

function getVM {
	echo -e "\n${esc.h} VM Info:" 
	lspci | grep -i vm
}

function getFileHandles {
	echo -e "\n${esc.h} Open File Handles:"  
	lsof
	echo -e "\n${esc.h} Network Open File Handles:"
	lsof -i
}

${esc.h}EXECUTE SYSTEM FUNCTIONS
echo "\n${esc.h}${esc.h}${esc.h}${esc.h}${esc.h} SYSTEM INFORMATION ${esc.h}${esc.h}${esc.h}${esc.h}${esc.h}" 
if [ skipOS != true ]
	then
		getOS
fi
getMemory
if [ skipCPU != true ]
	then
		getCPU
fi
getDiskInfo
getProcs
if [ skipNetStat != true ]
	then
		getNetStat
fi
if [ skipVM != true ]
	then
		getVM
fi		
if [ skipFileHandles != true ]
	then
	getFileHandles
fi
if [ skipTee != true ]
	then 
		exec &> >(tee -a "${esc.d}DOTLOGFILE")
	else
		exec > ${esc.d}DOTLOGFILE 2>&1
fi

${esc.h}DOTCMS FUNCTIONS
function getVersion {
	echo -e "\n${esc.h} Version:" 
	ls ${esc.d}DOTHOME/webapps/ROOT/WEB-INF/lib/dotcms*jar
	echo -e "\n${esc.h} Tomcat Version:" 
	echo ${esc.d}TOMCATVERSION
}

function getJVMInfo {
	echo -e "\n${esc.h} JVM INFORMATION AND MEMORY ALLOCATION:" 
	sudo -u ${esc.d}DOTOWNER jps -v
}

function getDotCMSMem {
	echo -e "\n${esc.h} Memory Info:" 
	echo ${esc.d}DOTPROCPID  | xargs ps -o rss,sz,vsz
}

function getIndexList {
	echo -e "\n${esc.h} List Indexes:"
	ls -l ${esc.d}DOTHOME/webapps/ROOT/dotsecure/esdata/dotCMSContentIndex_3x/nodes/*/indices
	echo -e "\n${esc.h} Index Content Volume:"
	find ${esc.d}DOTHOME/webapps/ROOT/dotsecure/esdata/dotCMSContentIndex_3x/nodes/ | wc -l
}

function getConfigFiles {
	echo -e "\n${esc.h} Config and log are located in the logs folder" 

	cp ${esc.d}DOTHOME/webapps/ROOT/WEB-INF/classes/dotmarketing-config.properties ${esc.d}LOGFOLDER

	cp ${esc.d}DOTHOME/webapps/ROOT/WEB-INF/classes/portal.properties ${esc.d}LOGFOLDER
	
	cp ${esc.d}DOTHOME/webapps/ROOT/WEB-INF/classes/dotcms-config-cluster.properties  ${esc.d}LOGFOLDER

	cp ${esc.d}DOTHOME/conf/server.xml ${esc.d}LOGFOLDER

	cp ${esc.d}DOTHOME/webapps/ROOT/dotsecure/logs/dotcms.log  ${esc.d}LOGFOLDER
	
	cp ${esc.d}DOTHOME/conf/web.xml ${esc.d}LOGFOLDER
	
}

function getPlugins {
	${esc.h}find -f on plugins, maybe tarball them, exclude build folder
	echo -e "\n${esc.h} Static Plugins:" 
	ls  ${esc.d}DOTHOME/webapps/ROOT/WEB-INF/lib
	echo -e "\n${esc.h} Dynamic Plugins: "
	ls ${esc.d}DOTHOME/webapps/ROOT/WEB-INF/felix/load
}

function getJavaDump {
	${esc.h}this should loop several times to get an idea of what's really going on
	echo -e "\n${esc.h}Java thread dump located at logs/javadump.txt" 
	PID=${esc.d}(pgrep -o -x java)
	sudo -u ${esc.d}DOTOWNER jstack ${esc.d}PID >> ${esc.d}LOGFOLDER/javadump.txt
}

function getPushConfig {
	echo -e "\n${esc.h} Push Info:"
	${esc.h}use admin login/pw to get via API
}

function getCacheStats {
	${esc.h}Version dependent, use jsp version of cache stats page (chris has one), add cmd line arg to dotcms inspector to indicate cachestats or not (default not)
	echo "\n${esc.h} CacheStats:"
}

function getAssetsInfo {
	${esc.h}look for asset_real_path, test writing  find ls -type f tree and write to separate file exclude dotGenerated, bundles, .zfs
	echo -e "\n${esc.h} Assets Permissions:" 
	ls -l ${esc.d}DOTHOME/webapps/ROOT/assets 
	
}

function getGCInfo {
	${esc.h}java opts lists gc log name
	echo -e "\n${esc.h} GC Info:"

	if [ -f ${esc.d}DOTHOME/webapps/ROOT/dotsecure/logs/*gc*.log ]; then

	cp ${esc.d}DOTHOME/webapps/ROOT/dotsecure/logs/*gc*.log ${esc.d}LOGFOLDER

	else
		echo "no GC log in ${esc.d}DOTHOME/webapps/ROOT/dotsecure/logs/"
	fi
}

${esc.h}EXECUTE DOTCMS FUNCTIONS
echo "dotCMS Inspector Run: " ${esc.d}RUNDATE 
echo -e "\n\n${esc.h}${esc.h}${esc.h}${esc.h}${esc.h} dotCMS INFORMATION ${esc.h}${esc.h}${esc.h}${esc.h}${esc.h}" 
getVersion
getJVMInfo
getDotCMSMem
getIndexList
getConfigFiles
getPlugins
getJavaDump
${esc.h}getPushConfig
${esc.h}getCacheStats
getAssetsInfo
getGCInfo

tar -czf "di-${esc.d}HOSTNAME-PROC${esc.d}DOTPROCPID-${esc.d}RUNDATE.tgz" ${esc.d}LOGFOLDER

rm -Rf ${esc.d}LOGFOLDER

done
exit