alarm-notify.sh.in 133 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521
  1. #!/usr/bin/env bash
  2. #shellcheck source=/dev/null disable=SC2086,SC2154
  3. # netdata
  4. # real-time performance and health monitoring, done right!
  5. # (C) 2023 Netdata Inc.
  6. # SPDX-License-Identifier: GPL-3.0-or-later
  7. #
  8. # Script to send alarm notifications for netdata
  9. #
  10. # Features:
  11. # - multiple notification methods
  12. # - multiple roles per alarm
  13. # - multiple recipients per role
  14. # - severity filtering per recipient
  15. #
  16. # Supported notification methods:
  17. # - emails by @ktsaou
  18. # - slack.com notifications by @ktsaou
  19. # - alerta.io notifications by @kattunga
  20. # - discord.com notifications by @lowfive
  21. # - pushover.net notifications by @ktsaou
  22. # - pushbullet.com push notifications by Tiago Peralta @tperalta82 #1070
  23. # - telegram.org notifications by @hashworks #1002
  24. # - twilio.com notifications by Levi Blaney @shadycuz #1211
  25. # - kafka notifications by @ktsaou #1342
  26. # - pagerduty.com notifications by Jim Cooley @jimcooley #1373
  27. # - messagebird.com notifications by @tech_no_logical #1453
  28. # - hipchat notifications by @ktsaou #1561
  29. # - fleep notifications by @Ferroin
  30. # - prowlapp.com notifications by @Ferroin
  31. # - irc notifications by @manosf
  32. # - custom notifications by @ktsaou
  33. # - syslog messages by @Ferroin
  34. # - Microsoft Team notification by @tioumen
  35. # - RocketChat notifications by @Hermsi1337 #3777
  36. # - Dynatrace Event by @illumine
  37. # - Opsgenie by @thiaoftsm #9858
  38. # - Gotify by @coffeegrind123
  39. # - ntfy.sh by @Dim-P
  40. # -----------------------------------------------------------------------------
  41. # testing notifications
  42. if { [ "${1}" = "test" ] || [ "${2}" = "test" ]; } && [ "${#}" -le 2 ]; then
  43. if [ "${2}" = "test" ]; then
  44. recipient="${1}"
  45. else
  46. recipient="${2}"
  47. fi
  48. [ -z "${recipient}" ] && recipient="sysadmin"
  49. id=1
  50. last="CLEAR"
  51. test_res=0
  52. for x in "WARNING" "CRITICAL" "CLEAR"; do
  53. echo >&2
  54. echo >&2 "# SENDING TEST ${x} ALARM TO ROLE: ${recipient}"
  55. "${0}" "${recipient}" "$(hostname)" 1 1 "${id}" "$(date +%s)" "test_alarm" "test.chart" "${x}" "${last}" 100 90 "${0}" 1 $((0 + id)) "units" "this is a test alarm to verify notifications work" "new value" "old value" "evaluated expression" "expression variable values" 0 0 "" "" "Test" "command to edit the alarm=0=$(hostname)" "" "" "a test alarm"
  56. #shellcheck disable=SC2181
  57. if [ $? -ne 0 ]; then
  58. echo >&2 "# FAILED"
  59. test_res=1
  60. else
  61. echo >&2 "# OK"
  62. fi
  63. last="${x}"
  64. id=$((id + 1))
  65. done
  66. exit $test_res
  67. fi
  68. export PATH="${PATH}:/sbin:/usr/sbin:/usr/local/sbin"
  69. export LC_ALL=C
  70. # -----------------------------------------------------------------------------
  71. PROGRAM_NAME="$(basename "${0}")"
  72. LOG_LEVEL_ERR=1
  73. LOG_LEVEL_WARN=2
  74. LOG_LEVEL_INFO=3
  75. LOG_LEVEL="$LOG_LEVEL_INFO"
  76. set_log_severity_level() {
  77. case ${NETDATA_LOG_SEVERITY_LEVEL,,} in
  78. "info") LOG_LEVEL="$LOG_LEVEL_INFO";;
  79. "warn" | "warning") LOG_LEVEL="$LOG_LEVEL_WARN";;
  80. "err" | "error") LOG_LEVEL="$LOG_LEVEL_ERR";;
  81. esac
  82. }
  83. set_log_severity_level
  84. logdate() {
  85. date "+%Y-%m-%d %H:%M:%S"
  86. }
  87. log() {
  88. local status="${1}"
  89. shift
  90. echo >&2 "$(logdate): ${PROGRAM_NAME}: ${status}: ${*}"
  91. }
  92. info() {
  93. [[ -n "$LOG_LEVEL" && "$LOG_LEVEL_INFO" -gt "$LOG_LEVEL" ]] && return
  94. log INFO "${@}"
  95. }
  96. warning() {
  97. [[ -n "$LOG_LEVEL" && "$LOG_LEVEL_WARN" -gt "$LOG_LEVEL" ]] && return
  98. log WARNING "${@}"
  99. }
  100. error() {
  101. [[ -n "$LOG_LEVEL" && "$LOG_LEVEL_ERR" -gt "$LOG_LEVEL" ]] && return
  102. log ERROR "${@}"
  103. }
  104. fatal() {
  105. log FATAL "${@}"
  106. exit 1
  107. }
  108. debug=${NETDATA_ALARM_NOTIFY_DEBUG-0}
  109. debug() {
  110. [ "${debug}" = "1" ] && log DEBUG "${@}"
  111. }
  112. docurl() {
  113. if [ -z "${curl}" ]; then
  114. error "${curl} is unset."
  115. return 1
  116. fi
  117. if [ "${debug}" = "1" ]; then
  118. echo >&2 "--- BEGIN curl command ---"
  119. printf >&2 "%q " ${curl} "${@}"
  120. echo >&2
  121. echo >&2 "--- END curl command ---"
  122. local out code ret
  123. out=$(mktemp /tmp/netdata-health-alarm-notify-XXXXXXXX)
  124. code=$(${curl} ${curl_options} --write-out "%{http_code}" --output "${out}" --silent --show-error "${@}")
  125. ret=$?
  126. echo >&2 "--- BEGIN received response ---"
  127. cat >&2 "${out}"
  128. echo >&2
  129. echo >&2 "--- END received response ---"
  130. echo >&2 "RECEIVED HTTP RESPONSE CODE: ${code}"
  131. rm "${out}"
  132. echo "${code}"
  133. return ${ret}
  134. fi
  135. ${curl} ${curl_options} --write-out "%{http_code}" --output /dev/null --silent --show-error "${@}"
  136. return $?
  137. }
  138. # -----------------------------------------------------------------------------
  139. # List of all the notification mechanisms we support.
  140. # Used in a couple of places to write more compact code.
  141. method_names="
  142. email
  143. pushover
  144. pushbullet
  145. telegram
  146. slack
  147. alerta
  148. flock
  149. discord
  150. hipchat
  151. twilio
  152. messagebird
  153. pd
  154. fleep
  155. syslog
  156. custom
  157. msteams
  158. kavenegar
  159. prowl
  160. irc
  161. awssns
  162. rocketchat
  163. sms
  164. dynatrace
  165. matrix
  166. ntfy
  167. "
  168. # -----------------------------------------------------------------------------
  169. # this is to be overwritten by the config file
  170. custom_sender() {
  171. info "not sending custom notification for ${status} of '${host}.${chart}.${name}'"
  172. }
  173. # -----------------------------------------------------------------------------
  174. # check for BASH v4+ (required for associative arrays)
  175. if [ ${BASH_VERSINFO[0]} -lt 4 ]; then
  176. fatal "BASH version 4 or later is required (this is ${BASH_VERSION})."
  177. fi
  178. # -----------------------------------------------------------------------------
  179. # defaults to allow running this script by hand
  180. [ -z "${NETDATA_USER_CONFIG_DIR}" ] && NETDATA_USER_CONFIG_DIR="@configdir_POST@"
  181. [ -z "${NETDATA_STOCK_CONFIG_DIR}" ] && NETDATA_STOCK_CONFIG_DIR="@libconfigdir_POST@"
  182. [ -z "${NETDATA_CACHE_DIR}" ] && NETDATA_CACHE_DIR="@cachedir_POST@"
  183. [ -z "${NETDATA_REGISTRY_URL}" ] && NETDATA_REGISTRY_URL="https://registry.my-netdata.io"
  184. [ -z "${NETDATA_REGISTRY_CLOUD_BASE_URL}" ] && NETDATA_REGISTRY_CLOUD_BASE_URL="https://app.netdata.cloud"
  185. # -----------------------------------------------------------------------------
  186. # parse command line parameters
  187. if [[ ${1} = "unittest" ]]; then
  188. unittest=1 # enable unit testing mode
  189. roles="${2}" # the role that should be used for unit testing
  190. cfgfile="${3}" # the location of the config file to use for unit testing
  191. status="${4}" # the current status : REMOVED, UNINITIALIZED, UNDEFINED, CLEAR, WARNING, CRITICAL
  192. old_status="${5}" # the previous status: REMOVED, UNINITIALIZED, UNDEFINED, CLEAR, WARNING, CRITICAL
  193. elif [[ ${1} = "dump_methods" ]]; then
  194. dump_methods=1
  195. status="WARNING"
  196. else
  197. roles="${1}" # the roles that should be notified for this event
  198. args_host="${2}" # the host generated this event
  199. unique_id="${3}" # the unique id of this event
  200. alarm_id="${4}" # the unique id of the alarm that generated this event
  201. event_id="${5}" # the incremental id of the event, for this alarm id
  202. when="${6}" # the timestamp this event occurred
  203. name="${7}" # the name of the alarm, as given in netdata health.d entries
  204. chart="${8}" # the name of the chart (type.id)
  205. status="${9}" # the current status : REMOVED, UNINITIALIZED, UNDEFINED, CLEAR, WARNING, CRITICAL
  206. old_status="${10}" # the previous status: REMOVED, UNINITIALIZED, UNDEFINED, CLEAR, WARNING, CRITICAL
  207. value="${11}" # the current value of the alarm
  208. old_value="${12}" # the previous value of the alarm
  209. src="${13}" # the line number and file the alarm has been configured
  210. duration="${14}" # the duration in seconds of the previous alarm state
  211. non_clear_duration="${15}" # the total duration in seconds this is/was non-clear
  212. units="${16}" # the units of the value
  213. info="${17}" # a short description of the alarm
  214. value_string="${18}" # friendly value (with units)
  215. # shellcheck disable=SC2034
  216. # variable is unused, but https://github.com/netdata/netdata/pull/5164#discussion_r255572947
  217. old_value_string="${19}" # friendly old value (with units), previously named "old_value_string"
  218. calc_expression="${20}" # contains the expression that was evaluated to trigger the alarm
  219. calc_param_values="${21}" # the values of the parameters in the expression, at the time of the evaluation
  220. total_warnings="${22}" # Total number of alarms in WARNING state
  221. total_critical="${23}" # Total number of alarms in CRITICAL state
  222. total_warn_alarms="${24}" # List of alarms in warning state
  223. total_crit_alarms="${25}" # List of alarms in critical state
  224. classification="${26}" # The class field from .conf files
  225. edit_command_line="${27}" # The command to edit the alarm, with the line number
  226. child_machine_guid="${28}" # the machine_guid of the child
  227. transition_id="${29}" # the transition_id of the alert
  228. summary="${30}" # the summary text field of the alert
  229. fi
  230. # -----------------------------------------------------------------------------
  231. # find a suitable hostname to use, if netdata did not supply a hostname
  232. if [ -z "${args_host}" ]; then
  233. this_host=$(hostname -s 2>/dev/null)
  234. host="${this_host}"
  235. args_host="${this_host}"
  236. else
  237. host="${args_host}"
  238. fi
  239. # -----------------------------------------------------------------------------
  240. # screen statuses we don't need to send a notification
  241. # don't do anything if this is not WARNING, CRITICAL or CLEAR
  242. if [ "${status}" != "WARNING" ] && [ "${status}" != "CRITICAL" ] && [ "${status}" != "CLEAR" ]; then
  243. info "not sending notification for ${status} of '${host}.${chart}.${name}'"
  244. exit 1
  245. fi
  246. # don't do anything if this is CLEAR, but it was not WARNING or CRITICAL
  247. if [ "${clear_alarm_always}" != "YES" ] && [ "${old_status}" != "WARNING" ] && [ "${old_status}" != "CRITICAL" ] && [ "${status}" = "CLEAR" ]; then
  248. info "not sending notification for ${status} of '${host}.${chart}.${name}' (last status was ${old_status})"
  249. exit 1
  250. fi
  251. # -----------------------------------------------------------------------------
  252. # load configuration
  253. # By default fetch images from the global public registry.
  254. # This is required by default, since all notification methods need to download
  255. # images via the Internet, and private registries might not be reachable.
  256. # This can be overwritten at the configuration file.
  257. images_base_url="https://registry.my-netdata.io"
  258. # curl options to use
  259. curl_options=""
  260. # hostname handling
  261. use_fqdn="NO"
  262. # needed commands
  263. # if empty they will be searched in the system path
  264. curl=
  265. sendmail=
  266. # enable / disable features
  267. for method_name in ${method_names^^}; do
  268. declare SEND_${method_name}="YES"
  269. declare DEFAULT_RECIPIENT_${method_name}
  270. done
  271. for method_name in ${method_names}; do
  272. declare -A role_recipients_${method_name}
  273. done
  274. # slack configs
  275. SLACK_WEBHOOK_URL=
  276. # Microsoft Teams configs
  277. MSTEAMS_WEBHOOK_URL=
  278. # Legacy Microsoft Teams configs for backwards compatibility:
  279. declare -A role_recipients_msteam
  280. # rocketchat configs
  281. ROCKETCHAT_WEBHOOK_URL=
  282. # alerta configs
  283. ALERTA_WEBHOOK_URL=
  284. ALERTA_API_KEY=
  285. # flock configs
  286. FLOCK_WEBHOOK_URL=
  287. # discord configs
  288. DISCORD_WEBHOOK_URL=
  289. # pushover configs
  290. PUSHOVER_APP_TOKEN=
  291. # pushbullet configs
  292. PUSHBULLET_ACCESS_TOKEN=
  293. PUSHBULLET_SOURCE_DEVICE=
  294. # twilio configs
  295. TWILIO_ACCOUNT_SID=
  296. TWILIO_ACCOUNT_TOKEN=
  297. TWILIO_NUMBER=
  298. # hipchat configs
  299. HIPCHAT_SERVER=
  300. HIPCHAT_AUTH_TOKEN=
  301. # messagebird configs
  302. MESSAGEBIRD_ACCESS_KEY=
  303. MESSAGEBIRD_NUMBER=
  304. # kavenegar configs
  305. KAVENEGAR_API_KEY=
  306. KAVENEGAR_SENDER=
  307. # telegram configs
  308. TELEGRAM_BOT_TOKEN=
  309. # kafka configs
  310. SEND_KAFKA="YES"
  311. KAFKA_URL=
  312. KAFKA_SENDER_IP=
  313. # pagerduty.com configs
  314. PD_SERVICE_KEY=
  315. USE_PD_VERSION=
  316. # fleep.io configs
  317. FLEEP_SENDER="${host}"
  318. # Amazon SNS configs
  319. AWSSNS_MESSAGE_FORMAT=
  320. # Matrix configs
  321. MATRIX_HOMESERVER=
  322. MATRIX_ACCESSTOKEN=
  323. # syslog configs
  324. SYSLOG_FACILITY=
  325. # email configs
  326. EMAIL_SENDER=
  327. EMAIL_CHARSET=$(locale charmap 2>/dev/null)
  328. EMAIL_THREADING=
  329. EMAIL_PLAINTEXT_ONLY=
  330. # irc configs
  331. IRC_NICKNAME=
  332. IRC_REALNAME=
  333. IRC_NETWORK=
  334. IRC_PORT=6667
  335. # dynatrace configs
  336. DYNATRACE_SPACE=
  337. DYNATRACE_SERVER=
  338. DYNATRACE_TOKEN=
  339. DYNATRACE_TAG_VALUE=
  340. DYNATRACE_ANNOTATION_TYPE=
  341. DYNATRACE_EVENT=
  342. SEND_DYNATRACE=
  343. # gotify configs
  344. GOTIFY_APP_URL=
  345. GOTIFY_APP_TOKEN=
  346. # opsgenie configs
  347. OPSGENIE_API_KEY=
  348. # load the stock and user configuration files
  349. # these will overwrite the variables above
  350. if [ ${unittest} ]; then
  351. if source "${cfgfile}"; then
  352. error "Failed to load requested config file."
  353. exit 1
  354. fi
  355. else
  356. for CONFIG in "${NETDATA_STOCK_CONFIG_DIR}/health_alarm_notify.conf" "${NETDATA_USER_CONFIG_DIR}/health_alarm_notify.conf"; do
  357. if [ -f "${CONFIG}" ]; then
  358. debug "Loading config file '${CONFIG}'..."
  359. source "${CONFIG}" || error "Failed to load config file '${CONFIG}'."
  360. else
  361. warning "Cannot find file '${CONFIG}'."
  362. fi
  363. done
  364. fi
  365. if [[ ! $curl_options =~ .*\--connect-timeout ]]; then
  366. curl_options+=" --connect-timeout 5"
  367. fi
  368. OPSGENIE_API_URL=${OPSGENIE_API_URL:-"https://api.opsgenie.com"}
  369. # If we didn't autodetect the character set for e-mail and it wasn't
  370. # set by the user, we need to set it to a reasonable default. UTF-8
  371. # should be correct for almost all modern UNIX systems.
  372. if [ -z ${EMAIL_CHARSET} ]; then
  373. EMAIL_CHARSET="UTF-8"
  374. fi
  375. # If we've been asked to use FQDN's for the URL's in the alarm, do so,
  376. # unless we're sending an alarm for a child system which we can't get the
  377. # FQDN of easily.
  378. if [ "${use_fqdn}" = "YES" ] && [ "${host}" = "$(hostname -s 2>/dev/null)" ]; then
  379. host="$(hostname -f 2>/dev/null)"
  380. fi
  381. # -----------------------------------------------------------------------------
  382. # migrate old Microsoft Teams configuration keys after loading configuration
  383. msteams_migration() {
  384. SEND_MSTEAMS=${SEND_MSTEAM:-$SEND_MSTEAMS}
  385. unset -v SEND_MSTEAM
  386. DEFAULT_RECIPIENT_MSTEAMS=${DEFAULT_RECIPIENT_MSTEAM:-$DEFAULT_RECIPIENT_MSTEAMS}
  387. MSTEAMS_WEBHOOK_URL=${MSTEAM_WEBHOOK_URL:-$MSTEAMS_WEBHOOK_URL}
  388. MSTEAMS_ICON_DEFAULT=${MSTEAM_ICON_DEFAULT:-$MSTEAMS_ICON_DEFAULT}
  389. MSTEAMS_ICON_CLEAR=${MSTEAM_ICON_CLEAR:-$MSTEAMS_ICON_CLEAR}
  390. MSTEAMS_ICON_WARNING=${MSTEAM_ICON_WARNING:-$MSTEAMS_ICON_WARNING}
  391. MSTEAMS_ICON_CRITICAL=${MSTEAM_ICON_CRITICAL:-$MSTEAMS_ICON_CRITICAL}
  392. MSTEAMS_COLOR_DEFAULT=${MSTEAM_COLOR_DEFAULT:-$MSTEAMS_COLOR_DEFAULT}
  393. MSTEAMS_COLOR_CLEAR=${MSTEAM_COLOR_CLEAR:-$MSTEAMS_COLOR_CLEAR}
  394. MSTEAMS_COLOR_WARNING=${MSTEAM_COLOR_WARNING:-$MSTEAMS_COLOR_WARNING}
  395. MSTEAMS_COLOR_CRITICAL=${MSTEAM_COLOR_CRITICAL:-$MSTEAMS_COLOR_CRITICAL}
  396. # migrate role specific recipients:
  397. for key in "${!role_recipients_msteam[@]}"; do
  398. # Disable check, if role_recipients_msteams is ever used:
  399. # The role_recipients_$method are created and used programmatically
  400. # by iterating over $methods. shellcheck therefore doesn't realize
  401. # that role_recipients_msteams is actually used in the block
  402. # "find the recipients' addresses per method".
  403. # shellcheck disable=SC2034
  404. role_recipients_msteams["$key"]="${role_recipients_msteam["$key"]}"
  405. done
  406. }
  407. msteams_migration
  408. # -----------------------------------------------------------------------------
  409. # filter a recipient based on alarm event severity
  410. filter_recipient_by_criticality() {
  411. local method="${1}" recipient_arg="${2}"
  412. local tracking_dir tracking_file modifier modifiers recipient="${recipient_arg/|*/}"
  413. local mod_critical=0 mod_noclear=0 mod_nowarn=0
  414. # no severity filtering for this person
  415. [ "${recipient}" = "${recipient_arg}" ] && return 0
  416. # find out which modifiers are set
  417. modifiers="${recipient_arg#*|}"
  418. modifiers="${modifiers//|/ }" # replace pipes with spaces
  419. modifiers="${modifiers,,}" # lowercase
  420. for modifier in ${modifiers}; do
  421. case "${modifier}" in
  422. critical) mod_critical=1 ;;
  423. noclear) mod_noclear=1 ;;
  424. nowarn) mod_nowarn=1 ;;
  425. *)
  426. error "SEVERITY FILTERING for ${recipient_arg} VIA ${method}: invalid modifier '${modifier}'."
  427. # invalid modifier, always send notification
  428. return 0
  429. ;;
  430. esac
  431. done
  432. # set status tracking directory/file var
  433. tracking_dir="${NETDATA_CACHE_DIR}/alarm-notify/${method}/${recipient}"
  434. tracking_file="${tracking_dir}/${alarm_id}"
  435. # create the status tracking directory for this user if "critical" modifier is set
  436. [ "${mod_critical}" == "1" ] && [ ! -d "${tracking_dir}" ] && mkdir -p "${tracking_dir}"
  437. case "${status}" in
  438. CRITICAL)
  439. # "critical" modifier set, create tracking file for future status changes
  440. if [ "${mod_critical}" == "1" ]; then
  441. touch "${tracking_file}"
  442. debug "SEVERITY FILTERING for ${recipient_arg} VIA ${method}: ALLOW: the alarm is CRITICAL (will now receive next status change)"
  443. return 0
  444. fi
  445. # always send CRITICAL notification
  446. debug "SEVERITY FILTERING for ${recipient_arg} VIA ${method}: ALLOW: the alarm is CRITICAL"
  447. return 0
  448. ;;
  449. WARNING)
  450. # "nowarn" modifier set, block notification
  451. if [ "${mod_nowarn}" == "1" ]; then
  452. debug "SEVERITY FILTERING for ${recipient_arg} VIA ${method}: BLOCK: recipient should not receive this notification (nowarn modifier set)"
  453. return 1
  454. fi
  455. # "critical" modifier not set, send notification
  456. if [ "${mod_critical}" == "0" ]; then
  457. debug "SEVERITY FILTERING for ${recipient_arg} VIA ${method}: ALLOW: the alarm is WARNING"
  458. return 0
  459. fi
  460. # "critical" modifier set, send notification if tracking file exists
  461. if [ "${mod_critical}" == "1" ] && [ -f "${tracking_file}" ]; then
  462. debug "SEVERITY FILTERING for ${recipient_arg} VIA ${method}: ALLOW: recipient has been notified for this alarm in the past (will still receive next status change)"
  463. return 0
  464. fi
  465. ;;
  466. CLEAR)
  467. # remove tracking file
  468. [ -f "${tracking_file}" ] && rm "${tracking_file}"
  469. # "noclear" modifier set, block notification
  470. if [ "${mod_noclear}" == "1" ]; then
  471. debug "SEVERITY FILTERING for ${recipient_arg} VIA ${method}: BLOCK: recipient should not receive this notification (noclear modifier set)"
  472. return 1
  473. fi
  474. # "critical" modifier not set, send notification
  475. if [ "${mod_critical}" == "0" ]; then
  476. debug "SEVERITY FILTERING for ${recipient_arg} VIA ${method}: ALLOW: the alarm is CLEAR"
  477. return 0
  478. fi
  479. # "critical" modifier set, send notification if tracking file exists
  480. if [ "${mod_critical}" == "1" ] && [ -f "${tracking_file}" ]; then
  481. debug "SEVERITY FILTERING for ${recipient_arg} VIA ${method}: ALLOW: recipient has been notified for this alarm in the past (no status change will be sent from now)"
  482. return 0
  483. fi
  484. ;;
  485. *)
  486. # "critical" modifier set, send notification if tracking file exists
  487. if [ "${mod_critical}" == "1" ] && [ -f "${tracking_file}" ]; then
  488. debug "SEVERITY FILTERING for ${recipient_arg} VIA ${method}: ALLOW: recipient has been notified for this alarm in the past (will still receive next status change)"
  489. return 0
  490. fi
  491. ;;
  492. esac
  493. debug "SEVERITY FILTERING for ${recipient_arg} VIA ${method}: BLOCK: recipient should not receive this notification"
  494. return 1
  495. }
  496. # -----------------------------------------------------------------------------
  497. # verify the delivery methods supported
  498. # check slack
  499. [ -z "${SLACK_WEBHOOK_URL}" ] && SEND_SLACK="NO"
  500. # check rocketchat
  501. [ -z "${ROCKETCHAT_WEBHOOK_URL}" ] && SEND_ROCKETCHAT="NO"
  502. # check alerta
  503. [ -z "${ALERTA_WEBHOOK_URL}" ] && SEND_ALERTA="NO"
  504. # check flock
  505. [ -z "${FLOCK_WEBHOOK_URL}" ] && SEND_FLOCK="NO"
  506. # check discord
  507. [ -z "${DISCORD_WEBHOOK_URL}" ] && SEND_DISCORD="NO"
  508. # check pushover
  509. [ -z "${PUSHOVER_APP_TOKEN}" ] && SEND_PUSHOVER="NO"
  510. # check pushbullet
  511. [ -z "${PUSHBULLET_ACCESS_TOKEN}" ] && SEND_PUSHBULLET="NO"
  512. # check twilio
  513. { [ -z "${TWILIO_ACCOUNT_TOKEN}" ] || [ -z "${TWILIO_ACCOUNT_SID}" ] || [ -z "${TWILIO_NUMBER}" ]; } && SEND_TWILIO="NO"
  514. # check hipchat
  515. [ -z "${HIPCHAT_AUTH_TOKEN}" ] && SEND_HIPCHAT="NO"
  516. # check messagebird
  517. { [ -z "${MESSAGEBIRD_ACCESS_KEY}" ] || [ -z "${MESSAGEBIRD_NUMBER}" ]; } && SEND_MESSAGEBIRD="NO"
  518. # check kavenegar
  519. { [ -z "${KAVENEGAR_API_KEY}" ] || [ -z "${KAVENEGAR_SENDER}" ]; } && SEND_KAVENEGAR="NO"
  520. # check telegram
  521. [ -z "${TELEGRAM_BOT_TOKEN}" ] && SEND_TELEGRAM="NO"
  522. # check kafka
  523. { [ -z "${KAFKA_URL}" ] || [ -z "${KAFKA_SENDER_IP}" ]; } && SEND_KAFKA="NO"
  524. # check irc
  525. [ -z "${IRC_NETWORK}" ] && SEND_IRC="NO"
  526. # check fleep
  527. #shellcheck disable=SC2153
  528. { [ -z "${FLEEP_SERVER}" ] || [ -z "${FLEEP_SENDER}" ]; } && SEND_FLEEP="NO"
  529. # check dynatrace
  530. { [ -z "${DYNATRACE_SPACE}" ] ||
  531. [ -z "${DYNATRACE_SERVER}" ] ||
  532. [ -z "${DYNATRACE_TOKEN}" ] ||
  533. [ -z "${DYNATRACE_TAG_VALUE}" ] ||
  534. [ -z "${DYNATRACE_EVENT}" ]; } && SEND_DYNATRACE="NO"
  535. # check opsgenie
  536. [ -z "${OPSGENIE_API_KEY}" ] && SEND_OPSGENIE="NO"
  537. # check matrix
  538. { [ -z "${MATRIX_HOMESERVER}" ] || [ -z "${MATRIX_ACCESSTOKEN}" ]; } && SEND_MATRIX="NO"
  539. # check gotify
  540. { [ -z "${GOTIFY_APP_TOKEN}" ] || [ -z "${GOTIFY_APP_URL}" ]; } && SEND_GOTIFY="NO"
  541. # check ntfy
  542. [ -z "${DEFAULT_RECIPIENT_NTFY}" ] && SEND_NTFY="NO"
  543. # check msteams
  544. [ -z "${MSTEAMS_WEBHOOK_URL}" ] && SEND_MSTEAMS="NO"
  545. # check pd
  546. [ -z "${DEFAULT_RECIPIENT_PD}" ] && SEND_PD="NO"
  547. # check prowl
  548. [ -z "${DEFAULT_RECIPIENT_PROWL}" ] && SEND_PROWL="NO"
  549. # check custom
  550. [ -z "${DEFAULT_RECIPIENT_CUSTOM}" ] && SEND_CUSTOM="NO"
  551. if [ "${SEND_PUSHOVER}" = "YES" ] ||
  552. [ "${SEND_SLACK}" = "YES" ] ||
  553. [ "${SEND_ROCKETCHAT}" = "YES" ] ||
  554. [ "${SEND_ALERTA}" = "YES" ] ||
  555. [ "${SEND_PD}" = "YES" ] ||
  556. [ "${SEND_FLOCK}" = "YES" ] ||
  557. [ "${SEND_DISCORD}" = "YES" ] ||
  558. [ "${SEND_HIPCHAT}" = "YES" ] ||
  559. [ "${SEND_TWILIO}" = "YES" ] ||
  560. [ "${SEND_MESSAGEBIRD}" = "YES" ] ||
  561. [ "${SEND_KAVENEGAR}" = "YES" ] ||
  562. [ "${SEND_TELEGRAM}" = "YES" ] ||
  563. [ "${SEND_PUSHBULLET}" = "YES" ] ||
  564. [ "${SEND_KAFKA}" = "YES" ] ||
  565. [ "${SEND_FLEEP}" = "YES" ] ||
  566. [ "${SEND_PROWL}" = "YES" ] ||
  567. [ "${SEND_MATRIX}" = "YES" ] ||
  568. [ "${SEND_CUSTOM}" = "YES" ] ||
  569. [ "${SEND_MSTEAMS}" = "YES" ] ||
  570. [ "${SEND_DYNATRACE}" = "YES" ] ||
  571. [ "${SEND_OPSGENIE}" = "YES" ] ||
  572. [ "${SEND_GOTIFY}" = "YES" ] ||
  573. [ "${SEND_NTFY}" = "YES" ]; then
  574. # if we need curl, check for the curl command
  575. if [ -z "${curl}" ]; then
  576. curl="$(command -v curl 2>/dev/null)"
  577. fi
  578. if [ -z "${curl}" ]; then
  579. error "Cannot find curl command in the system path. Disabling all curl based notifications."
  580. SEND_PUSHOVER="NO"
  581. SEND_PUSHBULLET="NO"
  582. SEND_TELEGRAM="NO"
  583. SEND_SLACK="NO"
  584. SEND_MSTEAMS="NO"
  585. SEND_ROCKETCHAT="NO"
  586. SEND_ALERTA="NO"
  587. SEND_PD="NO"
  588. SEND_FLOCK="NO"
  589. SEND_DISCORD="NO"
  590. SEND_TWILIO="NO"
  591. SEND_HIPCHAT="NO"
  592. SEND_MESSAGEBIRD="NO"
  593. SEND_KAVENEGAR="NO"
  594. SEND_KAFKA="NO"
  595. SEND_FLEEP="NO"
  596. SEND_PROWL="NO"
  597. SEND_MATRIX="NO"
  598. SEND_CUSTOM="NO"
  599. SEND_DYNATRACE="NO"
  600. SEND_OPSGENIE="NO"
  601. SEND_GOTIFY="NO"
  602. SEND_NTFY="NO"
  603. fi
  604. fi
  605. if [ "${SEND_SMS}" = "YES" ]; then
  606. if [ -z "${sendsms}" ]; then
  607. sendsms="$(command -v sendsms 2>/dev/null)"
  608. fi
  609. if [ -z "${sendsms}" ]; then
  610. SEND_SMS="NO"
  611. fi
  612. fi
  613. # if we need sendmail, check for the sendmail command
  614. if [ "${SEND_EMAIL}" = "YES" ] && [ -z "${sendmail}" ]; then
  615. sendmail="$(command -v sendmail 2>/dev/null)"
  616. if [ -z "${sendmail}" ]; then
  617. debug "Cannot find sendmail command in the system path. Disabling email notifications."
  618. SEND_EMAIL="NO"
  619. fi
  620. fi
  621. # if we need logger, check for the logger command
  622. if [ "${SEND_SYSLOG}" = "YES" ] && [ -z "${logger}" ]; then
  623. logger="$(command -v logger 2>/dev/null)"
  624. if [ -z "${logger}" ]; then
  625. debug "Cannot find logger command in the system path. Disabling syslog notifications."
  626. SEND_SYSLOG="NO"
  627. fi
  628. fi
  629. # if we need aws, check for the aws command
  630. if [ "${SEND_AWSSNS}" = "YES" ] && [ -z "${aws}" ]; then
  631. aws="$(command -v aws 2>/dev/null)"
  632. if [ -z "${aws}" ]; then
  633. debug "Cannot find aws command in the system path. Disabling Amazon SNS notifications."
  634. SEND_AWSSNS="NO"
  635. fi
  636. fi
  637. # if we need nc, check for the nc command
  638. if [ "${SEND_IRC}" = "YES" ] && [ -z "${nc}" ]; then
  639. nc="$(command -v nc 2>/dev/null)"
  640. if [ -z "${nc}" ]; then
  641. debug "Cannot find nc command in the system path. Disabling IRC notifications."
  642. SEND_IRC="NO"
  643. fi
  644. fi
  645. if [ ${dump_methods} ]; then
  646. for name in "${!SEND_@}"; do
  647. if [ "${!name}" = "YES" ]; then
  648. echo "$name"
  649. fi
  650. done
  651. exit
  652. fi
  653. # -----------------------------------------------------------------------------
  654. # find the recipients' addresses per method
  655. # netdata may call us with multiple roles, and roles may have multiple but
  656. # overlapping recipients - so, here we find the unique recipients.
  657. for method_name in ${method_names}; do
  658. send_var="SEND_${method_name^^}"
  659. if [ "${!send_var}" = "NO" ]; then
  660. continue
  661. fi
  662. declare -A arr_var=()
  663. for x in ${roles//,/ }; do
  664. # the roles 'silent' and 'disabled' mean:
  665. # don't send a notification for this role
  666. if [ "${x}" = "silent" ] || [ "${x}" = "disabled" ]; then
  667. continue
  668. fi
  669. role_recipients="role_recipients_${method_name}[$x]"
  670. default_recipient_var="DEFAULT_RECIPIENT_${method_name^^}"
  671. a="${!role_recipients}"
  672. [ -z "${a}" ] && a="${!default_recipient_var}"
  673. for r in ${a//,/ }; do
  674. [ "${r}" != "disabled" ] && filter_recipient_by_criticality ${method_name} "${r}" && arr_var[${r/|*/}]="1"
  675. done
  676. done
  677. # build the list of recipients
  678. to_var="to_${method_name}"
  679. declare to_${method_name}="${!arr_var[*]}"
  680. [ -z "${!to_var}" ] && declare ${send_var}="NO"
  681. done
  682. # -----------------------------------------------------------------------------
  683. # handle fixup of the email recipient list.
  684. fix_to_email() {
  685. to_email=
  686. while [ -n "${1}" ]; do
  687. [ -n "${to_email}" ] && to_email="${to_email}, "
  688. to_email="${to_email}${1}"
  689. shift 1
  690. done
  691. }
  692. # ${to_email} without quotes here
  693. fix_to_email ${to_email}
  694. # -----------------------------------------------------------------------------
  695. # handle output if we're running in unit test mode
  696. if [ ${unittest} ]; then
  697. for method_name in ${method_names}; do
  698. to_var="to_${method_name}"
  699. echo "results: ${method_name}: ${!to_var}"
  700. done
  701. exit 0
  702. fi
  703. # -----------------------------------------------------------------------------
  704. # check that we have at least a method enabled
  705. proceed=0
  706. for method in "${SEND_EMAIL}" \
  707. "${SEND_PUSHOVER}" \
  708. "${SEND_TELEGRAM}" \
  709. "${SEND_SLACK}" \
  710. "${SEND_ROCKETCHAT}" \
  711. "${SEND_ALERTA}" \
  712. "${SEND_FLOCK}" \
  713. "${SEND_DISCORD}" \
  714. "${SEND_TWILIO}" \
  715. "${SEND_HIPCHAT}" \
  716. "${SEND_MESSAGEBIRD}" \
  717. "${SEND_KAVENEGAR}" \
  718. "${SEND_PUSHBULLET}" \
  719. "${SEND_KAFKA}" \
  720. "${SEND_PD}" \
  721. "${SEND_FLEEP}" \
  722. "${SEND_PROWL}" \
  723. "${SEND_MATRIX}" \
  724. "${SEND_CUSTOM}" \
  725. "${SEND_IRC}" \
  726. "${SEND_AWSSNS}" \
  727. "${SEND_SYSLOG}" \
  728. "${SEND_SMS}" \
  729. "${SEND_MSTEAMS}" \
  730. "${SEND_DYNATRACE}" \
  731. "${SEND_OPSGENIE}" \
  732. "${SEND_GOTIFY}" \
  733. "${SEND_NTFY}" ; do
  734. if [ "${method}" == "YES" ]; then
  735. proceed=1
  736. break
  737. fi
  738. done
  739. if [ "$proceed" -eq 0 ]; then
  740. fatal "All notification methods are disabled. Not sending notification for host '${host}', chart '${chart}' to '${roles}' for '${name}' = '${value}' for status '${status}'."
  741. fi
  742. # -----------------------------------------------------------------------------
  743. # get the date the alarm happened
  744. date=$(date --date=@${when} "${date_format}" 2>/dev/null)
  745. [ -z "${date}" ] && date=$(date "${date_format}" 2>/dev/null)
  746. [ -z "${date}" ] && date=$(date --date=@${when} 2>/dev/null)
  747. [ -z "${date}" ] && date=$(date 2>/dev/null)
  748. # -----------------------------------------------------------------------------
  749. # get the date in utc the alarm happened
  750. date_utc=$(date --date=@${when} "${date_format}" -u 2>/dev/null)
  751. [ -z "${date_utc}" ] && date_utc=$(date -u "${date_format}" 2>/dev/null)
  752. [ -z "${date_utc}" ] && date_utc=$(date -u --date=@${when} 2>/dev/null)
  753. [ -z "${date_utc}" ] && date_utc=$(date -u 2>/dev/null)
  754. # ----------------------------------------------------------------------------
  755. # prepare some extra headers if we've been asked to thread e-mails
  756. if [ "${SEND_EMAIL}" == "YES" ] && [ "${EMAIL_THREADING}" != "NO" ]; then
  757. email_thread_headers="In-Reply-To: <${chart}-${name}@${host}>\\r\\nReferences: <${chart}-${name}@${host}>"
  758. else
  759. email_thread_headers=
  760. fi
  761. # -----------------------------------------------------------------------------
  762. # function to URL encode a string
  763. urlencode() {
  764. local string="${1}" strlen encoded pos c o
  765. strlen=${#string}
  766. for ((pos = 0; pos < strlen; pos++)); do
  767. c=${string:pos:1}
  768. case "${c}" in
  769. [-_.~a-zA-Z0-9])
  770. o="${c}"
  771. ;;
  772. *)
  773. printf -v o '%%%02x' "'${c}"
  774. ;;
  775. esac
  776. encoded+="${o}"
  777. done
  778. REPLY="${encoded}"
  779. echo "${REPLY}"
  780. }
  781. # -----------------------------------------------------------------------------
  782. # function to convert a duration in seconds, to a human readable duration
  783. # using DAYS, MINUTES, SECONDS
  784. duration4human() {
  785. local s="${1}" d=0 h=0 m=0 ds="day" hs="hour" ms="minute" ss="second" ret
  786. d=$((s / 86400))
  787. s=$((s - (d * 86400)))
  788. h=$((s / 3600))
  789. s=$((s - (h * 3600)))
  790. m=$((s / 60))
  791. s=$((s - (m * 60)))
  792. if [ ${d} -gt 0 ]; then
  793. [ ${m} -ge 30 ] && h=$((h + 1))
  794. [ ${d} -gt 1 ] && ds="days"
  795. [ ${h} -gt 1 ] && hs="hours"
  796. if [ ${h} -gt 0 ]; then
  797. ret="${d} ${ds} and ${h} ${hs}"
  798. else
  799. ret="${d} ${ds}"
  800. fi
  801. elif [ ${h} -gt 0 ]; then
  802. [ ${s} -ge 30 ] && m=$((m + 1))
  803. [ ${h} -gt 1 ] && hs="hours"
  804. [ ${m} -gt 1 ] && ms="minutes"
  805. if [ ${m} -gt 0 ]; then
  806. ret="${h} ${hs} and ${m} ${ms}"
  807. else
  808. ret="${h} ${hs}"
  809. fi
  810. elif [ ${m} -gt 0 ]; then
  811. [ ${m} -gt 1 ] && ms="minutes"
  812. [ ${s} -gt 1 ] && ss="seconds"
  813. if [ ${s} -gt 0 ]; then
  814. ret="${m} ${ms} and ${s} ${ss}"
  815. else
  816. ret="${m} ${ms}"
  817. fi
  818. else
  819. [ ${s} -gt 1 ] && ss="seconds"
  820. ret="${s} ${ss}"
  821. fi
  822. REPLY="${ret}"
  823. echo "${REPLY}"
  824. }
  825. # -----------------------------------------------------------------------------
  826. # email sender
  827. send_email() {
  828. local ret opts=() sender_email="${EMAIL_SENDER}" sender_name=
  829. if [ "${SEND_EMAIL}" = "YES" ]; then
  830. if [ -n "${EMAIL_SENDER}" ]; then
  831. if [[ ${EMAIL_SENDER} =~ ^\".*\"\ \<.*\>$ ]]; then
  832. # the name includes double quotes
  833. sender_email="$(echo "${EMAIL_SENDER}" | cut -d '<' -f 2 | cut -d '>' -f 1)"
  834. sender_name="$(echo "${EMAIL_SENDER}" | cut -d '"' -f 2)"
  835. elif [[ ${EMAIL_SENDER} =~ ^\'.*\'\ \<.*\>$ ]]; then
  836. # the name includes single quotes
  837. sender_email="$(echo "${EMAIL_SENDER}" | cut -d '<' -f 2 | cut -d '>' -f 1)"
  838. sender_name="$(echo "${EMAIL_SENDER}" | cut -d "'" -f 2)"
  839. elif [[ ${EMAIL_SENDER} =~ ^.*\ \<.*\>$ ]]; then
  840. # the name does not have any quotes
  841. sender_email="$(echo "${EMAIL_SENDER}" | cut -d '<' -f 2 | cut -d '>' -f 1)"
  842. sender_name="$(echo "${EMAIL_SENDER}" | cut -d '<' -f 1)"
  843. fi
  844. fi
  845. [ -n "${sender_email}" ] && opts+=(-f "${sender_email}")
  846. [ -n "${sender_name}" ] && ${sendmail} -F 2>&1 | head -1 | grep -qv "sendmail: unrecognized option: F" && opts+=(-F "${sender_name}")
  847. if [ "${debug}" = "1" ]; then
  848. echo >&2 "--- BEGIN sendmail command ---"
  849. printf >&2 "%q " "${sendmail}" -t "${opts[@]}"
  850. echo >&2
  851. echo >&2 "--- END sendmail command ---"
  852. fi
  853. local cmd_output
  854. cmd_output=$("${sendmail}" -t "${opts[@]}" 2>&1)
  855. ret=$?
  856. if [ ${ret} -eq 0 ]; then
  857. info "sent email notification for: ${host} ${chart}.${name} is ${status} to '${to_email}'"
  858. return 0
  859. else
  860. error "failed to send email notification for: ${host} ${chart}.${name} is ${status} to '${to_email}' with error code ${ret} (${cmd_output})."
  861. return 1
  862. fi
  863. fi
  864. return 1
  865. }
  866. # -----------------------------------------------------------------------------
  867. # pushover sender
  868. send_pushover() {
  869. local apptoken="${1}" usertokens="${2}" when="${3}" url="${4}" status="${5}" title="${6}" message="${7}" httpcode sent=0 user priority
  870. if [ "${SEND_PUSHOVER}" = "YES" ] && [ -n "${apptoken}" ] && [ -n "${usertokens}" ] && [ -n "${title}" ] && [ -n "${message}" ]; then
  871. # https://pushover.net/api
  872. priority=-2
  873. case "${status}" in
  874. CLEAR) priority=-1 ;; # low priority: no sound or vibration
  875. WARNING) priority=0 ;; # normal priority: respect quiet hours
  876. CRITICAL) priority=1 ;; # high priority: bypass quiet hours
  877. *) priority=-2 ;; # lowest priority: no notification at all
  878. esac
  879. for user in ${usertokens}; do
  880. httpcode=$(docurl \
  881. --form-string "token=${apptoken}" \
  882. --form-string "user=${user}" \
  883. --form-string "html=1" \
  884. --form-string "title=${title}" \
  885. --form-string "message=${message}" \
  886. --form-string "timestamp=${when}" \
  887. --form-string "url=${url}" \
  888. --form-string "url_title=Open netdata dashboard to view the alarm" \
  889. --form-string "priority=${priority}" \
  890. https://api.pushover.net/1/messages.json)
  891. if [ "${httpcode}" = "200" ]; then
  892. info "sent pushover notification for: ${host} ${chart}.${name} is ${status} to '${user}'"
  893. sent=$((sent + 1))
  894. else
  895. error "failed to send pushover notification for: ${host} ${chart}.${name} is ${status} to '${user}' with HTTP response status code ${httpcode}."
  896. fi
  897. done
  898. [ ${sent} -gt 0 ] && return 0
  899. fi
  900. return 1
  901. }
  902. # -----------------------------------------------------------------------------
  903. # pushbullet sender
  904. send_pushbullet() {
  905. local userapikey="${1}" source_device="${2}" recipients="${3}" url="${4}" title="${5}" message="${6}" httpcode sent=0 userOrChannelTag
  906. if [ "${SEND_PUSHBULLET}" = "YES" ] && [ -n "${userapikey}" ] && [ -n "${recipients}" ] && [ -n "${message}" ] && [ -n "${title}" ]; then
  907. # https://docs.pushbullet.com/#create-push
  908. # Accept specification of user(s) (PushBullet account email address) and/or channel tag(s), separated by spaces.
  909. # If recipient begins with a "#" then send to channel tag, otherwise send to email recipient.
  910. for userOrChannelTag in ${recipients}; do
  911. if [ "${userOrChannelTag::1}" = "#" ]; then
  912. userOrChannelTag_type="channel_tag"
  913. userOrChannelTag="${userOrChannelTag:1}" # Remove hash from start of channel tag (required by pushbullet API)
  914. else
  915. userOrChannelTag_type="email"
  916. fi
  917. httpcode=$(docurl \
  918. --header 'Access-Token: '${userapikey}'' \
  919. --header 'Content-Type: application/json' \
  920. --data-binary @<(
  921. cat <<EOF
  922. {"title": "${title}",
  923. "type": "link",
  924. "${userOrChannelTag_type}": "${userOrChannelTag}",
  925. "body": "$(echo -n ${message})",
  926. "url": "${url}",
  927. "source_device_iden": "${source_device}"}
  928. EOF
  929. ) "https://api.pushbullet.com/v2/pushes" -X POST)
  930. if [ "${httpcode}" = "200" ]; then
  931. info "sent pushbullet notification for: ${host} ${chart}.${name} is ${status} to '${userOrChannelTag}'"
  932. sent=$((sent + 1))
  933. else
  934. error "failed to send pushbullet notification for: ${host} ${chart}.${name} is ${status} to '${userOrChannelTag}' with HTTP response status code ${httpcode}."
  935. fi
  936. done
  937. [ ${sent} -gt 0 ] && return 0
  938. fi
  939. return 1
  940. }
  941. # -----------------------------------------------------------------------------
  942. # kafka sender
  943. send_kafka() {
  944. local httpcode sent=0
  945. if [ "${SEND_KAFKA}" = "YES" ]; then
  946. httpcode=$(docurl -X POST \
  947. --data "{host_ip:\"${KAFKA_SENDER_IP}\",when:${when},name:\"${name}\",chart:\"${chart}\",status:\"${status}\",old_status:\"${old_status}\",value:${value},old_value:${old_value},duration:${duration},non_clear_duration:${non_clear_duration},units:\"${units}\",info:\"${info}\"}" \
  948. "${KAFKA_URL}")
  949. if [ "${httpcode}" = "204" ]; then
  950. info "sent kafka data for: ${host} ${chart}.${name} is ${status} and ip '${KAFKA_SENDER_IP}'"
  951. sent=$((sent + 1))
  952. else
  953. error "failed to send kafka data for: ${host} ${chart}.${name} is ${status} and ip '${KAFKA_SENDER_IP}' with HTTP response status code ${httpcode}."
  954. fi
  955. [ ${sent} -gt 0 ] && return 0
  956. fi
  957. return 1
  958. }
  959. # -----------------------------------------------------------------------------
  960. # pagerduty.com sender
  961. send_pd() {
  962. local recipients="${1}" sent=0 severity current_time payload url response_code
  963. unset t
  964. case ${status} in
  965. CLEAR) t='resolve' ; severity='info' ;;
  966. WARNING) t='trigger' ; severity='warning' ;;
  967. CRITICAL) t='trigger' ; severity='critical' ;;
  968. esac
  969. if [ ${SEND_PD} = "YES" ] && [ -n "${t}" ]; then
  970. if [ "$(uname)" == "Linux" ]; then
  971. current_time=$(date -d @${when} +'%Y-%m-%dT%H:%M:%S.000')
  972. else
  973. current_time=$(date -r ${when} +'%Y-%m-%dT%H:%M:%S.000')
  974. fi
  975. for PD_SERVICE_KEY in ${recipients}; do
  976. d="${status} ${name} = ${value_string} - ${host}"
  977. if [ ${USE_PD_VERSION} = "2" ]; then
  978. payload="$(
  979. cat <<EOF
  980. {
  981. "payload" : {
  982. "summary": "${info:0:1024}",
  983. "source" : "${args_host}",
  984. "severity" : "${severity}",
  985. "timestamp" : "${current_time}",
  986. "class" : "${chart}",
  987. "custom_details": {
  988. "value_w_units": "${value_string}",
  989. "when": "${when}",
  990. "duration" : "${duration}",
  991. "roles": "${roles}",
  992. "alarm_id" : "${alarm_id}",
  993. "name" : "${name}",
  994. "chart" : "${chart}",
  995. "status" : "${status}",
  996. "old_status" : "${old_status}",
  997. "value" : "${value}",
  998. "old_value" : "${old_value}",
  999. "src" : "${src}",
  1000. "non_clear_duration" : "${non_clear_duration}",
  1001. "units" : "${units}",
  1002. "info" : "${info}"
  1003. }
  1004. },
  1005. "routing_key": "${PD_SERVICE_KEY}",
  1006. "event_action": "${t}",
  1007. "dedup_key": "${unique_id}"
  1008. }
  1009. EOF
  1010. )"
  1011. url="https://events.pagerduty.com/v2/enqueue"
  1012. response_code="202"
  1013. else
  1014. payload="$(
  1015. cat <<EOF
  1016. {
  1017. "service_key": "${PD_SERVICE_KEY}",
  1018. "event_type": "${t}",
  1019. "incident_key" : "${alarm_id}",
  1020. "description": "${d}",
  1021. "details": {
  1022. "value_w_units": "${value_string}",
  1023. "when": "${when}",
  1024. "duration" : "${duration}",
  1025. "roles": "${roles}",
  1026. "alarm_id" : "${alarm_id}",
  1027. "name" : "${name}",
  1028. "chart" : "${chart}",
  1029. "status" : "${status}",
  1030. "old_status" : "${old_status}",
  1031. "value" : "${value}",
  1032. "old_value" : "${old_value}",
  1033. "src" : "${src}",
  1034. "non_clear_duration" : "${non_clear_duration}",
  1035. "units" : "${units}",
  1036. "info" : "${info}"
  1037. }
  1038. }
  1039. EOF
  1040. )"
  1041. url="https://events.pagerduty.com/generic/2010-04-15/create_event.json"
  1042. response_code="200"
  1043. fi
  1044. httpcode=$(docurl -X POST --data "${payload}" ${url})
  1045. if [ "${httpcode}" = "${response_code}" ]; then
  1046. info "sent pagerduty notification for: ${host} ${chart}.${name} is ${status}'"
  1047. sent=$((sent + 1))
  1048. else
  1049. error "failed to send pagerduty notification for: ${host} ${chart}.${name} is ${status}, with HTTP response status code ${httpcode}."
  1050. fi
  1051. done
  1052. [ ${sent} -gt 0 ] && return 0
  1053. fi
  1054. return 1
  1055. }
  1056. # -----------------------------------------------------------------------------
  1057. # twilio sender
  1058. send_twilio() {
  1059. local accountsid="${1}" accounttoken="${2}" twilionumber="${3}" recipients="${4}" title="${5}" message="${6}" httpcode sent=0 user
  1060. if [ "${SEND_TWILIO}" = "YES" ] && [ -n "${accountsid}" ] && [ -n "${accounttoken}" ] && [ -n "${twilionumber}" ] && [ -n "${recipients}" ] && [ -n "${message}" ] && [ -n "${title}" ]; then
  1061. #https://www.twilio.com/packages/labs/code/bash/twilio-sms
  1062. for user in ${recipients}; do
  1063. httpcode=$(docurl -X POST \
  1064. --data-urlencode "From=${twilionumber}" \
  1065. --data-urlencode "To=${user}" \
  1066. --data-urlencode "Body=${title} ${message}" \
  1067. -u "${accountsid}:${accounttoken}" \
  1068. "https://api.twilio.com/2010-04-01/Accounts/${accountsid}/Messages.json")
  1069. if [ "${httpcode}" = "201" ]; then
  1070. info "sent Twilio SMS for: ${host} ${chart}.${name} is ${status} to '${user}'"
  1071. sent=$((sent + 1))
  1072. else
  1073. error "failed to send Twilio SMS for: ${host} ${chart}.${name} is ${status} to '${user}' with HTTP response status code ${httpcode}."
  1074. fi
  1075. done
  1076. [ ${sent} -gt 0 ] && return 0
  1077. fi
  1078. return 1
  1079. }
  1080. # -----------------------------------------------------------------------------
  1081. # hipchat sender
  1082. send_hipchat() {
  1083. local authtoken="${1}" recipients="${2}" message="${3}" httpcode sent=0 room color msg_format notify
  1084. # remove <small></small> from the message
  1085. message="${message//<small>/}"
  1086. message="${message//<\/small>/}"
  1087. if [ "${SEND_HIPCHAT}" = "YES" ] && [ -n "${HIPCHAT_SERVER}" ] && [ -n "${authtoken}" ] && [ -n "${recipients}" ] && [ -n "${message}" ]; then
  1088. # Valid values: html, text.
  1089. # Defaults to 'html'.
  1090. msg_format="html"
  1091. # Background color for message. Valid values: yellow, green, red, purple, gray, random. Defaults to 'yellow'.
  1092. case "${status}" in
  1093. WARNING) color="yellow" ;;
  1094. CRITICAL) color="red" ;;
  1095. CLEAR) color="green" ;;
  1096. *) color="gray" ;;
  1097. esac
  1098. # Whether this message should trigger a user notification (change the tab color, play a sound, notify mobile phones, etc).
  1099. # Each recipient's notification preferences are taken into account.
  1100. # Defaults to false.
  1101. notify="true"
  1102. for room in ${recipients}; do
  1103. httpcode=$(docurl -X POST \
  1104. -H "Content-type: application/json" \
  1105. -H "Authorization: Bearer ${authtoken}" \
  1106. -d "{\"color\": \"${color}\", \"from\": \"${host}\", \"message_format\": \"${msg_format}\", \"message\": \"${message}\", \"notify\": \"${notify}\"}" \
  1107. "https://${HIPCHAT_SERVER}/v2/room/${room}/notification")
  1108. if [ "${httpcode}" = "204" ]; then
  1109. info "sent HipChat notification for: ${host} ${chart}.${name} is ${status} to '${room}'"
  1110. sent=$((sent + 1))
  1111. else
  1112. error "failed to send HipChat notification for: ${host} ${chart}.${name} is ${status} to '${room}' with HTTP response status code ${httpcode}."
  1113. fi
  1114. done
  1115. [ ${sent} -gt 0 ] && return 0
  1116. fi
  1117. return 1
  1118. }
  1119. # -----------------------------------------------------------------------------
  1120. # messagebird sender
  1121. send_messagebird() {
  1122. local accesskey="${1}" messagebirdnumber="${2}" recipients="${3}" title="${4}" message="${5}" httpcode sent=0 user
  1123. if [ "${SEND_MESSAGEBIRD}" = "YES" ] && [ -n "${accesskey}" ] && [ -n "${messagebirdnumber}" ] && [ -n "${recipients}" ] && [ -n "${message}" ] && [ -n "${title}" ]; then
  1124. #https://developers.messagebird.com/docs/messaging
  1125. for user in ${recipients}; do
  1126. httpcode=$(docurl -X POST \
  1127. --data-urlencode "originator=${messagebirdnumber}" \
  1128. --data-urlencode "recipients=${user}" \
  1129. --data-urlencode "body=${title} ${message}" \
  1130. --data-urlencode "datacoding=auto" \
  1131. -H "Authorization: AccessKey ${accesskey}" \
  1132. "https://rest.messagebird.com/messages")
  1133. if [ "${httpcode}" = "201" ]; then
  1134. info "sent Messagebird SMS for: ${host} ${chart}.${name} is ${status} to '${user}'"
  1135. sent=$((sent + 1))
  1136. else
  1137. error "failed to send Messagebird SMS for: ${host} ${chart}.${name} is ${status} to '${user}' with HTTP response status code ${httpcode}."
  1138. fi
  1139. done
  1140. [ ${sent} -gt 0 ] && return 0
  1141. fi
  1142. return 1
  1143. }
  1144. # -----------------------------------------------------------------------------
  1145. # kavenegar sender
  1146. send_kavenegar() {
  1147. local API_KEY="${1}" kavenegarsender="${2}" recipients="${3}" title="${4}" message="${5}" httpcode sent=0 user
  1148. if [ "${SEND_KAVENEGAR}" = "YES" ] && [ -n "${API_KEY}" ] && [ -n "${kavenegarsender}" ] && [ -n "${recipients}" ] && [ -n "${message}" ] && [ -n "${title}" ]; then
  1149. # http://api.kavenegar.com/v1/{API-KEY}/sms/send.json
  1150. for user in ${recipients}; do
  1151. httpcode=$(docurl -X POST http://api.kavenegar.com/v1/${API_KEY}/sms/send.json \
  1152. --data-urlencode "sender=${kavenegarsender}" \
  1153. --data-urlencode "receptor=${user}" \
  1154. --data-urlencode "message=${title} ${message}")
  1155. if [ "${httpcode}" = "200" ]; then
  1156. info "sent Kavenegar SMS for: ${host} ${chart}.${name} is ${status} to '${user}'"
  1157. sent=$((sent + 1))
  1158. else
  1159. error "failed to send Kavenegar SMS for: ${host} ${chart}.${name} is ${status} to '${user}' with HTTP response status code ${httpcode}."
  1160. fi
  1161. done
  1162. [ ${sent} -gt 0 ] && return 0
  1163. fi
  1164. return 1
  1165. }
  1166. # -----------------------------------------------------------------------------
  1167. # telegram sender
  1168. send_telegram() {
  1169. local bottoken="${1}" chatids="${2}" message="${3}" httpcode sent=0 chatid emoji disableNotification=""
  1170. if [ "${status}" = "CLEAR" ]; then disableNotification="--data-urlencode disable_notification=true"; fi
  1171. case "${status}" in
  1172. WARNING) emoji="⚠️" ;;
  1173. CRITICAL) emoji="🔴" ;;
  1174. CLEAR) emoji="✅" ;;
  1175. *) emoji="⚪️" ;;
  1176. esac
  1177. if [ "${SEND_TELEGRAM}" = "YES" ] && [ -n "${bottoken}" ] && [ -n "${chatids}" ] && [ -n "${message}" ]; then
  1178. for chatid in ${chatids}; do
  1179. notify_telegram=1
  1180. notify_retries=${TELEGRAM_RETRIES_ON_LIMIT:-0}
  1181. while [ ${notify_telegram} -eq 1 ]; do
  1182. # https://core.telegram.org/bots/api#sendmessage
  1183. httpcode=$(docurl ${disableNotification} \
  1184. --data-urlencode "parse_mode=HTML" \
  1185. --data-urlencode "disable_web_page_preview=true" \
  1186. --data-urlencode "text=${emoji} ${message}" \
  1187. "https://api.telegram.org/bot${bottoken}/sendMessage?chat_id=${chatid}")
  1188. notify_telegram=0
  1189. if [ "${httpcode}" = "200" ]; then
  1190. info "sent telegram notification for: ${host} ${chart}.${name} is ${status} to '${chatid}'"
  1191. sent=$((sent + 1))
  1192. elif [ "${httpcode}" = "401" ]; then
  1193. error "failed to send telegram notification for: ${host} ${chart}.${name} is ${status} to '${chatid}': Wrong bot token."
  1194. elif [ "${httpcode}" = "429" ]; then
  1195. if [ "$notify_retries" -gt 0 ]; then
  1196. error "failed to send telegram notification for: ${host} ${chart}.${name} is ${status} to '${chatid}': rate limit exceeded, retrying after 1s."
  1197. notify_retries=$((notify_retries - 1))
  1198. notify_telegram=1
  1199. sleep 1
  1200. else
  1201. error "failed to send telegram notification for: ${host} ${chart}.${name} is ${status} to '${chatid}': rate limit exceeded."
  1202. fi
  1203. else
  1204. error "failed to send telegram notification for: ${host} ${chart}.${name} is ${status} to '${chatid}' with HTTP response status code ${httpcode}."
  1205. fi
  1206. done
  1207. done
  1208. [ ${sent} -gt 0 ] && return 0
  1209. fi
  1210. return 1
  1211. }
  1212. # -----------------------------------------------------------------------------
  1213. # Microsoft Team sender
  1214. send_msteams() {
  1215. local webhook="${1}" channels="${2}" httpcode sent=0 channel color payload
  1216. [ "${SEND_MSTEAMS}" != "YES" ] && return 1
  1217. case "${status}" in
  1218. WARNING) icon="${MSTEAMS_ICON_WARNING}" && color="${MSTEAMS_COLOR_WARNING}" ;;
  1219. CRITICAL) icon="${MSTEAMS_ICON_CRITICAL}" && color="${MSTEAMS_COLOR_CRITICAL}" ;;
  1220. CLEAR) icon="${MSTEAMS_ICON_CLEAR}" && color="${MSTEAMS_COLOR_CLEAR}" ;;
  1221. *) icon="${MSTEAMS_ICON_DEFAULT}" && color="${MSTEAMS_COLOR_DEFAULT}" ;;
  1222. esac
  1223. for channel in ${channels}; do
  1224. ## More details are available here regarding the payload syntax options : https://docs.microsoft.com/en-us/outlook/actionable-messages/message-card-reference
  1225. ## Online designer : https://adaptivecards.io/designer/
  1226. payload="$(
  1227. cat <<EOF
  1228. {
  1229. "@context": "http://schema.org/extensions",
  1230. "@type": "MessageCard",
  1231. "themeColor": "${color}",
  1232. "title": "$icon Alert ${status} from netdata for ${host}",
  1233. "text": "${host} ${status_message}, ${chart}, *${alarm}*",
  1234. "potentialAction": [
  1235. {
  1236. "@type": "OpenUri",
  1237. "name": "Netdata",
  1238. "targets": [
  1239. { "os": "default", "uri": "${goto_url}" }
  1240. ]
  1241. }
  1242. ]
  1243. }
  1244. EOF
  1245. )"
  1246. # Replacing in the webhook CHANNEL string by the MS Teams channel name from conf file.
  1247. cur_webhook="${webhook//CHANNEL/${channel}}"
  1248. httpcode=$(docurl -H "Content-Type: application/json" -d "${payload}" "${cur_webhook}")
  1249. if [ "${httpcode}" = "200" ]; then
  1250. info "sent Microsoft team notification for: ${host} ${chart}.${name} is ${status} to '${cur_webhook}'"
  1251. sent=$((sent + 1))
  1252. else
  1253. error "failed to send Microsoft team notification for: ${host} ${chart}.${name} is ${status} to '${cur_webhook}', with HTTP response status code ${httpcode}."
  1254. fi
  1255. done
  1256. [ ${sent} -gt 0 ] && return 0
  1257. return 1
  1258. }
  1259. # slack sender
  1260. send_slack() {
  1261. local webhook="${1}" channels="${2}" httpcode sent=0 channel color payload
  1262. [ "${SEND_SLACK}" != "YES" ] && return 1
  1263. case "${status}" in
  1264. WARNING) color="warning" ;;
  1265. CRITICAL) color="danger" ;;
  1266. CLEAR) color="good" ;;
  1267. *) color="#777777" ;;
  1268. esac
  1269. for channel in ${channels}; do
  1270. # Default entry in the recipient is without a hash in front (backwards-compatible). Accept specification of channel or user.
  1271. if [ "${channel::1}" != "#" ] && [ "${channel::1}" != "@" ]; then channel="#$channel"; fi
  1272. # If channel is equal to "#" then do not send the channel attribute at all. Slack also defines channels and users in webhooks.
  1273. if [ "${channel}" = "#" ]; then
  1274. ch=""
  1275. chstr="without specifying a channel"
  1276. else
  1277. ch="\"channel\": \"${channel}\","
  1278. chstr="to '${channel}'"
  1279. fi
  1280. payload="$(
  1281. cat <<EOF
  1282. {
  1283. $ch
  1284. "username": "netdata on ${host}",
  1285. "icon_url": "${images_base_url}/images/banner-icon-144x144.png",
  1286. "text": "${host} ${status_message}, \`${chart}\`, *${alarm}*",
  1287. "attachments": [
  1288. {
  1289. "fallback": "${alarm} - ${chart} - ${info}",
  1290. "color": "${color}",
  1291. "title": "${alarm}",
  1292. "title_link": "${goto_url}",
  1293. "text": "${info}",
  1294. "fields": [
  1295. {
  1296. "title": "${chart}",
  1297. "value": "chart",
  1298. "short": true
  1299. }
  1300. ],
  1301. "thumb_url": "${image}",
  1302. "footer": "by ${host}",
  1303. "ts": ${when}
  1304. }
  1305. ]
  1306. }
  1307. EOF
  1308. )"
  1309. httpcode=$(docurl -X POST --data-urlencode "payload=${payload}" "${webhook}")
  1310. if [ "${httpcode}" = "200" ]; then
  1311. info "sent slack notification for: ${host} ${chart}.${name} is ${status} ${chstr}"
  1312. sent=$((sent + 1))
  1313. else
  1314. error "failed to send slack notification for: ${host} ${chart}.${name} is ${status} ${chstr}, with HTTP response status code ${httpcode}."
  1315. fi
  1316. done
  1317. [ ${sent} -gt 0 ] && return 0
  1318. return 1
  1319. }
  1320. # -----------------------------------------------------------------------------
  1321. # rocketchat sender
  1322. send_rocketchat() {
  1323. local webhook="${1}" channels="${2}" httpcode sent=0 channel color payload
  1324. [ "${SEND_ROCKETCHAT}" != "YES" ] && return 1
  1325. case "${status}" in
  1326. WARNING) color="warning" ;;
  1327. CRITICAL) color="danger" ;;
  1328. CLEAR) color="good" ;;
  1329. *) color="#777777" ;;
  1330. esac
  1331. for channel in ${channels}; do
  1332. payload="$(
  1333. cat <<EOF
  1334. {
  1335. "channel": "#${channel}",
  1336. "alias": "netdata on ${host}",
  1337. "avatar": "${images_base_url}/images/banner-icon-144x144.png",
  1338. "text": "${host} ${status_message}, \`${chart}\`, *${alarm}*",
  1339. "attachments": [
  1340. {
  1341. "color": "${color}",
  1342. "title": "${alarm}",
  1343. "title_link": "${goto_url}",
  1344. "text": "${info}",
  1345. "fields": [
  1346. {
  1347. "title": "${chart}",
  1348. "short": true,
  1349. "value": "chart"
  1350. }
  1351. ],
  1352. "thumb_url": "${image}",
  1353. "ts": "${when}"
  1354. }
  1355. ]
  1356. }
  1357. EOF
  1358. )"
  1359. httpcode=$(docurl -X POST --data-urlencode "payload=${payload}" "${webhook}")
  1360. if [ "${httpcode}" = "200" ]; then
  1361. info "sent rocketchat notification for: ${host} ${chart}.${name} is ${status} to '${channel}'"
  1362. sent=$((sent + 1))
  1363. else
  1364. error "failed to send rocketchat notification for: ${host} ${chart}.${name} is ${status} to '${channel}', with HTTP response status code ${httpcode}."
  1365. fi
  1366. done
  1367. [ ${sent} -gt 0 ] && return 0
  1368. return 1
  1369. }
  1370. # -----------------------------------------------------------------------------
  1371. # alerta sender
  1372. send_alerta() {
  1373. local webhook="${1}" channels="${2}" httpcode sent=0 channel severity resource event payload auth
  1374. [ "${SEND_ALERTA}" != "YES" ] && return 1
  1375. case "${status}" in
  1376. CRITICAL) severity="critical" ;;
  1377. WARNING) severity="warning" ;;
  1378. CLEAR) severity="cleared" ;;
  1379. *) severity="indeterminate" ;;
  1380. esac
  1381. if [[ ${chart} == httpcheck* ]]; then
  1382. resource=$chart
  1383. event=$name
  1384. else
  1385. resource="${host}"
  1386. event="${chart}.${name}"
  1387. fi
  1388. for channel in ${channels}; do
  1389. payload="$(
  1390. cat <<EOF
  1391. {
  1392. "resource": "${resource}",
  1393. "event": "${event}",
  1394. "environment": "${channel}",
  1395. "severity": "${severity}",
  1396. "service": ["Netdata"],
  1397. "group": "Performance",
  1398. "value": "${value_string}",
  1399. "text": "${info}",
  1400. "tags": ["alarm_id:${alarm_id}"],
  1401. "attributes": {
  1402. "roles": "${roles}",
  1403. "name": "${name}",
  1404. "chart": "${chart}",
  1405. "source": "${src}",
  1406. "moreInfo": "<a href=\"${goto_url}\">View Netdata</a>"
  1407. },
  1408. "origin": "netdata/${host}",
  1409. "type": "netdataAlarm",
  1410. "rawData": "${BASH_ARGV[@]}"
  1411. }
  1412. EOF
  1413. )"
  1414. if [ -n "${ALERTA_API_KEY}" ]; then
  1415. auth="Key ${ALERTA_API_KEY}"
  1416. fi
  1417. httpcode=$(docurl -X POST "${webhook}/alert" -H "Content-Type: application/json" -H "Authorization: $auth" --data "${payload}")
  1418. if [ "${httpcode}" = "200" ] || [ "${httpcode}" = "201" ]; then
  1419. info "sent alerta notification for: ${host} ${chart}.${name} is ${status} to '${channel}'"
  1420. sent=$((sent + 1))
  1421. elif [ "${httpcode}" = "202" ]; then
  1422. info "suppressed alerta notification for: ${host} ${chart}.${name} is ${status} to '${channel}'"
  1423. else
  1424. error "failed to send alerta notification for: ${host} ${chart}.${name} is ${status} to '${channel}', with HTTP response status code ${httpcode}."
  1425. fi
  1426. done
  1427. [ ${sent} -gt 0 ] && return 0
  1428. return 1
  1429. }
  1430. # -----------------------------------------------------------------------------
  1431. # flock sender
  1432. send_flock() {
  1433. local webhook="${1}" channels="${2}" httpcode sent=0 channel color payload
  1434. [ "${SEND_FLOCK}" != "YES" ] && return 1
  1435. case "${status}" in
  1436. WARNING) color="warning" ;;
  1437. CRITICAL) color="danger" ;;
  1438. CLEAR) color="good" ;;
  1439. *) color="#777777" ;;
  1440. esac
  1441. for channel in ${channels}; do
  1442. httpcode=$(docurl -X POST "${webhook}" -H "Content-Type: application/json" -d "{
  1443. \"sendAs\": {
  1444. \"name\" : \"netdata on ${host}\",
  1445. \"profileImage\" : \"${images_base_url}/images/banner-icon-144x144.png\"
  1446. },
  1447. \"text\": \"${host} *${status_message}*\",
  1448. \"timestamp\": \"${when}\",
  1449. \"attachments\": [
  1450. {
  1451. \"description\": \"${chart} - ${info}\",
  1452. \"color\": \"${color}\",
  1453. \"title\": \"${alarm}\",
  1454. \"url\": \"${goto_url}\",
  1455. \"text\": \"${info}\",
  1456. \"views\": {
  1457. \"image\": {
  1458. \"original\": { \"src\": \"${image}\", \"width\": 400, \"height\": 400 },
  1459. \"thumbnail\": { \"src\": \"${image}\", \"width\": 50, \"height\": 50 },
  1460. \"filename\": \"${image}\"
  1461. }
  1462. }
  1463. }
  1464. ]
  1465. }")
  1466. if [ "${httpcode}" = "200" ]; then
  1467. info "sent flock notification for: ${host} ${chart}.${name} is ${status} to '${channel}'"
  1468. sent=$((sent + 1))
  1469. else
  1470. error "failed to send flock notification for: ${host} ${chart}.${name} is ${status} to '${channel}', with HTTP response status code ${httpcode}."
  1471. fi
  1472. done
  1473. [ ${sent} -gt 0 ] && return 0
  1474. return 1
  1475. }
  1476. # -----------------------------------------------------------------------------
  1477. # discord sender
  1478. send_discord() {
  1479. local webhook="${1}/slack" channels="${2}" httpcode sent=0 channel color payload username
  1480. [ "${SEND_DISCORD}" != "YES" ] && return 1
  1481. case "${status}" in
  1482. WARNING) color="warning" ;;
  1483. CRITICAL) color="danger" ;;
  1484. CLEAR) color="good" ;;
  1485. *) color="#777777" ;;
  1486. esac
  1487. for channel in ${channels}; do
  1488. username="netdata on ${host}"
  1489. [ ${#username} -gt 32 ] && username="${username:0:29}..."
  1490. payload="$(
  1491. cat <<EOF
  1492. {
  1493. "channel": "#${channel}",
  1494. "username": "${username}",
  1495. "text": "${host} ${status_message}, \`${chart}\`, *${alarm}*",
  1496. "icon_url": "${images_base_url}/images/banner-icon-144x144.png",
  1497. "attachments": [
  1498. {
  1499. "color": "${color}",
  1500. "title": "${alarm}",
  1501. "title_link": "${goto_url}",
  1502. "text": "${info}",
  1503. "fields": [
  1504. {
  1505. "title": "${chart}"
  1506. }
  1507. ],
  1508. "thumb_url": "${image}",
  1509. "footer_icon": "${images_base_url}/images/banner-icon-144x144.png",
  1510. "footer": "${host}",
  1511. "ts": ${when}
  1512. }
  1513. ]
  1514. }
  1515. EOF
  1516. )"
  1517. httpcode=$(docurl -X POST --data-urlencode "payload=${payload}" "${webhook}")
  1518. if [ "${httpcode}" = "200" ]; then
  1519. info "sent discord notification for: ${host} ${chart}.${name} is ${status} to '${channel}'"
  1520. sent=$((sent + 1))
  1521. else
  1522. error "failed to send discord notification for: ${host} ${chart}.${name} is ${status} to '${channel}', with HTTP response status code ${httpcode}."
  1523. fi
  1524. done
  1525. [ ${sent} -gt 0 ] && return 0
  1526. return 1
  1527. }
  1528. # -----------------------------------------------------------------------------
  1529. # fleep sender
  1530. send_fleep() {
  1531. local httpcode sent=0 webhooks="${1}" data message
  1532. if [ "${SEND_FLEEP}" = "YES" ]; then
  1533. message="${host} ${status_message}, \`${chart}\`, *${alarm}*\\n${info}"
  1534. for hook in ${webhooks}; do
  1535. data="{ "
  1536. data="${data} 'message': '${message}', "
  1537. data="${data} 'user': '${FLEEP_SENDER}' "
  1538. data="${data} }"
  1539. httpcode=$(docurl -X POST --data "${data}" "https://fleep.io/hook/${hook}")
  1540. if [ "${httpcode}" = "200" ]; then
  1541. info "sent fleep data for: ${host} ${chart}.${name} is ${status} and user '${FLEEP_SENDER}'"
  1542. sent=$((sent + 1))
  1543. else
  1544. error "failed to send fleep data for: ${host} ${chart}.${name} is ${status} and user '${FLEEP_SENDER}' with HTTP response status code ${httpcode}."
  1545. fi
  1546. done
  1547. [ ${sent} -gt 0 ] && return 0
  1548. fi
  1549. return 1
  1550. }
  1551. # -----------------------------------------------------------------------------
  1552. # Prowl sender
  1553. send_prowl() {
  1554. local httpcode sent=0 data message keys prio=0 alarm_url event
  1555. if [ "${SEND_PROWL}" = "YES" ]; then
  1556. message="$(urlencode "${host} ${status_message}, \`${chart}\`, *${alarm}*\\n${info}")"
  1557. message="description=${message}"
  1558. keys="$(urlencode "$(echo "${1}" | tr ' ' ,)")"
  1559. keys="apikey=${keys}"
  1560. app="application=Netdata"
  1561. case "${status}" in
  1562. CRITICAL)
  1563. prio=2
  1564. ;;
  1565. WARNING)
  1566. prio=1
  1567. ;;
  1568. esac
  1569. prio="priority=${prio}"
  1570. alarm_url="$(urlencode ${goto_url})"
  1571. alarm_url="url=${alarm_url}"
  1572. event="$(urlencode "${host} ${status_message}")"
  1573. event="event=${event}"
  1574. data="${keys}&${prio}&${alarm_url}&${app}&${event}&${message}"
  1575. httpcode=$(docurl -X POST --data "${data}" "https://api.prowlapp.com/publicapi/add")
  1576. if [ "${httpcode}" = "200" ]; then
  1577. info "sent prowl data for: ${host} ${chart}.${name} is ${status}"
  1578. sent=1
  1579. else
  1580. error "failed to send prowl data for: ${host} ${chart}.${name} is ${status} with with error code ${httpcode}."
  1581. fi
  1582. [ ${sent} -gt 0 ] && return 0
  1583. fi
  1584. return 1
  1585. }
  1586. # -----------------------------------------------------------------------------
  1587. # irc sender
  1588. send_irc() {
  1589. local NICKNAME="${1}" REALNAME="${2}" CHANNELS="${3}" NETWORK="${4}" PORT="${5}" SERVERNAME="${6}" MESSAGE="${7}" sent=0 channel color send_alarm reply_codes error
  1590. if [ "${SEND_IRC}" = "YES" ] && [ -n "${NICKNAME}" ] && [ -n "${REALNAME}" ] && [ -n "${CHANNELS}" ] && [ -n "${NETWORK}" ] && [ -n "${SERVERNAME}" ] && [ -n "${PORT}" ]; then
  1591. case "${status}" in
  1592. WARNING) color="warning" ;;
  1593. CRITICAL) color="danger" ;;
  1594. CLEAR) color="good" ;;
  1595. *) color="#777777" ;;
  1596. esac
  1597. SNDMESSAGE="${MESSAGE//$'\n'/", "}"
  1598. for CHANNEL in ${CHANNELS}; do
  1599. error=0
  1600. send_alarm=$(echo -e "USER ${NICKNAME} guest ${REALNAME} ${SERVERNAME}\\nNICK ${NICKNAME}\\nJOIN ${CHANNEL}\\nPRIVMSG ${CHANNEL} :${SNDMESSAGE}\\nQUIT\\n" \ | ${nc} "${NETWORK}" "${PORT}")
  1601. reply_codes=$(echo "${send_alarm}" | cut -d ' ' -f 2 | grep -o '[0-9]*')
  1602. for code in ${reply_codes}; do
  1603. if [ "${code}" -ge 400 ] && [ "${code}" -le 599 ]; then
  1604. error=1
  1605. break
  1606. fi
  1607. done
  1608. if [ "${error}" -eq 0 ]; then
  1609. info "sent irc notification for: ${host} ${chart}.${name} is ${status} to '${CHANNEL}'"
  1610. sent=$((sent + 1))
  1611. else
  1612. error "failed to send irc notification for: ${host} ${chart}.${name} is ${status} to '${CHANNEL}', with error code ${code}."
  1613. fi
  1614. done
  1615. fi
  1616. [ ${sent} -gt 0 ] && return 0
  1617. return 1
  1618. }
  1619. # -----------------------------------------------------------------------------
  1620. # Amazon SNS sender
  1621. send_awssns() {
  1622. local targets="${1}" message='' sent=0 region=''
  1623. local default_format="${status} on ${host} at ${date}: ${chart} ${value_string}"
  1624. [ "${SEND_AWSSNS}" = "YES" ] || return 1
  1625. message=${AWSSNS_MESSAGE_FORMAT:-${default_format}}
  1626. for target in ${targets}; do
  1627. # Extract the region from the target ARN. We need to explicitly specify the region so that it matches up correctly.
  1628. region="$(echo ${target} | cut -f 4 -d ':')"
  1629. if ${aws} sns publish --region "${region}" --subject "${host} ${status_message} - ${name//_/ } - ${chart}" --message "${message}" --target-arn ${target} &>/dev/null; then
  1630. info "sent Amazon SNS notification for: ${host} ${chart}.${name} is ${status} to '${target}'"
  1631. sent=$((sent + 1))
  1632. else
  1633. error "failed to send Amazon SNS notification for: ${host} ${chart}.${name} is ${status} to '${target}'"
  1634. fi
  1635. done
  1636. [ ${sent} -gt 0 ] && return 0
  1637. return 1
  1638. }
  1639. # -----------------------------------------------------------------------------
  1640. # Matrix sender
  1641. send_matrix() {
  1642. local homeserver="${1}" webhook accesstoken rooms="${2}" httpcode sent=0 payload
  1643. [ "${SEND_MATRIX}" != "YES" ] && return 1
  1644. [ -z "${MATRIX_ACCESSTOKEN}" ] && return 1
  1645. accesstoken="${MATRIX_ACCESSTOKEN}"
  1646. case "${status}" in
  1647. WARNING) emoji="⚠️" ;;
  1648. CRITICAL) emoji="🔴" ;;
  1649. CLEAR) emoji="✅" ;;
  1650. *) emoji="⚪️" ;;
  1651. esac
  1652. for room in ${rooms}; do
  1653. webhook="$homeserver/_matrix/client/r0/rooms/$(urlencode $room)/send/m.room.message?access_token=$accesstoken"
  1654. payload="$(
  1655. cat <<EOF
  1656. {
  1657. "msgtype": "m.notice",
  1658. "format": "org.matrix.custom.html",
  1659. "formatted_body": "${emoji} ${host} ${status_message} - <b>${name//_/ }</b><br>${chart}<br><a href=\"${goto_url}\">${alarm}</a><br><i>${info}</i>",
  1660. "body": "${emoji} ${host} ${status_message} - ${name//_/ } ${chart} ${goto_url} ${alarm} ${info}"
  1661. }
  1662. EOF
  1663. )"
  1664. httpcode=$(docurl -X POST --data "${payload}" "${webhook}")
  1665. if [ "${httpcode}" == "200" ]; then
  1666. info "sent Matrix notification for: ${host} ${chart}.${name} is ${status} to '${room}'"
  1667. sent=$((sent + 1))
  1668. else
  1669. error "failed to send Matrix notification for: ${host} ${chart}.${name} is ${status} to '${room}', with HTTP response status code ${httpcode}."
  1670. fi
  1671. done
  1672. [ ${sent} -gt 0 ] && return 0
  1673. return 1
  1674. }
  1675. # -----------------------------------------------------------------------------
  1676. # syslog sender
  1677. send_syslog() {
  1678. local facility=${SYSLOG_FACILITY:-"local6"} level='info' targets="${1}"
  1679. local priority='' message='' server='' port='' prefix=''
  1680. local temp1='' temp2=''
  1681. [ "${SEND_SYSLOG}" = "YES" ] || return 1
  1682. if [ "${status}" = "CRITICAL" ]; then
  1683. level='crit'
  1684. elif [ "${status}" = "WARNING" ]; then
  1685. level='warning'
  1686. fi
  1687. for target in ${targets}; do
  1688. priority="${facility}.${level}"
  1689. message=''
  1690. server=''
  1691. port=''
  1692. prefix=''
  1693. temp1=''
  1694. temp2=''
  1695. prefix=$(echo ${target} | cut -d '/' -f 2)
  1696. temp1=$(echo ${target} | cut -d '/' -f 1)
  1697. if [ ${prefix} != ${temp1} ]; then
  1698. if (echo ${temp1} | grep -q '@'); then
  1699. temp2=$(echo ${temp1} | cut -d '@' -f 1)
  1700. server=$(echo ${temp1} | cut -d '@' -f 2)
  1701. if [ ${temp2} != ${server} ]; then
  1702. priority=${temp2}
  1703. fi
  1704. port=$(echo ${server} | rev | cut -d ':' -f 1 | rev)
  1705. if (echo ${server} | grep -E -q '\[.*\]'); then
  1706. if (echo ${port} | grep -q ']'); then
  1707. port=''
  1708. else
  1709. server=$(echo ${server} | rev | cut -d ':' -f 2- | rev)
  1710. fi
  1711. else
  1712. if [ ${port} = ${server} ]; then
  1713. port=''
  1714. else
  1715. server=$(echo ${server} | cut -d ':' -f 1)
  1716. fi
  1717. fi
  1718. else
  1719. priority=${temp1}
  1720. fi
  1721. fi
  1722. message="${prefix} ${status} on ${host} at ${date}: ${chart} ${value_string}"
  1723. if [ ${server} ]; then
  1724. logger_options="${logger_options} -n ${server}"
  1725. if [ ${port} ]; then
  1726. logger_options="${logger_options} -P ${port}"
  1727. fi
  1728. fi
  1729. ${logger} -p ${priority} ${logger_options} "${message}"
  1730. done
  1731. return $?
  1732. }
  1733. # -----------------------------------------------------------------------------
  1734. # SMS sender
  1735. send_sms() {
  1736. local recipients="${1}" errcode errmessage sent=0
  1737. # Human readable SMS
  1738. local msg="${host} ${status_message}: ${chart}, ${alarm}"
  1739. # limit it to 160 characters
  1740. msg="${msg:0:160}"
  1741. if [ "${SEND_SMS}" = "YES" ] && [ -n "${sendsms}" ] && [ -n "${recipients}" ] && [ -n "${msg}" ]; then
  1742. # http://api.kavenegar.com/v1/{API-KEY}/sms/send.json
  1743. for phone in ${recipients}; do
  1744. errmessage=$($sendsms $phone "$msg" 2>&1)
  1745. errcode=$?
  1746. if [ ${errcode} -eq 0 ]; then
  1747. info "sent smstools3 SMS for: ${host} ${chart}.${name} is ${status} to '${user}'"
  1748. sent=$((sent + 1))
  1749. else
  1750. error "failed to send smstools3 SMS for: ${host} ${chart}.${name} is ${status} to '${user}' with error code ${errcode}: ${errmessage}."
  1751. fi
  1752. done
  1753. [ ${sent} -gt 0 ] && return 0
  1754. fi
  1755. return 1
  1756. }
  1757. # -----------------------------------------------------------------------------
  1758. # Dynatrace sender
  1759. send_dynatrace() {
  1760. [ "${SEND_DYNATRACE}" != "YES" ] && return 1
  1761. local dynatrace_url="${DYNATRACE_SERVER}/e/${DYNATRACE_SPACE}/api/v1/events"
  1762. local description="Netdata Notification for: ${host} ${chart}.${name} is ${status}"
  1763. local payload=""
  1764. payload=$(cat <<EOF
  1765. {
  1766. "title": "Netdata Alarm from ${host}",
  1767. "source" : "${DYNATRACE_ANNOTATION_TYPE}",
  1768. "description" : "${description}",
  1769. "eventType": "${DYNATRACE_EVENT}",
  1770. "attachRules":{
  1771. "tagRule":[{
  1772. "meTypes":["HOST"],
  1773. "tags":["${DYNATRACE_TAG_VALUE}"]
  1774. }]
  1775. },
  1776. "customProperties":{
  1777. "description": "${description}"
  1778. }
  1779. }
  1780. EOF
  1781. )
  1782. # echo ${payload}
  1783. httpcode=$(docurl -X POST -H "Authorization: Api-token ${DYNATRACE_TOKEN}" -H "Content-Type: application/json" -d "${payload}" ${dynatrace_url})
  1784. ret=$?
  1785. if [ ${ret} -eq 0 ]; then
  1786. if [ "${httpcode}" = "200" ]; then
  1787. info "sent ${DYNATRACE_EVENT} to ${DYNATRACE_SERVER}"
  1788. return 0
  1789. else
  1790. warning "Dynatrace ${DYNATRACE_SERVER} responded ${httpcode} notification for: ${host} ${chart}.${name} is ${status} was not sent!"
  1791. return 1
  1792. fi
  1793. else
  1794. error "failed to sent ${DYNATRACE_EVENT} notification for: ${host} ${chart}.${name} is ${status} to ${DYNATRACE_SERVER} with error code ${ret}."
  1795. return 1
  1796. fi
  1797. }
  1798. # -----------------------------------------------------------------------------
  1799. # Opsgenie sender
  1800. send_opsgenie() {
  1801. local payload httpcode oldv currv priority
  1802. [ "${SEND_OPSGENIE}" != "YES" ] && return 1
  1803. if [ -z "${OPSGENIE_API_KEY}" ] ; then
  1804. info "Can't send Opsgenie notification, because OPSGENIE_API_KEY is not defined"
  1805. return 1
  1806. fi
  1807. # Priority for OpsGenie alert (https://support.atlassian.com/opsgenie/docs/update-alert-priority-level/)
  1808. case "${status}" in
  1809. CRITICAL) priority="P1" ;; # Critical is P1
  1810. WARNING) priority="P3" ;; # Warning is P3
  1811. CLEAR) priority="P5" ;; # Clear is P5
  1812. *) priority="P3" ;; # OpsGenie's default alert level is P3
  1813. esac
  1814. # We are sending null when values are nan to avoid errors while JSON message is parsed
  1815. [ "${old_value}" != "nan" ] && oldv="${old_value}" || oldv="null"
  1816. [ "${value}" != "nan" ] && currv="${value}" || currv="null"
  1817. payload=$(cat <<EOF
  1818. {
  1819. "host" : "${host}",
  1820. "unique_id" : "${unique_id}",
  1821. "alarmId" : ${alarm_id},
  1822. "eventId" : ${event_id},
  1823. "chart" : "${chart}",
  1824. "when": ${when},
  1825. "name" : "${name}",
  1826. "priority" : "${priority}",
  1827. "status" : "${status}",
  1828. "old_status" : "${old_status}",
  1829. "value" : ${currv},
  1830. "old_value" : ${oldv},
  1831. "duration": ${duration},
  1832. "non_clear_duration": ${non_clear_duration},
  1833. "units" : "${units}",
  1834. "info" : "${status_message}, ${info}",
  1835. "calc_expression" : "${calc_expression}",
  1836. "total_warnings" : "${total_warnings}",
  1837. "total_critical" : "${total_critical}",
  1838. "src" : "${src}"
  1839. }
  1840. EOF
  1841. )
  1842. httpcode=$(docurl -X POST -H "Content-Type: application/json" -d "${payload}" "${OPSGENIE_API_URL}/v1/json/integrations/webhooks/netdata?apiKey=${OPSGENIE_API_KEY}")
  1843. # https://docs.opsgenie.com/docs/alert-api#create-alert
  1844. if [ "${httpcode}" = "200" ]; then
  1845. info "sent opsgenie notification for: ${host} ${chart}.${name} is ${status}"
  1846. else
  1847. error "failed to send opsgenie notification for: ${host} ${chart}.${name} is ${status}, with HTTP error code ${httpcode}."
  1848. return 1
  1849. fi
  1850. return 0
  1851. }
  1852. # -----------------------------------------------------------------------------
  1853. # Gotify sender
  1854. send_gotify() {
  1855. local payload httpcode priority
  1856. [ "${SEND_GOTIFY}" != "YES" ] && return 1
  1857. if [ -z "${GOTIFY_APP_TOKEN}" ] ; then
  1858. info "Can't send Gotify notification, because GOTIFY_APP_TOKEN is not defined"
  1859. return 1
  1860. fi
  1861. # priority for Gotify Android app
  1862. case "${status}" in
  1863. CRITICAL) priority=10 ;; # sound + vibration
  1864. WARNING) priority=4 ;; # sound
  1865. *) priority=1 ;; # notification only
  1866. esac
  1867. payload=$(cat <<EOF
  1868. {
  1869. "title" : "${status}, ${name} = ${value_string}, on ${host}",
  1870. "message" : "${date}: ${chart} ${value_string}",
  1871. "priority" : ${priority}
  1872. }
  1873. EOF
  1874. )
  1875. httpcode=$(docurl -X POST -H "Content-Type: application/json" -d "${payload}" "${GOTIFY_APP_URL}/message?token=${GOTIFY_APP_TOKEN}")
  1876. if [ "${httpcode}" = "200" ]; then
  1877. info "sent gotify notification for: ${host} ${chart}.${name} is ${status}"
  1878. else
  1879. error "failed to send gotify notification for: ${host} ${chart}.${name} is ${status}, with HTTP error code ${httpcode}."
  1880. return 1
  1881. fi
  1882. return 0
  1883. }
  1884. # -----------------------------------------------------------------------------
  1885. # ntfy sender
  1886. send_ntfy() {
  1887. local httpcode priority recipients=${1} sent=0 msg
  1888. [ "${SEND_NTFY}" != "YES" ] && return 1
  1889. case "${status}" in
  1890. WARNING) emoji="warning" ;;
  1891. CRITICAL) emoji="red_circle" ;;
  1892. CLEAR) emoji="white_check_mark" ;;
  1893. *) emoji="white_circle" ;;
  1894. esac
  1895. case ${status} in
  1896. WARNING) priority="high";;
  1897. CRITICAL) priority="urgent";;
  1898. *) priority="default" ;;
  1899. esac
  1900. # Adding ntfy header generation logic
  1901. # Heavily inspired by https://github.com/nickexyz/ntfy-shellscripts/blob/main/sabnzbd.sh
  1902. tmp_header=""
  1903. if [[ -n "${NTFY_USERNAME}" ]] && [[ -n "${NTFY_PASSWORD}" ]]; then
  1904. ntfy_base64=$( echo -n "$NTFY_USERNAME:$NTFY_PASSWORD" | base64 )
  1905. tmp_header="Authorization: Basic ${ntfy_base64}"
  1906. elif [ -n "${NTFY_ACCESS_TOKEN}" ]; then
  1907. tmp_header="Authorization: Bearer ${NTFY_ACCESS_TOKEN}"
  1908. fi
  1909. ntfy_auth_header=()
  1910. if [ -n "${tmp_header}" ]; then
  1911. ntfy_auth_header=("-H" "${tmp_header}")
  1912. fi
  1913. for recipient in ${recipients}; do
  1914. msg="${host} ${status_message}: ${alarm} - ${info}"
  1915. httpcode=$(docurl -X POST \
  1916. "${ntfy_auth_header[@]}" \
  1917. -H "Icon: https://raw.githubusercontent.com/netdata/netdata/master/web/gui/dashboard/images/favicon-196x196.png" \
  1918. -H "Title: ${host}: ${name//_/ }" \
  1919. -H "Tags: ${emoji}" \
  1920. -H "Priority: ${priority}" \
  1921. -H "Actions: view, View node, ${goto_url}, clear=true;" \
  1922. -d "${msg}" \
  1923. ${recipient})
  1924. if [ "${httpcode}" == "200" ]; then
  1925. info "sent ntfy notification for: ${host} ${chart}.${name} is ${status} to '${recipient}'"
  1926. sent=$((sent + 1))
  1927. else
  1928. error "failed to send ntfy notification for: ${host} ${chart}.${name} is ${status} to '${recipient}', with HTTP response status code ${httpcode}."
  1929. fi
  1930. done
  1931. [ ${sent} -gt 0 ] && return 0
  1932. return 1
  1933. }
  1934. # -----------------------------------------------------------------------------
  1935. # prepare the content of the notification
  1936. # the url to send the user on click
  1937. urlencode "${args_host}" >/dev/null
  1938. url_host="${REPLY}"
  1939. urlencode "${chart}" >/dev/null
  1940. url_chart="${REPLY}"
  1941. urlencode "${name}" >/dev/null
  1942. url_name="${REPLY}"
  1943. urlencode "${value_string}" >/dev/null
  1944. url_value_string="${REPLY}"
  1945. redirect_params="host=${url_host}&chart=${url_chart}&alarm=${url_name}&alarm_unique_id=${unique_id}&alarm_id=${alarm_id}&alarm_event_id=${event_id}&alarm_when=${when}&alarm_status=${status}&alarm_chart=${chart}&alarm_value=${url_value_string}"
  1946. if [ -z "${NETDATA_REGISTRY_UNIQUE_ID}" ]; then
  1947. if [ -f "@registrydir_POST@/netdata.public.unique.id" ]; then
  1948. NETDATA_REGISTRY_UNIQUE_ID="$(cat "@registrydir_POST@/netdata.public.unique.id")"
  1949. else
  1950. error "failed to identify this agent via its NETDATA_REGISTRY_UNIQUE_ID."
  1951. fi
  1952. fi
  1953. goto_url="${NETDATA_REGISTRY_URL}/registry-alert-redirect.html?agent_machine_guid=${NETDATA_REGISTRY_UNIQUE_ID}&host_machine_guid=${child_machine_guid}&transition_id=${transition_id}&${redirect_params}"
  1954. # the severity of the alarm
  1955. severity="${status}"
  1956. # the time the alarm was raised
  1957. duration4human ${duration} >/dev/null
  1958. duration_txt="${REPLY}"
  1959. duration4human ${non_clear_duration} >/dev/null
  1960. non_clear_duration_txt="${REPLY}"
  1961. raised_for="(was ${old_status,,} for ${duration_txt})"
  1962. # the key status message
  1963. status_message="status unknown"
  1964. # the color of the alarm
  1965. color="grey"
  1966. # the alarm value
  1967. alarm="${summary//_/ } = ${value_string}"
  1968. # the image of the alarm
  1969. image="${images_base_url}/images/banner-icon-144x144.png"
  1970. # have a default email status, in case the following case does not catch it
  1971. status_email_subject="${status}"
  1972. # prepare the title based on status
  1973. case "${status}" in
  1974. CRITICAL)
  1975. image="${images_base_url}/images/alert-128-red.png"
  1976. alarm_badge="https://app.netdata.cloud/static/email/img/label_critical.png"
  1977. status_message="is critical"
  1978. status_email_subject="Critical"
  1979. color="#ca414b"
  1980. rich_status_raised_for="Raised to critical, for ${non_clear_duration_txt}"
  1981. background_color="#FFEBEF"
  1982. border_color="#FF4136"
  1983. text_color="#FF4136"
  1984. action_text_color="#FFFFFF"
  1985. ;;
  1986. WARNING)
  1987. image="${images_base_url}/images/alert-128-orange.png"
  1988. alarm_badge="https://app.netdata.cloud/static/email/img/label_warning.png"
  1989. status_message="needs attention"
  1990. status_email_subject="Warning"
  1991. color="#ffc107"
  1992. rich_status_raised_for="Raised to warning, for ${non_clear_duration_txt}"
  1993. background_color="#FFF8E1"
  1994. border_color="#FFC300"
  1995. text_color="#536775"
  1996. action_text_color="#35414A"
  1997. ;;
  1998. CLEAR)
  1999. image="${images_base_url}/images/check-mark-2-128-green.png"
  2000. alarm_badge="https://app.netdata.cloud/static/email/img/label_recovered.png"
  2001. status_message="recovered"
  2002. status_email_subject="Clear"
  2003. color="#77ca6d"
  2004. rich_status_raised_for=
  2005. background_color="#E5F5E8"
  2006. border_color="#68C47D"
  2007. text_color="#00AB44"
  2008. action_text_color="#FFFFFF"
  2009. ;;
  2010. esac
  2011. # the html email subject
  2012. html_email_subject="${status_email_subject}, ${summary} = ${value_string}, on ${host}"
  2013. if [ "${status}" = "CLEAR" ]; then
  2014. severity="Recovered from ${old_status}"
  2015. if [ ${non_clear_duration} -gt ${duration} ]; then
  2016. raised_for="(alarm was raised for ${non_clear_duration_txt})"
  2017. fi
  2018. rich_status_raised_for="Recovered from ${old_status,,}, ${raised_for}"
  2019. # don't show the value when the status is CLEAR
  2020. # for certain alarms, this value might not have any meaning
  2021. alarm="${summary//_/ } ${raised_for}"
  2022. html_email_subject="${status_email_subject}, ${summary} ${raised_for}, on ${host}"
  2023. elif { [ "${old_status}" = "WARNING" ] && [ "${status}" = "CRITICAL" ]; }; then
  2024. severity="Escalated to ${status}"
  2025. if [ ${non_clear_duration} -gt ${duration} ]; then
  2026. raised_for="(alarm is raised for ${non_clear_duration_txt})"
  2027. fi
  2028. rich_status_raised_for="Escalated to critical, ${raised_for}"
  2029. elif { [ "${old_status}" = "CRITICAL" ] && [ "${status}" = "WARNING" ]; }; then
  2030. severity="Demoted to ${status}"
  2031. if [ ${non_clear_duration} -gt ${duration} ]; then
  2032. raised_for="(alarm is raised for ${non_clear_duration_txt})"
  2033. fi
  2034. rich_status_raised_for="Demoted to warning, ${raised_for}"
  2035. else
  2036. raised_for=
  2037. fi
  2038. # prepare HTML versions of elements
  2039. info_html=
  2040. [ -n "${info}" ] && info_html=" <small><br/>${info}</small>"
  2041. raised_for_html=
  2042. [ -n "${raised_for}" ] && raised_for_html="<br/><small>${raised_for}</small>"
  2043. # -----------------------------------------------------------------------------
  2044. # send the slack notification
  2045. # slack aggregates posts from the same username
  2046. # so we use "${host} ${status}" as the bot username, to make them diff
  2047. send_slack "${SLACK_WEBHOOK_URL}" "${to_slack}"
  2048. SENT_SLACK=$?
  2049. # -----------------------------------------------------------------------------
  2050. # send the Microsoft Teams notification
  2051. # Microsoft teams aggregates posts from the same username
  2052. # so we use "${host} ${status}" as the bot username, to make them diff
  2053. send_msteams "${MSTEAMS_WEBHOOK_URL}" "${to_msteams}"
  2054. SENT_MSTEAMS=$?
  2055. # -----------------------------------------------------------------------------
  2056. # send the rocketchat notification
  2057. # rocketchat aggregates posts from the same username
  2058. # so we use "${host} ${status}" as the bot username, to make them diff
  2059. send_rocketchat "${ROCKETCHAT_WEBHOOK_URL}" "${to_rocketchat}"
  2060. SENT_ROCKETCHAT=$?
  2061. # -----------------------------------------------------------------------------
  2062. # send the alerta notification
  2063. # alerta aggregates posts from the same username
  2064. # so we use "${host} ${status}" as the bot username, to make them diff
  2065. send_alerta "${ALERTA_WEBHOOK_URL}" "${to_alerta}"
  2066. SENT_ALERTA=$?
  2067. # -----------------------------------------------------------------------------
  2068. # send the flock notification
  2069. # flock aggregates posts from the same username
  2070. # so we use "${host} ${status}" as the bot username, to make them diff
  2071. send_flock "${FLOCK_WEBHOOK_URL}" "${to_flock}"
  2072. SENT_FLOCK=$?
  2073. # -----------------------------------------------------------------------------
  2074. # send the discord notification
  2075. # discord aggregates posts from the same username
  2076. # so we use "${host} ${status}" as the bot username, to make them diff
  2077. send_discord "${DISCORD_WEBHOOK_URL}" "${to_discord}"
  2078. SENT_DISCORD=$?
  2079. # -----------------------------------------------------------------------------
  2080. # send the pushover notification
  2081. send_pushover "${PUSHOVER_APP_TOKEN}" "${to_pushover}" "${when}" "${goto_url}" "${status}" "${host} ${status_message} - ${name//_/ } - ${chart}" "
  2082. <font color=\"${color}\"><b>${alarm}</b></font>${info_html}<br/>&nbsp;
  2083. <small><b>${chart}</b><br/>Chart<br/>&nbsp;</small>
  2084. <small><b>${severity}</b><br/>Severity<br/>&nbsp;</small>
  2085. <small><b>${date}${raised_for_html}</b><br/>Time<br/>&nbsp;</small>
  2086. <a href=\"${goto_url}\">View Netdata</a><br/>&nbsp;
  2087. <small><small>The source of this alarm is line ${src}</small></small>
  2088. "
  2089. SENT_PUSHOVER=$?
  2090. # -----------------------------------------------------------------------------
  2091. # send the pushbullet notification
  2092. send_pushbullet "${PUSHBULLET_ACCESS_TOKEN}" "${PUSHBULLET_SOURCE_DEVICE}" "${to_pushbullet}" "${goto_url}" "${host} ${status_message} - ${name//_/ } - ${chart}" "${alarm}\\n
  2093. Severity: ${severity}\\n
  2094. Chart: ${chart}\\n
  2095. ${date}\\n
  2096. The source of this alarm is line ${src}"
  2097. SENT_PUSHBULLET=$?
  2098. # -----------------------------------------------------------------------------
  2099. # send the twilio SMS
  2100. send_twilio "${TWILIO_ACCOUNT_SID}" "${TWILIO_ACCOUNT_TOKEN}" "${TWILIO_NUMBER}" "${to_twilio}" "${host} ${status_message} - ${name//_/ } - ${chart}" "${alarm}
  2101. Severity: ${severity}
  2102. Chart: ${chart}
  2103. ${info}"
  2104. SENT_TWILIO=$?
  2105. # -----------------------------------------------------------------------------
  2106. # send the messagebird SMS
  2107. send_messagebird "${MESSAGEBIRD_ACCESS_KEY}" "${MESSAGEBIRD_NUMBER}" "${to_messagebird}" "${host} ${status_message} - ${name//_/ } - ${chart}" "${alarm}
  2108. Severity: ${severity}
  2109. Chart: ${chart}
  2110. ${info}"
  2111. SENT_MESSAGEBIRD=$?
  2112. # -----------------------------------------------------------------------------
  2113. # send the kavenegar SMS
  2114. send_kavenegar "${KAVENEGAR_API_KEY}" "${KAVENEGAR_SENDER}" "${to_kavenegar}" "${host} ${status_message} - ${name//_/ } - ${chart}" "${alarm}
  2115. Severity: ${severity}
  2116. Chart: ${chart}
  2117. ${info}"
  2118. SENT_KAVENEGAR=$?
  2119. # -----------------------------------------------------------------------------
  2120. # send the telegram.org message
  2121. # https://core.telegram.org/bots/api#formatting-options
  2122. send_telegram "${TELEGRAM_BOT_TOKEN}" "${to_telegram}" "${host} ${status_message} - <b>${name//_/ }</b>
  2123. ${chart}
  2124. <a href=\"${goto_url}\">${alarm}</a>
  2125. <i>${info}</i>"
  2126. SENT_TELEGRAM=$?
  2127. # -----------------------------------------------------------------------------
  2128. # send the kafka message
  2129. send_kafka
  2130. SENT_KAFKA=$?
  2131. # -----------------------------------------------------------------------------
  2132. # send the pagerduty.com message
  2133. send_pd "${to_pd}"
  2134. SENT_PD=$?
  2135. # -----------------------------------------------------------------------------
  2136. # send the fleep message
  2137. send_fleep "${to_fleep}"
  2138. SENT_FLEEP=$?
  2139. # -----------------------------------------------------------------------------
  2140. # send the Prowl message
  2141. send_prowl "${to_prowl}"
  2142. SENT_PROWL=$?
  2143. # -----------------------------------------------------------------------------
  2144. # send the irc message
  2145. send_irc "${IRC_NICKNAME}" "${IRC_REALNAME}" "${to_irc}" "${IRC_NETWORK}" "${IRC_PORT}" "${host}" "${host} ${status_message} - ${name//_/ } - ${chart} ----- ${alarm}
  2146. Severity: ${severity}
  2147. Chart: ${chart}
  2148. ${info}"
  2149. SENT_IRC=$?
  2150. # -----------------------------------------------------------------------------
  2151. # send the SMS message with smstools3
  2152. send_sms "${to_sms}"
  2153. SENT_SMS=$?
  2154. # -----------------------------------------------------------------------------
  2155. # send the custom message
  2156. send_custom() {
  2157. # is it enabled?
  2158. [ "${SEND_CUSTOM}" != "YES" ] && return 1
  2159. # do we have any sender?
  2160. [ -z "${1}" ] && return 1
  2161. # call the custom_sender function
  2162. custom_sender "${@}"
  2163. }
  2164. send_custom "${to_custom}"
  2165. SENT_CUSTOM=$?
  2166. # -----------------------------------------------------------------------------
  2167. # send hipchat message
  2168. send_hipchat "${HIPCHAT_AUTH_TOKEN}" "${to_hipchat}" " \
  2169. ${host} ${status_message}<br/> \
  2170. <b>${alarm}</b> ${info_html}<br/> \
  2171. <b>${chart}</b><br/> \
  2172. <b>${date}${raised_for_html}</b><br/> \
  2173. <a href=\\\"${goto_url}\\\">View netdata dashboard</a> \
  2174. (source of alarm ${src}) \
  2175. "
  2176. SENT_HIPCHAT=$?
  2177. # -----------------------------------------------------------------------------
  2178. # send the Amazon SNS message
  2179. send_awssns "${to_awssns}"
  2180. SENT_AWSSNS=$?
  2181. # -----------------------------------------------------------------------------
  2182. # send the Matrix message
  2183. send_matrix "${MATRIX_HOMESERVER}" "${to_matrix}"
  2184. SENT_MATRIX=$?
  2185. # -----------------------------------------------------------------------------
  2186. # send the syslog message
  2187. send_syslog "${to_syslog}"
  2188. SENT_SYSLOG=$?
  2189. # -----------------------------------------------------------------------------
  2190. # send the email
  2191. IFS='' read -r -d '' email_plaintext_part <<EOF
  2192. Content-Type: text/plain; encoding=${EMAIL_CHARSET}
  2193. Content-Disposition: inline
  2194. Content-Transfer-Encoding: 8bit
  2195. ${host} ${status_message}
  2196. ${alarm} ${info}
  2197. ${raised_for}
  2198. Chart : ${chart}
  2199. Severity: ${severity}
  2200. URL : ${goto_url}
  2201. Source : ${src}
  2202. Date : ${date}
  2203. Notification generated on ${host}
  2204. Evaluated Expression : ${calc_expression}
  2205. Expression Variables : ${calc_param_values}
  2206. The host has ${total_warnings} WARNING and ${total_critical} CRITICAL alarm(s) raised.
  2207. EOF
  2208. if [[ "${EMAIL_PLAINTEXT_ONLY}" == "YES" ]]; then
  2209. send_email <<EOF
  2210. To: ${to_email}
  2211. Subject: ${host} ${status_message} - ${name//_/ } - ${chart}
  2212. MIME-Version: 1.0
  2213. Content-Type: multipart/alternative; boundary="multipart-boundary"
  2214. ${email_thread_headers}
  2215. X-Netdata-Severity: ${status,,}
  2216. X-Netdata-Alert-Name: $name
  2217. X-Netdata-Chart: $chart
  2218. X-Netdata-Classification: $classification
  2219. X-Netdata-Host: $host
  2220. X-Netdata-Role: $roles
  2221. This is a MIME-encoded multipart message
  2222. --multipart-boundary
  2223. ${email_plaintext_part}
  2224. --multipart-boundary--
  2225. EOF
  2226. else
  2227. now=$(date "+%s")
  2228. if [ -n "$total_warn_alarms" ]; then
  2229. while read -d, -r pair; do
  2230. IFS='=' read -r key val <<<"$pair"
  2231. date_w=$(date --date=@${val} "${date_format}" 2>/dev/null)
  2232. [ -z "${date_w}" ] && date_w=$(date "${date_format}" 2>/dev/null)
  2233. [ -z "${date_w}" ] && date_w=$(date --date=@${val} 2>/dev/null)
  2234. [ -z "${date_w}" ] && date_w=$(date 2>/dev/null)
  2235. elapsed=$((now - val))
  2236. duration4human ${elapsed} >/dev/null
  2237. elapsed_txt="${REPLY}"
  2238. WARN_ALARMS+="
  2239. <div class=\"set-font\" style=\"font-family: 'IBM Plex Sans', sans-serif; background: #FFFFFF; background-color: #FFFFFF; margin: 0px auto; max-width: 600px;\">
  2240. <table align=\"center\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\" role=\"presentation\" style=\"background:#FFFFFF;background-color:#FFFFFF;width:100%;\">
  2241. <tbody>
  2242. <tr>
  2243. <td style=\"border-top:8px solid #F7F8F8;direction:ltr;font-size:0px;padding:20px 0;text-align:center;\">
  2244. <!--[if mso | IE]><table role=\"presentation\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\"><tr><td class=\"\" style=\"vertical-align:top;width:300px;\" ><![endif]-->
  2245. <div class=\"mj-column-per-50 mj-outlook-group-fix\" style=\"font-size:0px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:50%;\">
  2246. <table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" role=\"presentation\" style=\"vertical-align:top;\" width=\"100%\">
  2247. <tbody>
  2248. <tr>
  2249. <td align=\"left\" style=\"font-size:0px;padding:10px 25px;word-break:break-word;\">
  2250. <div style=\"font-family:Open Sans, sans-serif;font-size:14px;font-weight:600;line-height:1;text-align:left;color:#35414A;\">${key}</div>
  2251. </td>
  2252. </tr>
  2253. <tr>
  2254. <td align=\"left\" style=\"font-size:0px;padding:10px 25px;padding-top:2px;word-break:break-word;\">
  2255. <div style=\"font-family:Open Sans, sans-serif;font-size:12px;line-height:1;text-align:left;color:#35414A;\">${date_w}</div>
  2256. </td>
  2257. </tr>
  2258. </tbody>
  2259. </table>
  2260. </div>
  2261. <!--[if mso | IE]></td><td class=\"\" style=\"vertical-align:top;width:300px;\" ><![endif]-->
  2262. <div class=\"mj-column-per-50 mj-outlook-group-fix\" style=\"font-size:0px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:50%;\">
  2263. <table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" role=\"presentation\" width=\"100%\">
  2264. <tbody>
  2265. <tr>
  2266. <td style=\"vertical-align:top;padding-top:13px;\">
  2267. <table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" role=\"presentation\" style width=\"100%\">
  2268. <tbody>
  2269. <tr>
  2270. <td align=\"right\" style=\"font-size:0px;padding:10px 25px;word-break:break-word;\">
  2271. <div style=\"font-family:Open Sans, sans-serif;font-size:13px;line-height:1;text-align:right;color:#555555;\"><span style=\"background-color:#FFF8E1; border: 1px solid #FFC300; border-radius:36px; padding: 2px 12px; margin-top: 20px; white-space: nowrap\">
  2272. Warning for ${elapsed_txt}
  2273. </span></div>
  2274. </td>
  2275. </tr>
  2276. </tbody>
  2277. </table>
  2278. </td>
  2279. </tr>
  2280. </tbody>
  2281. </table>
  2282. </div>
  2283. <!--[if mso | IE]></td></tr></table><![endif]-->
  2284. </td>
  2285. </tr>
  2286. </tbody>
  2287. </table>
  2288. </div>
  2289. "
  2290. done <<<"$total_warn_alarms,"
  2291. fi
  2292. if [ -n "$total_crit_alarms" ]; then
  2293. while read -d, -r pair; do
  2294. IFS='=' read -r key val <<<"$pair"
  2295. date_c=$(date --date=@${val} "${date_format}" 2>/dev/null)
  2296. [ -z "${date_c}" ] && date_c=$(date "${date_format}" 2>/dev/null)
  2297. [ -z "${date_c}" ] && date_c=$(date --date=@${val} 2>/dev/null)
  2298. [ -z "${date_c}" ] && date_c=$(date 2>/dev/null)
  2299. elapsed=$((now - val))
  2300. duration4human ${elapsed} >/dev/null
  2301. elapsed_txt="${REPLY}"
  2302. CRIT_ALARMS+="
  2303. <div class=\"set-font\" style=\"font-family: 'IBM Plex Sans', sans-serif; background: #FFFFFF; background-color: #FFFFFF; margin: 0px auto; max-width: 600px;\">
  2304. <table align=\"center\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\" role=\"presentation\" style=\"background:#FFFFFF;background-color:#FFFFFF;width:100%;\">
  2305. <tbody>
  2306. <tr>
  2307. <td style=\"border-top:8px solid #F7F8F8;direction:ltr;font-size:0px;padding:20px 0;text-align:center;\">
  2308. <!--[if mso | IE]><table role=\"presentation\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\"><tr><td class=\"\" style=\"vertical-align:top;width:300px;\" ><![endif]-->
  2309. <div class=\"mj-column-per-50 mj-outlook-group-fix\" style=\"font-size:0px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:50%;\">
  2310. <table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" role=\"presentation\" style=\"vertical-align:top;\" width=\"100%\">
  2311. <tbody>
  2312. <tr>
  2313. <td align=\"left\" style=\"font-size:0px;padding:10px 25px;word-break:break-word;\">
  2314. <div style=\"font-family:Open Sans, sans-serif;font-size:14px;font-weight:600;line-height:1;text-align:left;color:#35414A;\">${key}</div>
  2315. </td>
  2316. </tr>
  2317. <tr>
  2318. <td align=\"left\" style=\"font-size:0px;padding:10px 25px;padding-top:2px;word-break:break-word;\">
  2319. <div style=\"font-family:Open Sans, sans-serif;font-size:12px;line-height:1;text-align:left;color:#35414A;\">${date_c}</div>
  2320. </td>
  2321. </tr>
  2322. </tbody>
  2323. </table>
  2324. </div>
  2325. <!--[if mso | IE]></td><td class=\"\" style=\"vertical-align:top;width:300px;\" ><![endif]-->
  2326. <div class=\"mj-column-per-50 mj-outlook-group-fix\" style=\"font-size:0px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:50%;\">
  2327. <table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" role=\"presentation\" width=\"100%\">
  2328. <tbody>
  2329. <tr>
  2330. <td style=\"vertical-align:top;padding-top:13px;\">
  2331. <table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" role=\"presentation\" style width=\"100%\">
  2332. <tbody>
  2333. <tr>
  2334. <td align=\"right\" style=\"font-size:0px;padding:10px 25px;word-break:break-word;\">
  2335. <div style=\"font-family:Open Sans, sans-serif;font-size:13px;line-height:1;text-align:right;color:#35414A;\"><span style=\"background-color:#FFEBEF; border: 1px solid #FF4136; border-radius:36px; padding: 2px 12px; margin-top: 20px; white-space: nowrap\">
  2336. Critical for ${elapsed_txt}
  2337. </span></div>
  2338. </td>
  2339. </tr>
  2340. </tbody>
  2341. </table>
  2342. </td>
  2343. </tr>
  2344. </tbody>
  2345. </table>
  2346. </div>
  2347. <!--[if mso | IE]></td></tr></table><![endif]-->
  2348. </td>
  2349. </tr>
  2350. </tbody>
  2351. </table>
  2352. </div>
  2353. "
  2354. done <<<"$total_crit_alarms,"
  2355. fi
  2356. if (( total_warnings + total_critical > 15 )); then
  2357. EXTRA_ALARMS_LIST_TEXT="(Showing latest 15 alerts)"
  2358. fi
  2359. if [ -n "$edit_command_line" ]; then
  2360. IFS='=' read -r edit_command line s_host <<<"$edit_command_line"
  2361. fi
  2362. IFS='' read -r -d '' email_html_part <<EOF
  2363. Content-Type: text/html; encoding=${EMAIL_CHARSET}
  2364. Content-Disposition: inline
  2365. Content-Transfer-Encoding: 8bit
  2366. <!doctype html>
  2367. <html xmlns="http://www.w3.org/1999/xhtml" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office">
  2368. <head>
  2369. <title>
  2370. </title>
  2371. <!--[if !mso]><!-->
  2372. <meta http-equiv="X-UA-Compatible" content="IE=edge">
  2373. <!--<![endif]-->
  2374. <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  2375. <meta name="viewport" content="width=device-width, initial-scale=1">
  2376. <style type="text/css">
  2377. #outlook a { padding:0; }
  2378. body { margin:0;padding:0;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%; }
  2379. table, td { border-collapse:collapse;mso-table-lspace:0pt;mso-table-rspace:0pt; }
  2380. img { border:0;height:auto;line-height:100%; outline:none;text-decoration:none;-ms-interpolation-mode:bicubic; }
  2381. p { display:block;margin:13px 0; }
  2382. </style>
  2383. <!--[if mso]>
  2384. <xml>
  2385. <o:OfficeDocumentSettings>
  2386. <o:AllowPNG/>
  2387. <o:PixelsPerInch>96</o:PixelsPerInch>
  2388. </o:OfficeDocumentSettings>
  2389. </xml>
  2390. <![endif]-->
  2391. <!--[if lte mso 11]>
  2392. <style type="text/css">
  2393. .mj-outlook-group-fix { width:100% !important; }
  2394. </style>
  2395. <![endif]-->
  2396. <!--[if !mso]><!-->
  2397. <link href="https://fonts.googleapis.com/css2?family=Open+Sans:wght@300;400;500;600;700&display=swap" rel="stylesheet" type="text/css">
  2398. <link href="https://fonts.googleapis.com/css?family=Ubuntu:300,400,500,700" rel="stylesheet" type="text/css">
  2399. <style type="text/css">
  2400. @import url(https://fonts.googleapis.com/css2?family=Open+Sans:wght@300;400;500;600;700&display=swap);
  2401. @import url(https://fonts.googleapis.com/css?family=Ubuntu:300,400,500,700);
  2402. </style>
  2403. <!--<![endif]-->
  2404. <style type="text/css">
  2405. @media only screen and (min-width:100px) {
  2406. .mj-column-px-130 { width:130px !important; max-width: 130px; }
  2407. .mj-column-per-50 { width:50% !important; max-width: 50%; }
  2408. .mj-column-per-70 { width:70% !important; max-width: 70%; }
  2409. .mj-column-per-30 { width:30% !important; max-width: 30%; }
  2410. .mj-column-per-100 { width:100% !important; max-width: 100%; }
  2411. .mj-column-px-66 { width:66px !important; max-width: 66px; }
  2412. .mj-column-px-400 { width:400px !important; max-width: 400px; }
  2413. }
  2414. </style>
  2415. <style type="text/css">
  2416. @media only screen and (max-width:100px) {
  2417. table.mj-full-width-mobile { width: 100% !important; }
  2418. td.mj-full-width-mobile { width: auto !important; }
  2419. }
  2420. </style>
  2421. </head>
  2422. <body style="word-spacing:normal;">
  2423. <div class="svgbg" style="background-image: url('https://staging.netdata.cloud/static/email/img/isotype_600.png'); background-repeat: no-repeat; background-position: top center; background-size: 600px 192px;">
  2424. <!--[if mso | IE]><table align="center" border="0" cellpadding="0" cellspacing="0" class="" style="width:600px;" width="600" ><tr><td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]-->
  2425. <div style="margin:0px auto;max-width:600px;">
  2426. <table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width:100%;">
  2427. <tbody>
  2428. <tr>
  2429. <td style="direction:ltr;font-size:0px;padding:20px 0;padding-bottom:50px;padding-left:0;text-align:left;">
  2430. <!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td class="" style="vertical-align:top;width:130px;" ><![endif]-->
  2431. <div class="mj-column-px-130 mj-outlook-group-fix" style="font-size:0px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:130px;">
  2432. <table border="0" cellpadding="0" cellspacing="0" role="presentation" style="vertical-align:top;" width="100%">
  2433. <tbody>
  2434. <tr>
  2435. <td align="center" style="font-size:0px;padding:10px 25px;padding-right:0;padding-left:0;word-break:break-word;">
  2436. <table border="0" cellpadding="0" cellspacing="0" role="presentation" style="border-collapse:collapse;border-spacing:0px;">
  2437. <tbody>
  2438. <tr>
  2439. <td style="width:130px;">
  2440. <img alt="Netdata Logo" height="auto" src="https://app.netdata.cloud/static/email/img/full_logo.png" style="border:0;display:block;outline:none;text-decoration:none;height:auto;width:100%;font-size:13px;" width="130">
  2441. </td>
  2442. </tr>
  2443. </tbody>
  2444. </table>
  2445. </td>
  2446. </tr>
  2447. </tbody>
  2448. </table>
  2449. </div>
  2450. <!--[if mso | IE]></td><td class="" style="vertical-align:top;width:300px;" ><![endif]-->
  2451. <div class="mj-column-per-50 mj-outlook-group-fix" style="font-size:0px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:50%;">
  2452. <table border="0" cellpadding="0" cellspacing="0" role="presentation" width="100%">
  2453. <tbody>
  2454. <tr>
  2455. <td style="vertical-align:top;padding-top:4px;">
  2456. <table border="0" cellpadding="0" cellspacing="0" role="presentation" style width="100%">
  2457. <tbody>
  2458. <tr>
  2459. <td align="left" style="font-size:0px;padding:10px 25px;padding-left:10px;word-break:break-word;">
  2460. <div style="font-family:Open Sans, sans-serif;font-size:16px;line-height:1;text-align:left;color:#35414A;">Notification</div>
  2461. </td>
  2462. </tr>
  2463. </tbody>
  2464. </table>
  2465. </td>
  2466. </tr>
  2467. </tbody>
  2468. </table>
  2469. </div>
  2470. <!--[if mso | IE]></td></tr></table><![endif]-->
  2471. </td>
  2472. </tr>
  2473. </tbody>
  2474. </table>
  2475. </div>
  2476. <!--[if mso | IE]></td></tr></table><table align="center" border="0" cellpadding="0" cellspacing="0" class="no-collapse-outlook" style="width:600px;" width="600" ><tr><td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]-->
  2477. <div class="no-collapse" style="border-collapse: initial; margin: 0px auto; border-radius: 4px; max-width: 600px;">
  2478. <table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width:100%;border-radius:4px;">
  2479. <tbody>
  2480. <tr>
  2481. <td style="border:1px solid ${border_color};direction:ltr;font-size:0px;padding:20px 0;text-align:center;">
  2482. <!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td class="set-font-outlook" width="600px" ><table align="center" border="0" cellpadding="0" cellspacing="0" class="set-font-outlook" style="width:598px;" width="598" ><tr><td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]-->
  2483. <div class="set-font" style="font-family: 'IBM Plex Sans', sans-serif; margin: 0px auto; max-width: 598px;">
  2484. <table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width:100%;">
  2485. <tbody>
  2486. <tr>
  2487. <td style="direction:ltr;font-size:0px;padding:20px 0;padding-bottom:0;padding-top:0;text-align:center;">
  2488. <!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td class="" style="vertical-align:top;width:418.6px;" ><![endif]-->
  2489. <div class="mj-column-per-70 mj-outlook-group-fix" style="font-size:0px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:70%;">
  2490. <table border="0" cellpadding="0" cellspacing="0" role="presentation" style="vertical-align:top;" width="100%">
  2491. <tbody>
  2492. <tr>
  2493. <td align="left" style="font-size:0px;padding:10px 25px;padding-top:15px;word-break:break-word;">
  2494. <div style="font-family:Open Sans, sans-serif;font-size:20px;font-weight:700;line-height:1;text-align:left;color:#35414A;">${summary}</div>
  2495. </td>
  2496. </tr>
  2497. </tbody>
  2498. </table>
  2499. </div>
  2500. <!--[if mso | IE]></td><td class="" style="vertical-align:top;width:179.4px;" ><![endif]-->
  2501. <div class="mj-column-per-30 mj-outlook-group-fix" style="font-size:0px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:30%;">
  2502. <table border="0" cellpadding="0" cellspacing="0" role="presentation" style="vertical-align:top;" width="100%">
  2503. <tbody>
  2504. <tr>
  2505. <td align="right" style="font-size:0px;padding:10px 25px;padding-right:25px;word-break:break-word;">
  2506. <table border="0" cellpadding="0" cellspacing="0" role="presentation" style="border-collapse:collapse;border-spacing:0px;">
  2507. <tbody>
  2508. <tr>
  2509. <td style="width:100px;">
  2510. <img height="auto" src="${alarm_badge}" style="border:0;display:block;outline:none;text-decoration:none;height:auto;width:100%;font-size:13px;" width="100"/>
  2511. </td>
  2512. </tr>
  2513. </tbody>
  2514. </table>
  2515. </td>
  2516. </tr>
  2517. </tbody>
  2518. </table>
  2519. </div>
  2520. <!--[if mso | IE]></td></tr></table><![endif]-->
  2521. </td>
  2522. </tr>
  2523. </tbody>
  2524. </table>
  2525. </div>
  2526. <!--[if mso | IE]></td></tr></table></td></tr><tr><td class="set-font-outlook" width="600px" ><table align="center" border="0" cellpadding="0" cellspacing="0" class="set-font-outlook" style="width:598px;" width="598" ><tr><td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]-->
  2527. <div class="set-font" style="font-family: 'IBM Plex Sans', sans-serif; margin: 0px auto; max-width: 598px;">
  2528. <table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width:100%;">
  2529. <tbody>
  2530. <tr>
  2531. <td style="direction:ltr;font-size:0px;padding:0;text-align:center;">
  2532. <!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td class="" style="vertical-align:top;width:598px;" ><![endif]-->
  2533. <div class="mj-column-per-100 mj-outlook-group-fix" style="font-size:0px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:100%;">
  2534. <table border="0" cellpadding="0" cellspacing="0" role="presentation" style="vertical-align:top;" width="100%">
  2535. <tbody>
  2536. <tr>
  2537. <td align="left" style="font-size:0px;padding:10px 25px;padding-top:0;word-break:break-word;">
  2538. <div style="font-family:IBM Plex Sans, sans-serif;font-size:16px;line-height:1;text-align:left;color:#35414A;">on ${host}</div>
  2539. </td>
  2540. </tr>
  2541. </tbody>
  2542. </table>
  2543. </div>
  2544. <!--[if mso | IE]></td></tr></table><![endif]-->
  2545. </td>
  2546. </tr>
  2547. </tbody>
  2548. </table>
  2549. </div>
  2550. <!--[if mso | IE]></td></tr></table></td></tr><tr><td class="set-font-outlook" width="600px" ><table align="center" border="0" cellpadding="0" cellspacing="0" class="set-font-outlook" style="width:598px;" width="598" ><tr><td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]-->
  2551. <div class="set-font" style="font-family: 'IBM Plex Sans', sans-serif; margin: 0px auto; max-width: 598px;">
  2552. <table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width:100%;">
  2553. <tbody>
  2554. <tr>
  2555. <td style="direction:ltr;font-size:0px;padding:20px 0;text-align:center;">
  2556. <!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td class="" style="vertical-align:top;width:598px;" ><![endif]-->
  2557. <div class="mj-column-per-100 mj-outlook-group-fix" style="font-size:0px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:100%;">
  2558. <table border="0" cellpadding="0" cellspacing="0" role="presentation" style="vertical-align:top;" width="100%">
  2559. <tbody>
  2560. <tr>
  2561. <td align="left" style="font-size:0px;padding:10px 25px;padding-top:0;word-break:break-word;">
  2562. <div style="font-family:Open Sans, sans-serif;font-size:26px;font-weight:700;line-height:1;text-align:left;color:#35414A;"><span style="color: ${text_color}; font-size:26px; background: ${background_color}; padding:4px 24px; border-radius: 36px">${value_string}
  2563. </span></div>
  2564. </td>
  2565. </tr>
  2566. </tbody>
  2567. </table>
  2568. </div>
  2569. <!--[if mso | IE]></td></tr></table><![endif]-->
  2570. </td>
  2571. </tr>
  2572. </tbody>
  2573. </table>
  2574. </div>
  2575. <!--[if mso | IE]></td></tr></table></td></tr><tr><td class="set-font-outlook" width="600px" ><table align="center" border="0" cellpadding="0" cellspacing="0" class="set-font-outlook" style="width:598px;" width="598" ><tr><td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]-->
  2576. <div class="set-font" style="font-family: 'IBM Plex Sans', sans-serif; margin: 0px auto; max-width: 598px;">
  2577. <table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width:100%;">
  2578. <tbody>
  2579. <tr>
  2580. <td style="direction:ltr;font-size:0px;padding:20px 0;padding-bottom:0;padding-top:0;text-align:center;">
  2581. <!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td class="" style="vertical-align:top;width:598px;" ><![endif]-->
  2582. <div class="mj-column-per-100 mj-outlook-group-fix" style="font-size:0px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:100%;">
  2583. <table border="0" cellpadding="0" cellspacing="0" role="presentation" style="vertical-align:top;" width="100%">
  2584. <tbody>
  2585. <tr>
  2586. <td align="left" style="font-size:0px;padding:10px 25px;padding-top:0;word-break:break-word;">
  2587. <div style="font-family:Open Sans, sans-serif;font-size:16px;line-height:21px;text-align:left;color:#35414A;">Details: ${info}</div>
  2588. </td>
  2589. </tr>
  2590. </tbody>
  2591. </table>
  2592. </div>
  2593. <!--[if mso | IE]></td></tr></table><![endif]-->
  2594. </td>
  2595. </tr>
  2596. </tbody>
  2597. </table>
  2598. </div>
  2599. <!--[if mso | IE]></td></tr></table></td></tr><tr><td class="set-font-outlook" width="600px" ><table align="center" border="0" cellpadding="0" cellspacing="0" class="set-font-outlook" style="width:598px;" width="598" ><tr><td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]-->
  2600. <div class="set-font" style="font-family: 'IBM Plex Sans', sans-serif; margin: 0px auto; max-width: 598px;">
  2601. <table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width:100%;">
  2602. <tbody>
  2603. <tr>
  2604. <td style="direction:ltr;font-size:0px;padding:20px 0;padding-bottom:0;padding-top:0;text-align:center;">
  2605. <!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td class="" style="vertical-align:top;width:598px;" ><![endif]-->
  2606. <div class="mj-column-per-100 mj-outlook-group-fix" style="font-size:0px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:100%;">
  2607. <table border="0" cellpadding="0" cellspacing="0" role="presentation" style="vertical-align:top;" width="100%">
  2608. <tbody>
  2609. <tr>
  2610. <td align="center" vertical-align="middle" style="font-size:0px;padding:10px 25px;word-break:break-word;">
  2611. <table border="0" cellpadding="0" cellspacing="0" role="presentation" style="border-collapse:separate;width:100%;line-height:100%;">
  2612. <tr>
  2613. <td
  2614. align="center" bgcolor="${border_color}" role="presentation" style="border:none;border-radius:3px;cursor:auto;height:44px;background:${border_color};" valign="middle">
  2615. <p style="display:block;background:${border_color};color:#ffffff;font-size:13px;font-weight:600;line-height:44px;margin:0;text-decoration:none;text-transform:none;mso-padding-alt:0px;border-radius:3px;">
  2616. <a href="${goto_url}" style="color: ${action_text_color}; text-decoration: none; width: 100%; display: inline-block">GO TO CHART</a>
  2617. </p>
  2618. </td>
  2619. </tr>
  2620. </table>
  2621. </td>
  2622. </tr>
  2623. </tbody>
  2624. </table>
  2625. </div>
  2626. <!--[if mso | IE]></td></tr></table><![endif]-->
  2627. </td>
  2628. </tr>
  2629. </tbody>
  2630. </table>
  2631. </div>
  2632. <!--[if mso | IE]></td></tr></table></td></tr></table><![endif]-->
  2633. </td>
  2634. </tr>
  2635. </tbody>
  2636. </table>
  2637. </div>
  2638. <!--[if mso | IE]></td></tr></table><![endif]-->
  2639. <div style="height:32px;line-height:32px;">&#8202;</div>
  2640. <!--[if mso | IE]><table align="center" border="0" cellpadding="0" cellspacing="0" class="set-font-outlook" style="width:600px;" width="600" ><tr><td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]-->
  2641. <div class="set-font" style="font-family: 'IBM Plex Sans', sans-serif; background: ${background_color}; background-color: ${background_color}; margin: 0px auto; border-radius: 4px; max-width: 600px;">
  2642. <table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="background:${background_color};background-color:${background_color};width:100%;border-radius:4px;">
  2643. <tbody>
  2644. <tr>
  2645. <td style="direction:ltr;font-size:0px;padding:20px 0;text-align:center;">
  2646. <!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td class="" style="vertical-align:top;width:600px;" ><![endif]-->
  2647. <div class="mj-column-per-100 mj-outlook-group-fix" style="font-size:0px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:100%;">
  2648. <table border="0" cellpadding="0" cellspacing="0" role="presentation" style="vertical-align:top;" width="100%">
  2649. <tbody>
  2650. <tr>
  2651. <td align="left" style="font-size:0px;padding:10px 25px;padding-bottom:6px;word-break:break-word;">
  2652. <div style="font-family:Open Sans, sans-serif;font-size:18px;line-height:1;text-align:left;color:#35414A;">Alert:
  2653. <span style="font-weight:700; font-size:20px">${name}</span></div>
  2654. </td>
  2655. </tr>
  2656. <tr>
  2657. <td align="left" style="font-size:0px;padding:10px 25px;padding-top:0;word-break:break-word;">
  2658. <div style="font-family:Open Sans, sans-serif;font-size:18px;line-height:1;text-align:left;color:#35414A;">Chart:
  2659. <span style="font-weight:700; font-size:20px">${chart}</span></div>
  2660. </td>
  2661. </tr>
  2662. <tr>
  2663. <td align="left" style="font-size:0px;padding:10px 25px;padding-top:4px;word-break:break-word;">
  2664. <div style="font-family:Open Sans, sans-serif;font-size:14px;line-height:1;text-align:left;color:#35414A;">${rich_status_raised_for}</div>
  2665. </td>
  2666. </tr>
  2667. <tr>
  2668. <td align="center" style="font-size:0px;padding:10px 25px;word-break:break-word;">
  2669. <p style="border-top:solid 1px lightgrey;font-size:1px;margin:0px auto;width:100%;">
  2670. </p>
  2671. <!--[if mso | IE]><table align="center" border="0" cellpadding="0" cellspacing="0" style="border-top:solid 1px lightgrey;font-size:1px;margin:0px auto;width:550px;" role="presentation" width="550px" ><tr><td style="height:0;line-height:0;"> &nbsp;
  2672. </td></tr></table><![endif]-->
  2673. </td>
  2674. </tr>
  2675. <tr>
  2676. <td align="left" style="font-size:0px;padding:10px 25px;padding-bottom:6px;word-break:break-word;">
  2677. <div style="font-family:Open Sans, sans-serif;font-size:16px;line-height:1;text-align:left;color:#35414A;">On
  2678. <span style="font-weight:600">${date}</span></div>
  2679. </td>
  2680. </tr>
  2681. <tr>
  2682. <td align="left" style="font-size:0px;padding:10px 25px;padding-top:0;word-break:break-word;">
  2683. <div style="font-family:Open Sans, sans-serif;font-size:16px;line-height:1;text-align:left;color:#35414A;">By:
  2684. <span style="font-weight:600">${host}</span></div>
  2685. </td>
  2686. </tr>
  2687. <tr>
  2688. <td align="left" style="font-size:0px;padding:10px 25px;padding-top:4px;word-break:break-word;">
  2689. <div style="font-family:Open Sans, sans-serif;font-size:14px;line-height:1;text-align:left;color:#35414A;">Global time:
  2690. <span style="font-weight:600">${date_utc}</span></div>
  2691. </td>
  2692. </tr>
  2693. <tr>
  2694. <td align="center" style="font-size:0px;padding:10px 25px;word-break:break-word;">
  2695. <p style="border-top:solid 1px lightgrey;font-size:1px;margin:0px auto;width:100%;">
  2696. </p>
  2697. <!--[if mso | IE]><table align="center" border="0" cellpadding="0" cellspacing="0" style="border-top:solid 1px lightgrey;font-size:1px;margin:0px auto;width:550px;" role="presentation" width="550px" ><tr><td style="height:0;line-height:0;"> &nbsp;
  2698. </td></tr></table><![endif]-->
  2699. </td>
  2700. </tr>
  2701. <tr>
  2702. <td align="left" style="font-size:0px;padding:10px 25px;padding-bottom:6px;word-break:break-word;">
  2703. <div style="font-family:Open Sans, sans-serif;font-size:16px;line-height:1;text-align:left;color:#35414A;">Classification:
  2704. <span style="font-weight:600">${classification}</span></div>
  2705. </td>
  2706. </tr>
  2707. <tr>
  2708. <td align="left" style="font-size:0px;padding:10px 25px;padding-top:0;word-break:break-word;">
  2709. <div style="font-family:Open Sans, sans-serif;font-size:16px;line-height:1;text-align:left;color:#35414A;">Role:
  2710. <span style="font-weight:600">${roles}</span></div>
  2711. </td>
  2712. </tr>
  2713. </tbody>
  2714. </table>
  2715. </div>
  2716. <!--[if mso | IE]></td></tr></table><![endif]-->
  2717. </td>
  2718. </tr>
  2719. </tbody>
  2720. </table>
  2721. </div>
  2722. <!--[if mso | IE]></td></tr></table><table align="center" border="0" cellpadding="0" cellspacing="0" class="set-font-outlook" style="width:600px;" width="600" ><tr><td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]-->
  2723. <div class="set-font" style="font-family: 'IBM Plex Sans', sans-serif; margin: 0px auto; max-width: 600px;">
  2724. <table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width:100%;">
  2725. <tbody>
  2726. <tr>
  2727. <td style="direction:ltr;font-size:0px;padding:20px 0;padding-left:25px;text-align:left;">
  2728. <!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td class="" style="vertical-align:top;width:66px;" ><![endif]-->
  2729. <div class="mj-column-px-66 mj-outlook-group-fix" style="font-size:0px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:66px;">
  2730. <table border="0" cellpadding="0" cellspacing="0" role="presentation" width="100%">
  2731. <tbody>
  2732. <tr>
  2733. <td style="vertical-align:top;padding:0;">
  2734. <table border="0" cellpadding="0" cellspacing="0" role="presentation" style width="100%">
  2735. <tbody>
  2736. <tr>
  2737. <td align="left" style="font-size:0px;padding:10px 25px;padding-right:0;padding-left:0;word-break:break-word;">
  2738. <table border="0" cellpadding="0" cellspacing="0" role="presentation" style="border-collapse:collapse;border-spacing:0px;">
  2739. <tbody>
  2740. <tr>
  2741. <td style="width:48px;">
  2742. <img height="auto" src="https://app.netdata.cloud/static/email/img/community_icon.png" style="border:0;display:block;outline:none;text-decoration:none;height:auto;width:100%;font-size:13px;" width="48">
  2743. </td>
  2744. </tr>
  2745. </tbody>
  2746. </table>
  2747. </td>
  2748. </tr>
  2749. </tbody>
  2750. </table>
  2751. </td>
  2752. </tr>
  2753. </tbody>
  2754. </table>
  2755. </div>
  2756. <!--[if mso | IE]></td><td align="left" class="" style="vertical-align:top;width:400px;" ><![endif]-->
  2757. <div class="mj-column-px-400 mj-outlook-group-fix" style="font-size:0px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:400px;">
  2758. <table border="0" cellpadding="0" cellspacing="0" role="presentation" width="100%">
  2759. <tbody>
  2760. <tr>
  2761. <td style="vertical-align:top;padding-left:0;">
  2762. <table border="0" cellpadding="0" cellspacing="0" role="presentation" style width="100%">
  2763. <tbody>
  2764. <tr>
  2765. <td align="left" style="font-size:0px;padding:10px 25px;padding-left:0;word-break:break-word;">
  2766. <div style="font-family:Open Sans, sans-serif;font-size:16px;font-weight:700;line-height:1;text-align:left;color:#35414A;">Want to know more about this alert?</div>
  2767. </td>
  2768. </tr>
  2769. <tr>
  2770. <td align="left" style="font-size:0px;padding:10px 25px;padding-left:0;word-break:break-word;">
  2771. <div style="font-family:Open Sans, sans-serif;font-size:14px;line-height:1.3;text-align:left;color:#35414A;">Join the troubleshooting discussion for this alert on our <a href="https://community.netdata.cloud/t/${name//[._]/-}" class="link" style="color: #00AB44; text-decoration: none;">community forums</a>.</div>
  2772. </td>
  2773. </tr>
  2774. </tbody>
  2775. </table>
  2776. </td>
  2777. </tr>
  2778. </tbody>
  2779. </table>
  2780. </div>
  2781. <!--[if mso | IE]></td></tr></table><![endif]-->
  2782. </td>
  2783. </tr>
  2784. </tbody>
  2785. </table>
  2786. </div>
  2787. <!--[if mso | IE]></td></tr></table><table align="center" border="0" cellpadding="0" cellspacing="0" class="set-font-outlook" style="width:600px;" width="600" ><tr><td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]-->
  2788. <div class="set-font" style="font-family: 'IBM Plex Sans', sans-serif; margin: 0px auto; max-width: 600px;">
  2789. <table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width:100%;">
  2790. <tbody>
  2791. <tr>
  2792. <td style="direction:ltr;font-size:0px;padding:20px 0;padding-left:25px;text-align:left;">
  2793. <!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td class="" style="vertical-align:top;width:66px;" ><![endif]-->
  2794. <div class="mj-column-px-66 mj-outlook-group-fix" style="font-size:0px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:66px;">
  2795. <table border="0" cellpadding="0" cellspacing="0" role="presentation" width="100%">
  2796. <tbody>
  2797. <tr>
  2798. <td style="vertical-align:top;padding:0;">
  2799. <table border="0" cellpadding="0" cellspacing="0" role="presentation" style width="100%">
  2800. <tbody>
  2801. <tr>
  2802. <td align="left" style="font-size:0px;padding:10px 25px;padding-right:0;padding-left:0;word-break:break-word;">
  2803. <table border="0" cellpadding="0" cellspacing="0" role="presentation" style="border-collapse:collapse;border-spacing:0px;">
  2804. <tbody>
  2805. <tr>
  2806. <td style="width:48px;">
  2807. <img height="auto" src="https://app.netdata.cloud/static/email/img/configure_icon.png" style="border:0;display:block;outline:none;text-decoration:none;height:auto;width:100%;font-size:13px;" width="48">
  2808. </td>
  2809. </tr>
  2810. </tbody>
  2811. </table>
  2812. </td>
  2813. </tr>
  2814. </tbody>
  2815. </table>
  2816. </td>
  2817. </tr>
  2818. </tbody>
  2819. </table>
  2820. </div>
  2821. <!--[if mso | IE]></td><td align="left" class="" style="vertical-align:top;width:400px;" ><![endif]-->
  2822. <div class="mj-column-px-400 mj-outlook-group-fix" style="font-size:0px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:400px;">
  2823. <table border="0" cellpadding="0" cellspacing="0" role="presentation" width="100%">
  2824. <tbody>
  2825. <tr>
  2826. <td style="vertical-align:top;padding-left:0;">
  2827. <table border="0" cellpadding="0" cellspacing="0" role="presentation" style width="100%">
  2828. <tbody>
  2829. <tr>
  2830. <td align="left" style="font-size:0px;padding:10px 25px;padding-left:0;word-break:break-word;">
  2831. <div style="font-family:Open Sans, sans-serif;font-size:16px;font-weight:700;line-height:1;text-align:left;color:#35414A;">Need to configure this alert?</div>
  2832. </td>
  2833. </tr>
  2834. <tr>
  2835. <td align="left" style="font-size:0px;padding:10px 25px;padding-left:0;word-break:break-word;">
  2836. <div style="font-family:Open Sans, sans-serif;font-size:14px;line-height:1.3;text-align:left;color:#35414A;"><span style="color: #00AB44"><a href="https://learn.netdata.cloud/docs/agent/health/notifications#:~:text=To%20edit%20it%20on%20your,have%20one%20or%20more%20destinations" class="link" style="color: #00AB44; text-decoration: none;">Edit</a></span> this alert's configuration file by logging into $s_host and running the following command:</div>
  2837. </td>
  2838. </tr>
  2839. <tr>
  2840. <td align="left" style="font-size:0px;padding:10px 25px;padding-top:8px;padding-left:0;word-break:break-word;">
  2841. <div style="font-family:Open Sans, sans-serif;font-size:12px;line-height:1.3;text-align:left;color:#35414A;">${edit_command} <br>
  2842. <br>The alarm to edit is at line ${line}</div>
  2843. </td>
  2844. </tr>
  2845. </tbody>
  2846. </table>
  2847. </td>
  2848. </tr>
  2849. </tbody>
  2850. </table>
  2851. </div>
  2852. <!--[if mso | IE]></td></tr></table><![endif]-->
  2853. </td>
  2854. </tr>
  2855. </tbody>
  2856. </table>
  2857. </div>
  2858. <!--[if mso | IE]></td></tr></table><table align="center" border="0" cellpadding="0" cellspacing="0" class="history-wrapper-outlook" style="width:600px;" width="600" ><tr><td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]-->
  2859. <div class="history-wrapper" style="background: #F7F8F8; background-color: #F7F8F8; margin: 0px auto; max-width: 100%;">
  2860. <table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="background:#F7F8F8;background-color:#F7F8F8;width:100%;">
  2861. <tbody>
  2862. <tr>
  2863. <td style="direction:ltr;font-size:0px;padding:0;padding-bottom:24px;text-align:center;">
  2864. <!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td class="set-font-outlook" width="600px" ><table align="center" border="0" cellpadding="0" cellspacing="0" class="set-font-outlook" style="width:600px;" width="600" ><tr><td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]-->
  2865. <div class="set-font" style="font-family: 'IBM Plex Sans', sans-serif; margin: 0px auto; max-width: 600px;">
  2866. <table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width:100%;">
  2867. <tbody>
  2868. <tr>
  2869. <td style="direction:ltr;font-size:0px;padding:20px 0;padding-bottom:12px;text-align:center;">
  2870. <!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td class="" style="vertical-align:top;width:600px;" ><![endif]-->
  2871. <div class="mj-column-per-100 mj-outlook-group-fix" style="font-size:0px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:100%;">
  2872. <table border="0" cellpadding="0" cellspacing="0" role="presentation" style="vertical-align:top;" width="100%">
  2873. <tbody>
  2874. <tr>
  2875. <td align="left" style="font-size:0px;padding:10px 25px;word-break:break-word;">
  2876. <div style="font-family:Open Sans, sans-serif;font-size:16px;line-height:1;text-align:center;color:#35414A;">The node has
  2877. <span style="font-weight:600">${total_warnings} warning</span>
  2878. and
  2879. <span style="font-weight:600">${total_critical} critical</span>
  2880. additional active alert(s)</div>
  2881. </td>
  2882. </tr>
  2883. <td align="left" style="font-size:0px;padding:10px 25px;word-break:break-word;">
  2884. <div style="font-family:Open Sans, sans-serif;font-size:12px;line-height:1;text-align:center;color:#35414A;">${EXTRA_ALARMS_LIST_TEXT}</div>
  2885. </td>
  2886. </tr>
  2887. </tbody>
  2888. </table>
  2889. </div>
  2890. <!--[if mso | IE]></td></tr></table><![endif]-->
  2891. </td>
  2892. </tr>
  2893. </tbody>
  2894. </table>
  2895. </div>
  2896. ${CRIT_ALARMS}
  2897. ${WARN_ALARMS}
  2898. <!--[if mso | IE]></td></tr></table></td></tr></table><![endif]-->
  2899. </td>
  2900. </tr>
  2901. </tbody>
  2902. </table>
  2903. </div>
  2904. <!--[if mso | IE]></td></tr></table><table align="center" border="0" cellpadding="0" cellspacing="0" class="set-font-outlook" style="width:600px;" width="600" ><tr><td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]-->
  2905. <div class="set-font" style="font-family: 'IBM Plex Sans', sans-serif; margin: 0px auto; max-width: 600px;">
  2906. <table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width:100%;">
  2907. <tbody>
  2908. <tr>
  2909. <td style="direction:ltr;font-size:0px;padding:20px 0;text-align:center;">
  2910. <!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td class="" style="vertical-align:top;width:600px;" ><![endif]-->
  2911. <div class="mj-column-per-100 mj-outlook-group-fix" style="font-size:0px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:100%;">
  2912. <table border="0" cellpadding="0" cellspacing="0" role="presentation" width="100%">
  2913. <tbody>
  2914. <tr>
  2915. <td style="vertical-align:top;padding-top:44px;padding-bottom:12px;">
  2916. <table border="0" cellpadding="0" cellspacing="0" role="presentation" style width="100%">
  2917. <tbody>
  2918. <tr>
  2919. <td align="left" style="font-size:0px;padding:10px 25px;padding-top:0;padding-bottom:0;word-break:break-word;">
  2920. <div style="font-family:Open Sans, sans-serif;font-size:13px;line-height:1;text-align:center;color:#35414A;">© Netdata $(date +'%Y') - The real-time performance and health monitoring</div>
  2921. </td>
  2922. </tr>
  2923. </tbody>
  2924. </table>
  2925. </td>
  2926. </tr>
  2927. </tbody>
  2928. </table>
  2929. </div>
  2930. <!--[if mso | IE]></td></tr></table><![endif]-->
  2931. </td>
  2932. </tr>
  2933. </tbody>
  2934. </table>
  2935. </div>
  2936. <!--[if mso | IE]></td></tr></table><![endif]-->
  2937. </div>
  2938. </body>
  2939. </html>
  2940. EOF
  2941. send_email <<EOF
  2942. To: ${to_email}
  2943. Subject: ${html_email_subject}
  2944. MIME-Version: 1.0
  2945. Content-Type: multipart/alternative; boundary="multipart-boundary"
  2946. ${email_thread_headers}
  2947. X-Netdata-Severity: ${status,,}
  2948. X-Netdata-Alert-Name: $name
  2949. X-Netdata-Chart: $chart
  2950. X-Netdata-Classification: $classification
  2951. X-Netdata-Host: $host
  2952. X-Netdata-Role: $roles
  2953. This is a MIME-encoded multipart message
  2954. --multipart-boundary
  2955. ${email_plaintext_part}
  2956. --multipart-boundary
  2957. ${email_html_part}
  2958. --multipart-boundary--
  2959. EOF
  2960. fi
  2961. SENT_EMAIL=$?
  2962. # -----------------------------------------------------------------------------
  2963. # send the EVENT to Dynatrace
  2964. send_dynatrace "${host}" "${chart}" "${name}" "${status}"
  2965. SENT_DYNATRACE=$?
  2966. # -----------------------------------------------------------------------------
  2967. # send messages to Opsgenie
  2968. send_opsgenie
  2969. SENT_OPSGENIE=$?
  2970. # -----------------------------------------------------------------------------
  2971. # send messages to Gotify
  2972. send_gotify
  2973. SENT_GOTIFY=$?
  2974. # -----------------------------------------------------------------------------
  2975. # send messages to ntfy
  2976. send_ntfy "${DEFAULT_RECIPIENT_NTFY}"
  2977. SENT_NTFY=$?
  2978. # -----------------------------------------------------------------------------
  2979. # let netdata know
  2980. for state in "${SENT_EMAIL}" \
  2981. "${SENT_PUSHOVER}" \
  2982. "${SENT_TELEGRAM}" \
  2983. "${SENT_SLACK}" \
  2984. "${SENT_ROCKETCHAT}" \
  2985. "${SENT_ALERTA}" \
  2986. "${SENT_FLOCK}" \
  2987. "${SENT_DISCORD}" \
  2988. "${SENT_TWILIO}" \
  2989. "${SENT_HIPCHAT}" \
  2990. "${SENT_MESSAGEBIRD}" \
  2991. "${SENT_KAVENEGAR}" \
  2992. "${SENT_PUSHBULLET}" \
  2993. "${SENT_KAFKA}" \
  2994. "${SENT_PD}" \
  2995. "${SENT_FLEEP}" \
  2996. "${SENT_PROWL}" \
  2997. "${SENT_CUSTOM}" \
  2998. "${SENT_IRC}" \
  2999. "${SENT_AWSSNS}" \
  3000. "${SENT_MATRIX}" \
  3001. "${SENT_SYSLOG}" \
  3002. "${SENT_SMS}" \
  3003. "${SENT_MSTEAMS}" \
  3004. "${SENT_DYNATRACE}" \
  3005. "${SENT_OPSGENIE}" \
  3006. "${SENT_GOTIFY}" \
  3007. "${SENT_NTFY}"; do
  3008. if [ "${state}" -eq 0 ]; then
  3009. # we sent something
  3010. exit 0
  3011. fi
  3012. done
  3013. # we did not send anything
  3014. exit 1