ecMK.sh 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371
  1. #!/bin/bash
  2. # Author : mrkips (Cybergavin)
  3. # Date Created : 4th April 2015
  4. # Description : This script accepts email requests for the health status of applications/hosts and sends
  5. # a response back to the requester with results from the Check_MK monitoring system.
  6. # Tested with Check_MK 1.2.8p9, Postfix 2.6.6, RHEL 6
  7. #~~~~~~~~~~~~~~~~~~
  8. # VERSION HISTORY
  9. #~~~~~~~~~~~~~~~~~~
  10. # v1.0 | 04-Apr-2015 | First Version
  11. # v2.0 | 03-Dec-2015 | Created URLs for hosts pointing to their Check_MK pages
  12. #
  13. ##############################################################################################
  14. #
  15. # Determine Script Location and define directories
  16. #
  17. if [ -n "`dirname $0 | grep '^/'`" ]; then
  18. SCRIPT_LOCATION=`dirname $0`
  19. elif [ -n "`dirname $0 | grep '^..'`" ]; then
  20. cd `dirname $0`
  21. SCRIPT_LOCATION=$PWD
  22. cd - > /dev/null
  23. else
  24. SCRIPT_LOCATION=`echo ${PWD}/\`dirname $0\` | sed 's#\/\.$##g'`
  25. fi
  26. #
  27. # Variables and Constants
  28. #
  29. LOGDIR=${SCRIPT_LOCATION}/logs
  30. DATADIR=${SCRIPT_LOCATION}/data
  31. WORKDIR=${SCRIPT_LOCATION}/work
  32. TDATE=`date '+%Y%m%d'`
  33. YMDATE=`date '+%Y%m'`
  34. TIMESTAMP=`date '+%Y%m%d%H%M%S'`
  35. SCRIPT_NAME=`basename $0`
  36. OUTFILE=${SCRIPT_LOCATION}/${SCRIPT_NAME%%.*}.out
  37. ERRFILE=${SCRIPT_LOCATION}/${SCRIPT_NAME%%.*}.err
  38. LOGFILE=${LOGDIR}/${SCRIPT_NAME%%.*}_${YMDATE}.log
  39. DATAFILE=${DATADIR}/${SCRIPT_NAME%%.*}_${YMDATE}.tsv
  40. MAILFILE=${WORKDIR}/${SCRIPT_NAME%%.*}_$$.txt
  41. HTMLFILE=${WORKDIR}/${SCRIPT_NAME%%.*}.html
  42. CMKFILE=${WORKDIR}/${SCRIPT_NAME%%.*}.check
  43. MUTTRC=${SCRIPT_LOCATION}/.muttrc
  44. #
  45. # Redirect stdout and stderr
  46. #
  47. exec 1> $OUTFILE
  48. exec 2> $ERRFILE
  49. #
  50. # Validate directory structure
  51. #
  52. for mydir in $LOGDIR $DATADIR $WORKDIR
  53. do
  54. if [ ! -d $mydir ]; then
  55. mkdir $mydir 2> /dev/null
  56. if [ $? -ne 0 ]; then
  57. echo "\n`date '+%Y%m%d_%H%M'` : CRITICAL : $mydir does not exist and cannot be created. Exiting..."
  58. exit 999
  59. fi
  60. fi
  61. done
  62. #####################################################################################
  63. # Functions
  64. #####################################################################################
  65. #
  66. # Parsing Functions
  67. #
  68. function parseMail
  69. {
  70. # Extract application(s) and host(s) - Try subject first (preferred) and then body
  71. sender=`grep "^From:" $MAILFILE | awk '{print $NF}' | sed 's/<//g;s/>//g'`
  72. # First try subject..
  73. sub=`grep "^Subject:" $MAILFILE | grep -v Undeliverable | head -1`
  74. if [ -n "$sub" ]; then
  75. myhosts=`echo $sub | awk -F':' '{ for (i=1;i<=NF;i++) if (tolower($i) ~ /host=/) print $i}' | cut -d'=' -f2`
  76. myapps=`echo $sub | awk -F':' '{ for (i=1;i<=NF;i++) if (tolower($i) ~ /app=/) print $i}' | cut -d'=' -f2`
  77. fi
  78. # ..And then try Body
  79. if [ -n "`grep \"Content-Transfer-Encoding: quoted-printable\" $MAILFILE`" ]; then
  80. myhosts="${myhosts} `sed -n '/Content-Transfer-Encoding: quoted-printable/,/Content-Transfer-Encoding: quoted-printable/p' $MAILFILE | grep -i \"Host=\" | awk -F'=3D' '{print $2}'`"
  81. myapps="${myapps} `sed -n '/Content-Transfer-Encoding: quoted-printable/,/Content-Transfer-Encoding: quoted-printable/p' $MAILFILE | grep -i \"App=\" | awk -F'=3D' '{print $2}'`"
  82. elif [ -n "`grep \"Content-Transfer-Encoding: base64\" $MAILFILE`" ]; then
  83. myhosts="${myhosts} `sed -n '/Content-Transfer-Encoding: base64/,/Content-Transfer-Encoding: base64/p' $MAILFILE | sed '/^Content/d;/^--/d;/^$/d' | base64 -d | grep -i \"Host=\" | awk -F'=' '{print $2}' | tr -d '\r'`"
  84. myapps="${myapps} `sed -n '/Content-Transfer-Encoding: base64/,/Content-Transfer-Encoding: base64/p' $MAILFILE | sed '/^Content/d;/^--/d;/^$/d' | base64 -d | grep -i \"App=\" | awk -F'=' '{print $2}' | tr -d '\r'`"
  85. fi
  86. }
  87. #
  88. # Validation Functions
  89. #
  90. function ignoreNDR
  91. {
  92. ndr=0
  93. if [ -n "`grep \"^Subject: Undeliverable\" $MAILFILE`" ]; then
  94. ndr=1
  95. fi
  96. echo $ndr
  97. }
  98. function validateApp
  99. {
  100. cat<<EOF | /omd/sites/mysite/local/bin/unixcat /omd/sites/mysite/tmp/run/live
  101. GET hostgroups
  102. Columns: name
  103. Filter: name = $1
  104. EOF
  105. }
  106. function validateHost
  107. {
  108. cat<<EOF | /omd/sites/mysite/local/bin/unixcat /omd/sites/mysite/tmp/run/live
  109. GET hosts
  110. Columns: name
  111. Filter: name = $1
  112. EOF
  113. }
  114. #
  115. # Data Query Functions
  116. #
  117. function lqlHS
  118. {
  119. # Determine count of services in various states for a given host using LQL
  120. l_host=$1
  121. cat<<EOF | /omd/sites/mysite/local/bin/unixcat /omd/sites/mysite/tmp/run/live
  122. GET services
  123. Filter: host_name = $l_host
  124. Stats: state = 0
  125. Stats: state = 1
  126. Stats: state = 2
  127. Stats: state = 3
  128. EOF
  129. }
  130. function lqlAH
  131. {
  132. # Determine hosts belonging to a given hostgroup (application) using LQL
  133. l_app=$1
  134. cat<<EOF | /omd/sites/mysite/local/bin/unixcat /omd/sites/mysite/tmp/run/live
  135. GET hostgroups
  136. Columns: name members
  137. Filter: name = $l_app
  138. EOF
  139. }
  140. function lqlHG
  141. {
  142. # Determine list of hostgroups (applications) using LQL
  143. cat<<EOF | /omd/sites/mysite/local/bin/unixcat /omd/sites/mysite/tmp/run/live
  144. GET hostgroups
  145. Columns: name
  146. EOF
  147. }
  148. #
  149. # HTML Build Functions
  150. #
  151. function htmlInit
  152. {
  153. # Initialize HTML file for email body - HEAD section with CSS
  154. if [ -f $HTMLFILE ]; then
  155. rm $HTMLFILE
  156. fi
  157. cat <<EOF > $HTMLFILE
  158. <html>
  159. <head>
  160. <style>
  161. .datagrid1 table { border: 1px solid blue; border-collapse: collapse; text-align: center; width: 70%; font: normal 12px/150% Verdana, Arial, Helvetica, sans-serif; }
  162. .datagrid2 table { border: 1px solid #960D06; border-collapse: collapse; text-align: left; width: 70%; font: normal 12px/150% Verdana, Arial, Helvetica, sans-serif; }
  163. .datagrid1 td {border: 1px solid #588FEC;}
  164. .datagrid2 td {border: 1px solid #960D06;}
  165. .invalid {text-align: justify; font: normal 14px /150% Verdana, Arial, Helvetica, sans-serif; }
  166. .invalid table {text-align: justify; font: normal 12px /150% Verdana, Arial, Helvetica, sans-serif; border: 1px solid blue; border-collapse: collapse; width: 20%; }
  167. .alt { background: #E1EEf4; color: #000000; }
  168. .header1 { background: #E1EEf4; color: #588FEC; font-size: 14px; border: 1px solid blue; }
  169. .header2 { background: #FBDEDC; color: #961511; font-size: 14px; border: 1px solid #960D06; }
  170. .host1 { background: #588FEC; color: #FFFFFF; border: 1px solid blue; }
  171. .host2 { background: #FBDEDC; color: #961511; border: 1px solid #960D06; }
  172. .ok { background: #117C23; color: #FFFFFF; border: 1px solid blue; }
  173. .warn { background: #EEF47C; color: #000000; border: 1px solid blue; }
  174. .crit { background: #C51616; color: #FFFFFF; border: 1px solid blue; }
  175. .unknown { background: #FBC138; color: #000000; border: 1px solid blue; }
  176. </style>
  177. </head>
  178. <body>
  179. EOF
  180. }
  181. function htmlHostStatus
  182. {
  183. # Start HTML Table for HOST HEALTH SUMMARY
  184. cat <<EOF >> $HTMLFILE
  185. <div class="datagrid1"><table>
  186. <thead><tr><th class="header1" colspan="5">HOST HEALTH SUMMARY</th></thead>
  187. <thead><tr><th class="host1" width="20%">HOST</th><th class="ok" width="10%">OK</th><th class="warn" width="10%">WARN</th><th class="crit" width="10%">CRIT</th><th class="unknown" width="10%">UNKNOWN</th></tr></thead>
  188. <tbody>
  189. EOF
  190. }
  191. function htmlHostAlerts
  192. {
  193. # End HTML Table for HOST HEALTH SUMMARY and start HTML Table for HOST ALERTS
  194. cat <<EOF >> $HTMLFILE
  195. </tbody>
  196. </table></div>
  197. <br/><br/>
  198. <div class="datagrid2"><table>
  199. <thead><tr><th class="header2" colspan="3">HOST ALERTS</th></thead>
  200. <thead><tr><th class="host2">HOST</th><th class="host2">ALERT</th><th class="host2">DESCRIPTION</th></tr></thead>
  201. <tbody>
  202. EOF
  203. }
  204. function htmlFooter
  205. {
  206. # End HTML for Email Body
  207. cat <<EOF >> $HTMLFILE
  208. </tbody>
  209. </table></div>
  210. <br /><br />
  211. <p align="justify">ecMK is an application that allows users to request the health status (from Check_MK) of IT Infrastructure via email. Refer <b><a href="http://wiki.corp.abc.com/index.php/ecMK#ecMK_Usage">ecMK</a></b> for more details.</p>
  212. </body>
  213. </html>
  214. EOF
  215. }
  216. function htmlInvalidApp
  217. {
  218. # Start HTML for Email Body when a request for health status for invalid application(s)/host(s) is received
  219. cat <<EOF >> $HTMLFILE
  220. <div class="invalid">
  221. You have sent an <b>INVALID</b> request for Application/Host Health status. In order to request Application health status, use <b>App=&lt;app name&gt;</b> in the email body. E.g. App=cards. In order to request Host health status, use <b>Host=&lt;host name&gt;</b> in the email body. E.g. Host=vtorcrddv01. <br />
  222. Given below is a list of valid applications: <br /><br />
  223. </div>
  224. <div class="invalid"><table>
  225. <thead><tr><th class="header1">APPLICATIONS</th></tr></thead>
  226. <tbody>
  227. EOF
  228. }
  229. #
  230. # Post-Processing Functions
  231. #
  232. function doHousekeep
  233. {
  234. # Housekeeping - cleanup
  235. if [ -f $MAILFILE ]; then
  236. rm $MAILFILE
  237. fi
  238. find $DATADIR -type f -name "*.tsv" -mtime +90 | xargs rm -f
  239. }
  240. function collectStats
  241. {
  242. # Collect statistics on script usage
  243. if [ -f $DATAFILE ]; then
  244. echo "${TIMESTAMP}~${sender}~${myapps}~${myhosts}" >> $DATAFILE
  245. else
  246. echo "TIMESTAMP~SENDER~APPS~HOSTS" > $DATAFILE
  247. echo "${TIMESTAMP}~${sender}~${myapps}~${myhosts}" >> $DATAFILE
  248. fi
  249. }
  250. #####################################################################################
  251. # Main
  252. #####################################################################################
  253. # Accept email from postfix
  254. cat - > $MAILFILE
  255. # Parse email
  256. parseMail
  257. # Check for NDR
  258. if [ `ignoreNDR` -eq 1 ]; then
  259. collectStats
  260. echo "${TIMESTAMP} : Received NDR, ignoring." >> $LOGFILE
  261. doHousekeep
  262. exit 0
  263. fi
  264. # Start building HTML for EMail Body
  265. htmlInit
  266. # Validate app(s) and host(s)
  267. unset capp chost
  268. for a in `echo $myapps | sed 's/,/ /g'`
  269. do
  270. capp="`validateApp ${a}` ${capp}"
  271. done
  272. if [ -n "${capp}" ]; then
  273. for h in $capp
  274. do
  275. ah="`lqlAH $h | cut -d';' -f2 | sed 's/,/ /g'` ${ah}"
  276. done
  277. fi
  278. if [ -n "${ah}" -o -n "`echo $myhosts | sed 's/,/ /g'`" ]; then
  279. for ch in $ah `echo $myhosts | sed 's/,/ /g'`
  280. do
  281. chost="`validateHost ${ch}` ${chost}"
  282. done
  283. fi
  284. # Work on app(s) and host(s)
  285. if [ -n "${chost}" ]; then
  286. htmlHostStatus
  287. r=1
  288. s1t=s2t=s3t=0
  289. for myhost in $chost
  290. do
  291. svcs=`lqlHS $myhost`
  292. s0=`echo $svcs | cut -d';' -f1`
  293. s1=`echo $svcs | cut -d';' -f2`
  294. s2=`echo $svcs | cut -d';' -f3`
  295. s3=`echo $svcs | cut -d';' -f4`
  296. if [ $((r%2)) -eq 1 ]; then
  297. cat<<EOF >> $HTMLFILE
  298. <tr><td><a href="https://mysitecmk/mysite/check_mk/view.py?view_name=host&host=${myhost}">${myhost}</a></td><td>${s0}</td><td>${s1}</td><td>${s2}</td><td>${s3}</td></tr>
  299. EOF
  300. else
  301. cat<<EOF >> $HTMLFILE
  302. <tr class="alt"><td><a href="https://mysitecmk/mysite/check_mk/view.py?view_name=host&host=${myhost}">${myhost}</a></td><td>${s0}</td><td>${s1}</td><td>${s2}</td><td>${s3}</td></tr>
  303. EOF
  304. fi
  305. r=$((r+1))
  306. s1t=$((s1t+s1))
  307. s2t=$((s2t+s2))
  308. s3t=$((s3t+s3))
  309. done
  310. if [ $s1t -gt 0 -o $s2t -gt 0 -o $s3t -gt 0 ]; then
  311. htmlHostAlerts
  312. for myhost in $chost
  313. do
  314. svcs=`lqlHS $myhost`
  315. s0=`echo $svcs | cut -d';' -f1`
  316. s1=`echo $svcs | cut -d';' -f2`
  317. s2=`echo $svcs | cut -d';' -f3`
  318. s3=`echo $svcs | cut -d';' -f4`
  319. /omd/sites/mysite/local/bin/check_mk -nv $myhost > $CMKFILE
  320. if [ $s1 -gt 0 ]; then
  321. grep "WARN -" $CMKFILE | while read line
  322. do
  323. s1_svc=`echo $line | cut -d'-' -f2-`
  324. cat<<EOF >> $HTMLFILE
  325. <tr><td>${myhost}</td><td class="warn">WARN</td><td>${s1_svc}</td></tr>
  326. EOF
  327. done
  328. fi
  329. if [ $s2 -gt 0 ]; then
  330. grep "CRIT -" $CMKFILE | while read line
  331. do
  332. s2_svc=`echo $line | cut -d'-' -f2-`
  333. cat<<EOF >> $HTMLFILE
  334. <tr><td>${myhost}</td><td class="crit">CRIT</td><td>${s2_svc}</td></tr>
  335. EOF
  336. done
  337. fi
  338. if [ $s3 -gt 0 ]; then
  339. grep "UNKNOWN -" $CMKFILE | while read line
  340. do
  341. s3_svc=`echo $line | cut -d'-' -f2-`
  342. cat<<EOF >> $HTMLFILE
  343. <tr><td>${myhost}</td><td class="unknown">UNKNOWN</td><td>${s3_svc}</td></tr>
  344. EOF
  345. done
  346. fi
  347. done
  348. htmlFooter
  349. elif [ $s1t -eq 0 -a $s2t -eq 0 -a $s3t -eq 0 ]; then
  350. echo "</tbody></table></div><br/><br/><h3>OVERALL HEALTH : <font style=\"font-family: Verdana, Arial, Helvetica, sans-serif; color:green;\">OK</font></h3><p align=\"justify\">ecMK is an application that allows users to request the health status (from Check_MK) of IT Infrastructure via email. Refer <b><a href=\"http://wiki.corp.abc.com/index.php/ecMK#ecMK_Usage\">ecMK</a></b> for more details.</p></body></html>" >> $HTMLFILE
  351. fi
  352. if [ -n "${capp}" ]; then
  353. sub="Application Health Status : ${capp}"
  354. else
  355. sub="Host Health Status : ${chost}"
  356. fi
  357. mutt -F ${MUTTRC} -s "${sub}" $sender < $HTMLFILE
  358. else
  359. htmlInvalidApp
  360. v_apps=`lqlHG`
  361. for v in $v_apps
  362. do
  363. cat<<EOF >> $HTMLFILE
  364. <tr><td class="alt">${v}</td></tr>
  365. EOF
  366. done
  367. htmlFooter
  368. mutt -F ${MUTTRC} -s "Health Status : INVALID Request" $sender < $HTMLFILE
  369. fi
  370. collectStats
  371. doHousekeep