alarm-notify.sh.in 138 KB

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