alarm-notify.sh.in 139 KB

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