uptimeatomic/tinystatus

216 lines
6.7 KiB
Bash
Executable File

#!/bin/ksh
#
export PATH=/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin
CHECKFILE="checks.csv"
HEADER="Global Status"
HTMLDIR="/var/www/htdocs/default"
HTMLFILE="index.html"
INCIDENTSFILE="incidents.txt"
PASTINCIDENTSFILE="pastincidents.txt"
PUSHOVER="/home/mischa/bin/pushover.pl"
PUSHOVER_CONF="/home/mischa/_pushover.conf"
PUSHOVER_STATUS="status"
RECIPIENT="alerts@high5.nl"
REFRESH=60
SENDER="tinystatus@high5.nl"
TIMEOUT=10
TITLE="Tiny Status"
USERAGENT="User-Agent: Mozilla/5.0 (OpenBSD; Intel OpenBSD 7.4; rv:109.0) Gecko/20100101 Firefox/113.0"
WORKDIR=/home/mischa/tinystatus
usage() {
echo "usage: ${0##*/} [-c checksfile] [-i incidentsfile] [-p pastincidentsfile] [-o htmlfile]" 1>&2
exit
}
get_element() {
echo "${2}" | awk -v col="${1}" -F',' '{gsub(/^[ \t]+|[ \t]+$/, "", $col); print $col}'
}
notify() {
name="${1}"
status="${2}"
priority="${3}"
if [ ${priority} == "KO" ]; then
echo "${status}." | mail -r "${TITLE} <${SENDER}>" -s "${name} DOWN" ${RECIPIENT}
${PUSHOVER} -c ${PUSHOVER_CONF} -t "${TITLE}" -m "${name} DOWN ${status}." -p 1 >/dev/null 2>&1
touch "${PUSHOVER_STATUS}/${name}"
fi
if [ ${priority} == "OK" ]; then
_seconds=$(expr $(date +%s) - $(stat -r ${PUSHOVER_STATUS}/${name} | awk '{print $11}'))
_downtime=$(date -r${_seconds} -u +%H:%M:%S)
echo "${status} - down for ${_downtime}" | mail -r "${TITLE} <${SENDER}>" -s "${name} OK" ${RECIPIENT}
${PUSHOVER} -c ${PUSHOVER_CONF} -t "${TITLE}" -m "${name} OK ${status} - down for ${_downtime}" >/dev/null 2>&1
rm -rf "${PUSHOVER_STATUS}/${name}"
fi
}
check(){
ctype="${1}"
host="${2}"
name="${3}"
expectedcode="${4}"
IPv="${ctype#(http|ping|port)}"
case "${ctype}" in
http*)
statuscode="$(curl -${IPv}sSkLo /dev/null -H "${USERAGENT}" -m "${TIMEOUT}" -w "%{http_code}" "${host}" 2> "${tmp}/ko/${name}.error")"
if [ "${statuscode}" -ne "${expectedcode}" ]; then
if [ -s "${tmp}/ko/${name}.error" ]; then
sed -e 's,curl: ([0-9]*) ,,' "${tmp}/ko/${name}.error" > "${tmp}/ko/${name}.status"
else
echo "Status code: ${statuscode}" > "${tmp}/ko/${name}.status"
fi
else
echo "Status code: ${statuscode}" > "${tmp}/ok/${name}.status"
fi
;;
ping*)
ping -${IPv}w "${TIMEOUT}" -c 1 "${host}" >/dev/null 2>&1
statuscode=$?
if [ "${statuscode}" -ne "${expectedcode}" ]; then
echo "Host unreachable" > "${tmp}/ko/${name}.status"
else
echo "Host reachable" > "${tmp}/ok/${name}.status"
fi
;;
port*)
error="$(nc -${IPv}w "${TIMEOUT}" -zv ${host} 2>&1)"
statuscode=$?
if [ "${statuscode}" -ne "${expectedcode}" ]; then
echo "Connection refused" > "${tmp}/ko/${name}.status"
else
echo "Connection succeeded" > "${tmp}/ok/${name}.status"
fi
;;
maint*)
echo "Maintenance" > "${tmp}/maint/${name}.status"
;;
esac
}
while getopts c:i:o:h arg; do
case ${arg} in
c) CHECKFILE=${OPTARG};;
i) INCIDENTSFILE=${OPTARG};;
p) PASTINCIDENTSFILE=${OPTARG};;
o) HTMLFILE=${OPTARG};;
h) usage;;
*) usage;;
esac
done
cd ${WORKDIR}
if [ ! -e "${CHECKFILE}" ]; then
echo "Checkfile ${WORKDIR}/${CHECKFILE} doesn't exist."
exit
fi
tmp="$(mktemp -d)"
mkdir -p "${tmp}/ok" "${tmp}/ko" "${tmp}/maint" || exit 1
_htmlfile="${tmp}/${HTMLFILE}"
while IFS="$(printf '\n')" read -r line; do
ctype="$(get_element 1 "${line}")"
code="$(get_element 2 "${line}")"
name="$(get_element 3 "${line}")"
host="$(get_element 4 "${line}")"
check "${ctype}" "${host}" "${name}" "${code}" &
done < "${CHECKFILE}"
wait
cat << EOF >> ${_htmlfile}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="refresh" content="${REFRESH}">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>${TITLE}</title>
<style>
body { font-family: segoe ui,Roboto,Oxygen-Sans,Ubuntu,Cantarell,helvetica neue,Verdana,sans-serif; }
h1 { margin-top: 30px; }
ul { padding: 0px; }
li { list-style: none; margin-bottom: 2px; padding: 5px; border-bottom: 1px solid #ddd; }
.container { max-width: 600px; width: 100%; margin: 15px auto; }
.panel { text-align: center; padding: 10px; border: 0px; border-radius: 5px; }
.failed-bg { color: white; background-color: #E25D6A; }
.success-bg { color: white; background-color: #52B86A; }
.maint-bg { color: white; background-color: #5DADE2; }
.failed { color: #E25D6A; }
.success { color: #52B86A; }
.maint { color: #5DADE2; }
.small { font-size: 80%; }
.status { float: right; }
</style>
</head>
<body>
<div class='container'>
<h1>${HEADER}</h1>
EOF
_outage_count="$(find "${tmp}/ko" -mindepth 1 | grep -c 'status$')"
_maint_count="$(find "${tmp}/maint" -mindepth 1 | grep -c 'status$')"
if [ "${_outage_count}" -ne 0 ]; then
echo "<ul><li class='panel failed-bg'>${_outage_count} Outage(s)</li></ul>" >> ${_htmlfile}
fi
if [ "${_maint_count}" -ne 0 ]; then
echo "<ul><li class='panel maint-bg'>${_maint_count} Maintenance</li></ul>" >> ${_htmlfile}
fi
if [[ "${_outage_count}" -eq 0 && "${_maint_count}" -eq 0 ]]; then
echo "<ul><li class='panel success-bg'>All Systems Operational</li></ul>" >> ${_htmlfile}
fi
if [ -f "${INCIDENTSFILE}" ]; then
echo '<h1>Incidents</h1>' >> ${_htmlfile}
if [ -s "${INCIDENTSFILE}" ]; then
sed 's|^\(.*\)$|<p>\1</p>|' "${INCIDENTSFILE}" >> ${_htmlfile}
else
echo '<p>No incident reported yet ;)</p>' >> ${_htmlfile}
fi
fi
cat << EOF >> ${_htmlfile}
<h1>Services</h1>
<ul>
EOF
ls ${tmp}/ko/*.status 2>/dev/null | sort -V | while read file; do
[ -e "${file}" ] || continue
name="$(basename "${file%.status}")"
status="$(cat "${file}")"
echo "<li>${name} <span class='small failed'>(${status})</span><span class='status failed'>Disrupted</span></li>" >> ${_htmlfile}
if [ ! -e "${PUSHOVER_STATUS}/${name}" ]; then
notify "${name}" "${status}" "KO"
fi
done
ls ${tmp}/maint/*.status 2>/dev/null | sort -V | while read file; do
[ -e "${file}" ] || continue
name="$(basename "${file%.status}")"
echo "<li>${name} <span class='status maint'>Maintenance</span></li>" >> ${_htmlfile}
done
ls ${tmp}/ok/*.status 2>/dev/null | sort -V | while read file; do
[ -e "${file}" ] || continue
name="$(basename "${file%.status}")"
status="$(cat "${file}")"
echo "<li>${name} <span class='status success'>Operational</span></li>" >> ${_htmlfile}
if [ -e "${PUSHOVER_STATUS}/${name}" ]; then
notify "${name}" "${status}" "OK"
fi
done
cat << EOF >> ${_htmlfile}
</ul>
<p class=small> Last check: $(date +%FT%T%z) <a href="https://git.high5.nl/tinystatus/">Tinystatus</a> loosely based on <a href="https://github.com/bderenzo/tinystatus">Tinystatus</a></p>
EOF
if [ -s "${PASTINCIDENTSFILE}" ]; then
echo '<h1>Past Incidents</h1>' >> ${_htmlfile}
sed 's|^\(.*\)$|<p>\1</p>|' "${PASTINCIDENTSFILE}" >> ${_htmlfile}
fi
cat << EOF >> ${_htmlfile}
</div>
</body>
</html>
EOF
cp ${_htmlfile} ${HTMLDIR}/${HTMLFILE}
rm -r "${tmp}" 2>/dev/null