alarm-notify.sh.in 137 KB

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