alarm-notify.sh.in 131 KB

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