Initial commit
This commit is contained in:
commit
fe3bb0937f
|
@ -0,0 +1,2 @@
|
||||||
|
high5*
|
||||||
|
obsdams*
|
|
@ -0,0 +1,22 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2021 Baptiste DE RENZO
|
||||||
|
Copyright (c) 2023 Mischa Peters
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is furnished
|
||||||
|
to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice (including the next
|
||||||
|
paragraph) shall be included in all copies or substantial portions of the
|
||||||
|
Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
|
||||||
|
OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||||
|
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
|
||||||
|
OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
@ -0,0 +1,45 @@
|
||||||
|
# Tinystatus
|
||||||
|
|
||||||
|
tinystatus generate an html status page via shell script.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
* Parallel checks
|
||||||
|
* HTTP, ping, port checks
|
||||||
|
* HTTP expected status code (401, ...)
|
||||||
|
* Minimal dependencies (curl, nc and coreutils)
|
||||||
|
* Easy configuration and customisation
|
||||||
|
* Tiny (~1kb) optimized result page
|
||||||
|
* Incident history (manual)
|
||||||
|
|
||||||
|
## Demo
|
||||||
|
|
||||||
|
An example site is available [here](https://lab.bdro.fr/tinystatus/).
|
||||||
|
|
||||||
|
## Setup
|
||||||
|
|
||||||
|
To install tinystatus:
|
||||||
|
|
||||||
|
* Clone the repository and go to the created directory
|
||||||
|
* Edit `tinystatus` variables to your liking
|
||||||
|
* Edit the checks file `checks.csv`
|
||||||
|
* To add incidents or maintenance, edit `incidents.txt`
|
||||||
|
* Generate status page `./tinystatus`
|
||||||
|
* Serve the page with your favorite web server
|
||||||
|
|
||||||
|
## Configuration file
|
||||||
|
|
||||||
|
The syntax of `checks.csv` file is:
|
||||||
|
```
|
||||||
|
Command, Expected Code, Status Text, Host to check
|
||||||
|
```
|
||||||
|
|
||||||
|
Command can be:
|
||||||
|
* `http` - Check http status
|
||||||
|
* `ping` - Check ping status
|
||||||
|
* `port` - Check open port status
|
||||||
|
* `maint` - Host is in maintance
|
||||||
|
|
||||||
|
There are also `http4`, `http6`, `ping4`, `ping6`, `port4`, `port6` for IPv4 or IPv6 only check.
|
||||||
|
Note: `port4` and `port6` require OpenBSD `nc` binary.
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
http, 200, Google Website, https://google.com
|
||||||
|
http4, 200, Google Website (IPv4), https://google.com
|
||||||
|
http6, 200, Google Website (IPv6), https://google.com
|
||||||
|
http, 404, Google 404, https://google.com/dummy
|
||||||
|
maint, 200, Google Drive Maintenance, https://drive.google.com
|
||||||
|
ping, 0, Google ping, 8.8.8.8
|
||||||
|
port, 0, Google DNS, 8.8.8.8 53
|
|
|
@ -0,0 +1 @@
|
||||||
|
2023-05-28 21:17 - Short network outage was observed at our upstream provider between 2023-05-28 21:17:28 UTC and 2023-05-28 21:19:17 UTC
|
|
@ -0,0 +1,192 @@
|
||||||
|
#!/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"
|
||||||
|
PUSHOVER="/home/mischa/bin/pushover.pl"
|
||||||
|
PUSHOVER_CONF="/home/mischa/_pushover.conf"
|
||||||
|
PUSHOVER_STATUS="status"
|
||||||
|
RECIPIENT="mischa@high5.nl"
|
||||||
|
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
|
||||||
|
|
||||||
|
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: arg; do
|
||||||
|
case ${arg} in
|
||||||
|
c) CHECKFILE=${OPTARG};;
|
||||||
|
i) INCIDENTSFILE=${OPTARG};;
|
||||||
|
o) HTMLFILE=${OPTARG};;
|
||||||
|
*) 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}")"
|
||||||
|
method="$(get_element 5 "${line}")"
|
||||||
|
check "${ctype}" "${host}" "${name}" "${code}" &
|
||||||
|
done < "${CHECKFILE}"
|
||||||
|
wait
|
||||||
|
|
||||||
|
cat << EOF >> ${_htmlfile}
|
||||||
|
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><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
|
||||||
|
for file in "${tmp}/ko/"*.status; 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
|
||||||
|
for file in "${tmp}/maint/"*.status; do
|
||||||
|
[ -e "${file}" ] || continue
|
||||||
|
name="$(basename "${file%.status}")"
|
||||||
|
echo "<li>${name} <span class='status maint'>Maintenance</span></li>" >> ${_htmlfile}
|
||||||
|
done
|
||||||
|
for file in "${tmp}/ok/"*.status; 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) loosely based on <a href="https://github.com/bderenzo/tinystatus">Tinystatus</a></p>
|
||||||
|
</div>
|
||||||
|
</body></html>
|
||||||
|
EOF
|
||||||
|
|
||||||
|
cp ${_htmlfile} ${HTMLDIR}/${HTMLFILE}
|
||||||
|
rm -r "${tmp}" 2>/dev/null
|
Loading…
Reference in New Issue