├── .github └── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── .gitignore ├── .mdl_style.rb ├── .mdlrc ├── .travis.yml ├── CACTI-THOLD-MIB ├── CHANGELOG.md ├── INFO ├── LICENSE ├── README.md ├── cli_import.php ├── cli_thresholds.php ├── extras ├── apply_realms.php ├── index.php └── upgrade.php ├── images ├── disable_thold.png ├── edit_object.png ├── enable_thold.png ├── graph_create_thold.png ├── index.php ├── reddot.png ├── tab_thold.gif ├── tab_thold_down.gif ├── view_graphs.gif └── view_log.gif ├── includes ├── arrays.php ├── database.php ├── index.php ├── polling.php ├── settings.php └── tab.php ├── index.php ├── locales ├── LC_MESSAGES │ ├── ar-SA.mo │ ├── bg-BG.mo │ ├── de-DE.mo │ ├── el-GR.mo │ ├── es-ES.mo │ ├── fr-FR.mo │ ├── he-IL.mo │ ├── hi-IN.mo │ ├── index.php │ ├── it-IT.mo │ ├── ja-JP.mo │ ├── ko-KR.mo │ ├── nl-NL.mo │ ├── pl-PL.mo │ ├── pt-BR.mo │ ├── pt-PT.mo │ ├── ru-RU.mo │ ├── sv-SE.mo │ ├── tr-TR.mo │ ├── uk-UA.mo │ ├── uk.mo │ ├── vi-VN.mo │ ├── zh-CN.mo │ └── zh-TW.mo ├── build_gettext.sh ├── index.php └── po │ ├── ar-SA.po │ ├── bg-BG.po │ ├── cacti.pot │ ├── de-DE.po │ ├── el-GR.po │ ├── es-ES.po │ ├── fr-FR.po │ ├── he-IL.po │ ├── hi-IN.po │ ├── index.php │ ├── it-IT.po │ ├── ja-JP.po │ ├── ko-KR.po │ ├── nl-NL.po │ ├── pl-PL.po │ ├── pt-BR.po │ ├── pt-PT.po │ ├── ru-RU.po │ ├── sv-SE.po │ ├── tr-TR.po │ ├── uk-UA.po │ ├── vi-VN.po │ ├── zh-CN.po │ └── zh-TW.po ├── notify_lists.php ├── notify_queue.php ├── poller_thold.php ├── service ├── README.md ├── index.php └── systemd │ ├── index.php │ └── thold_daemon.service ├── setup.php ├── themes ├── classic │ ├── index.php │ └── main.css ├── dark │ ├── index.php │ └── main.css ├── index.php ├── midwinter │ └── main.css ├── modern │ ├── index.php │ └── main.css ├── paper-plane │ ├── index.php │ └── main.css ├── paw │ ├── index.php │ └── main.css └── sunrise │ ├── index.php │ └── main.css ├── thold.php ├── thold_daemon.php ├── thold_functions.php ├── thold_graph.php ├── thold_notify.php ├── thold_process.php ├── thold_templates.php └── thold_webapi.php /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 14 | 15 | **Describe the bug** 16 | A clear and concise description of what the bug is. 17 | 18 | **To Reproduce** 19 | Steps to reproduce the behavior: 20 | 1. Go to '...' 21 | 2. Click on '....' 22 | 3. Scroll down to '....' 23 | 4. See error 24 | 25 | **Expected behavior** 26 | A clear and concise description of what you expected to happen. 27 | 28 | **Screenshots** 29 | If applicable, add screenshots to help explain your problem. 30 | 31 | **Plugin (please complete the following information):** 32 | - Version: [e.g. 1.2] 33 | - Source: [e.g. cacti.net, package, github] 34 | - Identifier: [e.g. apt/yum package name or github commit ref] 35 | 36 | **Desktop (please complete the following information):** 37 | - OS: [e.g. iOS] 38 | - Browser [e.g. chrome, safari] 39 | - Version [e.g. 22] 40 | 41 | **Smartphone (please complete the following information):** 42 | - Device: [e.g. iPhone6] 43 | - OS: [e.g. iOS8.1] 44 | - Browser [e.g. stock browser, safari] 45 | - Version [e.g. 22] 46 | 47 | **Additional context** 48 | Add any other context about the problem here. 49 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 14 | 15 | **Is your feature request related to a problem? Please describe.** 16 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 17 | 18 | **Describe the solution you'd like** 19 | A clear and concise description of what you want to happen. 20 | 21 | **Describe alternatives you've considered** 22 | A clear and concise description of any alternative solutions or features you've considered. 23 | 24 | **Additional context** 25 | Add any other context or screenshots about the feature request here. 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # +-------------------------------------------------------------------------+ 2 | # | Copyright (C) 2004-2023 The Cacti Group | 3 | # | | 4 | # | This program is free software; you can redistribute it and/or | 5 | # | modify it under the terms of the GNU General Public License | 6 | # | as published by the Free Software Foundation; either version 2 | 7 | # | of the License, or (at your option) any later version. | 8 | # | | 9 | # | This program is distributed in the hope that it will be useful, | 10 | # | but WITHOUT ANY WARRANTY; without even the implied warranty of | 11 | # | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 12 | # | GNU General Public License for more details. | 13 | # +-------------------------------------------------------------------------+ 14 | # | Cacti: The Complete RRDtool-based Graphing Solution | 15 | # +-------------------------------------------------------------------------+ 16 | # | This code is designed, written, and maintained by the Cacti Group. See | 17 | # | about.php and/or the AUTHORS file for specific developer information. | 18 | # +-------------------------------------------------------------------------+ 19 | # | http://www.cacti.net/ | 20 | # +-------------------------------------------------------------------------+ 21 | 22 | locales/po/*.mo 23 | -------------------------------------------------------------------------------- /.mdl_style.rb: -------------------------------------------------------------------------------- 1 | # +-------------------------------------------------------------------------+ 2 | # | Copyright (C) 2004-2023 The Cacti Group | 3 | # | | 4 | # | This program is free software; you can redistribute it and/or | 5 | # | modify it under the terms of the GNU General Public License | 6 | # | as published by the Free Software Foundation; either version 2 | 7 | # | of the License, or (at your option) any later version. | 8 | # | | 9 | # | This program is distributed in the hope that it will be useful, | 10 | # | but WITHOUT ANY WARRANTY; without even the implied warranty of | 11 | # | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 12 | # | GNU General Public License for more details. | 13 | # +-------------------------------------------------------------------------+ 14 | # | Cacti: The Complete RRDtool-based Graphing Solution | 15 | # +-------------------------------------------------------------------------+ 16 | # | This code is designed, written, and maintained by the Cacti Group. See | 17 | # | about.php and/or the AUTHORS file for specific developer information. | 18 | # +-------------------------------------------------------------------------+ 19 | # | http://www.cacti.net/ | 20 | # +-------------------------------------------------------------------------+ 21 | 22 | # customize style guide 23 | all 24 | rule "MD010", code_blocks: false 25 | rule "MD013", code_blocks: false, tables: false 26 | rule "MD029", style: "ordered" 27 | rule "MD046", style: "fenced" 28 | 29 | # Lesser rules 30 | exclude_rule "MD010" # hard tabs 31 | #exclude_rule "MD013" # line length 32 | 33 | # Rule Exclusions 34 | exclude_rule "MD001" # Headers are useful in other ways 35 | exclude_rule "MD024" # Headers with same name are useful, but break link labeling (Rework needed on affected files before enabling this rule) 36 | exclude_rule "MD041" # YAML header is being flagged as not the first 37 | exclude_rule "MD046" # seems broken 38 | -------------------------------------------------------------------------------- /.mdlrc: -------------------------------------------------------------------------------- 1 | # +-------------------------------------------------------------------------+ 2 | # | Copyright (C) 2004-2023 The Cacti Group | 3 | # | | 4 | # | This program is free software; you can redistribute it and/or | 5 | # | modify it under the terms of the GNU General Public License | 6 | # | as published by the Free Software Foundation; either version 2 | 7 | # | of the License, or (at your option) any later version. | 8 | # | | 9 | # | This program is distributed in the hope that it will be useful, | 10 | # | but WITHOUT ANY WARRANTY; without even the implied warranty of | 11 | # | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 12 | # | GNU General Public License for more details. | 13 | # +-------------------------------------------------------------------------+ 14 | # | Cacti: The Complete RRDtool-based Graphing Solution | 15 | # +-------------------------------------------------------------------------+ 16 | # | This code is designed, written, and maintained by the Cacti Group. See | 17 | # | about.php and/or the AUTHORS file for specific developer information. | 18 | # +-------------------------------------------------------------------------+ 19 | # | http://www.cacti.net/ | 20 | # +-------------------------------------------------------------------------+ 21 | 22 | # mdl cli configuration 23 | style ".mdl_style.rb" 24 | verbose false 25 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | dist: trusty 3 | 4 | php: 5 | - '5.4' 6 | - '5.5' 7 | - '5.6' 8 | - '7.0' 9 | - '7.1' 10 | - '7.2' 11 | 12 | before_script: 13 | - if find . -name "*.php" -exec php -l {} 2>&1 \; | grep -iv "no syntax errors detected"; then exit 1; fi 14 | 15 | script: true 16 | 17 | install: true 18 | 19 | notifications: 20 | email: 21 | on_success: change 22 | on_failure: always 23 | recipients: 24 | - developers@cacti.net 25 | slack: 26 | secure: UMpmQq5QrMxpZBOdMD43Z2KMBIY+HCif8ps1s837ECs6Ue7nOHeNSwnDWcAoJXbUP8FSev32ONEV26ZsYNk1ZSo+Ux/8vaWXZ1CE5IrSXt3XRxCouHuoPb4qMw1D0cw8UA5ARHdfKo/2ifc30Jtaa2LuE4XrPMGpBEjjNBI7O2p5bwKQPzU17D/bIkkF8BQlEKFDa+x4ff5ABQTyAAicIwNOYyYh3XhEdeQLP1OhIyRzXF9ykE+Xf3N6tlS/ymW0FAVt4wYBCb3A9K3wdsLQhjCKlIthbhuFD/vzqEwibTqUXjDECKgNZHVLWUsdpH+810Y7QFEAgDOKVqMLkvQInzecXWtZveY/mEL4KyazMS3n0OuJ886489jZVIQDfKgj4YL64ighzTuhw+gUd2lIgAU/UKQWxWqH3u50344rV13ZqeoeFO3EUuUpv1xmrJEXuB585PwURfZ5lu/OtO3WCAil8NrzbMxp6ji78/wTmdRZ1htmWOZgkEDX+VrnZsmL+JoHmkXVR+afo0kz05+tml01C5Lgx/gMOF87FtKaaeTvW6G0a4rD4438yklQabo+nyuMt7y7nOzvEVk6kARxlgt7cY7Of2M1w3HiHzzvvEIQNCskhjULS0WdPPPiWjb7PbwJvTiC0qAfFbMWnFOLLqQk7sUuh1/cH4wdpa23g5Y= 27 | -------------------------------------------------------------------------------- /CACTI-THOLD-MIB: -------------------------------------------------------------------------------- 1 | -- ***************************************************************** 2 | -- CACTI-THOLD-MIB: Thold Management Information Base for Cacti 3 | -- 4 | -- September 2013, Andreas Braun (aka browniebraun) 5 | -- 6 | -- Copyright (c) 2004-2023 by The Cacti Group 7 | -- All rights reserved. 8 | -- 9 | -- ***************************************************************** 10 | 11 | CACTI-THOLD-MIB DEFINITIONS ::= BEGIN 12 | 13 | IMPORTS 14 | OBJECT-TYPE, 15 | MODULE-IDENTITY, 16 | OBJECT-IDENTITY, 17 | NOTIFICATION-TYPE, 18 | Integer32, 19 | IpAddress 20 | FROM SNMPv2-SMI 21 | 22 | OBJECT-GROUP, 23 | NOTIFICATION-GROUP 24 | FROM SNMPv2-CONF 25 | 26 | TEXTUAL-CONVENTION, 27 | DisplayString 28 | FROM SNMPv2-TC 29 | 30 | cactiPlugins 31 | FROM CACTI-MIB 32 | ; 33 | 34 | thold MODULE-IDENTITY 35 | LAST-UPDATED "201309260000Z" 36 | ORGANIZATION "The Cacti Group" 37 | CONTACT-INFO 38 | "The Cacti Group 39 | 40 | E-mail: developers@cacti.net" 41 | DESCRIPTION 42 | "This modules defines a MIB for THOLD, a threshold 43 | monitoring plugin for Cacti." 44 | REVISION "201309260000Z" 45 | DESCRIPTION 46 | "Initial version of this MIB module." 47 | ::= { cactiPlugins 1 } -- assigned by the Cacti Group 48 | 49 | -- 50 | -- TEXTUAL CONVENTIONS 51 | -- 52 | 53 | -- 54 | -- Event Class 55 | -- 56 | 57 | TcEventClass ::= TEXTUAL-CONVENTION 58 | STATUS current 59 | DESCRIPTION 60 | "Represents the classification of an event: 61 | 62 | info - threshold item is in normal state 63 | warning - warning condition fulfilled 64 | alert - alerting condition fulfilled " 65 | SYNTAX INTEGER 66 | { 67 | info(1), 68 | warning(2), 69 | alert(3) 70 | } 71 | 72 | -- 73 | -- Event Severity 74 | -- 75 | 76 | TcEventSeverity ::= TEXTUAL-CONVENTION 77 | STATUS current 78 | DESCRIPTION 79 | "The following are the severities an THOLD event 80 | can have: 81 | 82 | low - informational event, 83 | event with low impact 84 | medium - warning event, 85 | event with medium impact 86 | high - warning event or alert event, 87 | event with high impact 88 | critical - alert event, 89 | event with critical impact " 90 | SYNTAX INTEGER 91 | { 92 | low(1), 93 | medium(2), 94 | high(3), 95 | critical(4) 96 | } 97 | 98 | -- 99 | -- Event Threshold Type 100 | -- 101 | 102 | TcEventThresholdType ::= TEXTUAL-CONVENTION 103 | STATUS current 104 | DESCRIPTION 105 | "The monitoring type used to verify a threshold 106 | condition: 107 | 108 | highLow - Monitoring if the current value was below a 109 | specific upper and above a specific lower limit 110 | baseline - Monitoring based on deviation in percentage 111 | for the upper and lower bound threshold against a 112 | time reference in the past 113 | timebased - Monitoring if a breach condition was given for a 114 | specific number of times within a specific amount of time 115 | in the past " 116 | SYNTAX INTEGER 117 | { 118 | highLow(1), 119 | baseline(2), 120 | timebased(3) 121 | } 122 | 123 | -- 124 | -- Event Notification Type 125 | -- 126 | 127 | TcEventNotificationType ::= TEXTUAL-CONVENTION 128 | STATUS current 129 | DESCRIPTION 130 | "Describes the several conditions detected by THOLD 131 | which will end in a notification / alert: 132 | 133 | restoral - not available (restoral) 134 | triggera - not available (trigger alert) 135 | notifyra - Notify Alert Retrigger 136 | notifywa - Notify Warning 137 | notifyal - Notify Alert 138 | notifyrs - Notify Restoral 139 | triggerw - not available (trigger warning) 140 | notifyaw - Notify Restoral to Warning: 141 | This occurs if conditions for an alert state are no 142 | longer breached, but the thresholds for a warning state 143 | are still exceeded. " 144 | SYNTAX INTEGER 145 | { 146 | restoral(1), 147 | triggera(2), 148 | notifyra(3), 149 | notifywa(4), 150 | notifyal(5), 151 | notifyrs(6), 152 | triggerw(7), 153 | notifyaw(8) 154 | } 155 | 156 | -- 157 | -- Event Status 158 | -- 159 | 160 | TcEventStatus ::= TEXTUAL-CONVENTION 161 | STATUS current 162 | DESCRIPTION 163 | "Represents the state of a threshold: 164 | 165 | normal - Threshold has not been exceeded 166 | low - Lower threshold condition breached 167 | high - Upper threshold condition breached " 168 | SYNTAX INTEGER 169 | { 170 | normal(1), 171 | low(2), 172 | high(3) 173 | } 174 | 175 | -- 176 | -- Event Re-Alert Status 177 | -- 178 | 179 | TcEventRealertStatus ::= TEXTUAL-CONVENTION 180 | STATUS current 181 | DESCRIPTION 182 | "Represents the long-term state of a threshold: 183 | 184 | normal - Threshold has not been exceeded 185 | low - Lower threshold condition still breached 186 | high - Upper threshold condition still breached " 187 | SYNTAX INTEGER 188 | { 189 | normal(1), 190 | low(2), 191 | high(3) 192 | } 193 | 194 | -- 195 | -- THOLD APPLICATION DATA 196 | -- 197 | tholdAppl OBJECT-IDENTITY 198 | STATUS current 199 | DESCRIPTION 200 | "reserved for Thold application data" 201 | ::= { thold 1 } 202 | 203 | -- 204 | -- THOLD Statistics 205 | -- 206 | tholdStats OBJECT-IDENTITY 207 | STATUS current 208 | DESCRIPTION 209 | "reserved for statistics" 210 | ::= { thold 2 } 211 | 212 | -- 213 | -- THOLD Events 214 | -- 215 | tholdEvents OBJECT-IDENTITY 216 | STATUS current 217 | DESCRIPTION 218 | "reserved for events" 219 | ::= { thold 3 } 220 | 221 | tholdEventObjects OBJECT-IDENTITY 222 | STATUS current 223 | DESCRIPTION 224 | "reserved for event attributes" 225 | ::= { tholdEvents 1 } 226 | 227 | eventDateRFC822 OBJECT-TYPE 228 | SYNTAX DisplayString (SIZE(1..48)) 229 | MAX-ACCESS accessible-for-notify 230 | STATUS current 231 | DESCRIPTION 232 | "The current date/time of when the event was detected by Thold." 233 | ::= { tholdEventObjects 1 } 234 | 235 | eventClass OBJECT-TYPE 236 | SYNTAX TcEventClass 237 | MAX-ACCESS accessible-for-notify 238 | STATUS current 239 | DESCRIPTION 240 | "Represents the classification of an event: 241 | 242 | info - threshold item is in normal state 243 | warning - warning condition fulfilled 244 | alert - alerting condition fulfilled " 245 | DEFVAL { alert } 246 | ::= { tholdEventObjects 2 } 247 | 248 | eventSeverity OBJECT-TYPE 249 | SYNTAX TcEventSeverity 250 | MAX-ACCESS accessible-for-notify 251 | STATUS current 252 | DESCRIPTION 253 | "The following are the severities an THOLD event 254 | can have: 255 | 256 | low - informational event, 257 | event with low impact 258 | medium - warning event, 259 | event with medium impact 260 | high - warning event or alert event, 261 | event with high impact 262 | critical - alert event, 263 | event with critical impact " 264 | DEFVAL { high } 265 | ::= { tholdEventObjects 3 } 266 | 267 | eventCategory OBJECT-TYPE 268 | SYNTAX DisplayString (SIZE(1..255)) 269 | MAX-ACCESS accessible-for-notify 270 | STATUS current 271 | DESCRIPTION 272 | "To allow a NMS to categorize different SNMP traps THOLD SNMP traps 273 | this object can be used to define a custom category 274 | like 'disk_usage', 'link_utilization' or 'ping_test'." 275 | DEFVAL { "" } 276 | ::= { tholdEventObjects 4 } 277 | 278 | eventSource OBJECT-TYPE 279 | SYNTAX DisplayString (SIZE(1..255)) 280 | MAX-ACCESS accessible-for-notify 281 | STATUS current 282 | DESCRIPTION 283 | "Contains the threshold name to identify the threshold (source) 284 | generating this event." 285 | DEFVAL { "" } 286 | ::= { tholdEventObjects 5 } 287 | 288 | eventDescription OBJECT-TYPE 289 | SYNTAX DisplayString (SIZE(1..1000)) 290 | MAX-ACCESS accessible-for-notify 291 | STATUS current 292 | DESCRIPTION 293 | "Contains a customized event description." 294 | DEFVAL { "Threshold exceeded" } 295 | ::= { tholdEventObjects 6 } 296 | 297 | eventDevice OBJECT-TYPE 298 | SYNTAX DisplayString (SIZE(1..255)) 299 | MAX-ACCESS accessible-for-notify 300 | STATUS current 301 | DESCRIPTION 302 | "This item describes the device name this event is related to." 303 | DEFVAL { "" } 304 | ::= { tholdEventObjects 7 } 305 | 306 | eventDeviceIp OBJECT-TYPE 307 | SYNTAX IpAddress 308 | MAX-ACCESS accessible-for-notify 309 | STATUS current 310 | DESCRIPTION 311 | "IPv4 address of the device the event is related to." 312 | DEFVAL { "0.0.0.0" } 313 | ::= { tholdEventObjects 8 } 314 | 315 | eventDataSource OBJECT-TYPE 316 | SYNTAX DisplayString (SIZE(1..255)) 317 | MAX-ACCESS accessible-for-notify 318 | STATUS current 319 | DESCRIPTION 320 | "Identifies the rrd data source being used for this threshold 321 | monitor." 322 | DEFVAL { "" } 323 | ::= { tholdEventObjects 9 } 324 | 325 | eventCurrentValue OBJECT-TYPE 326 | SYNTAX DisplayString (SIZE(1..255)) 327 | MAX-ACCESS accessible-for-notify 328 | STATUS current 329 | DESCRIPTION 330 | "The current value of the data source that caused this event." 331 | DEFVAL { "" } 332 | ::= { tholdEventObjects 10 } 333 | 334 | eventHigh OBJECT-TYPE 335 | SYNTAX DisplayString (SIZE(1..255)) 336 | MAX-ACCESS accessible-for-notify 337 | STATUS current 338 | DESCRIPTION 339 | "In relation to eventClass this object describes the upper 340 | limit for a warning or an alerting condition. 341 | If eventCurrentValue is higher than this upper limit 342 | an event will be triggered" 343 | DEFVAL { "" } 344 | ::= { tholdEventObjects 11 } 345 | 346 | eventLow OBJECT-TYPE 347 | SYNTAX DisplayString (SIZE(1..255)) 348 | MAX-ACCESS accessible-for-notify 349 | STATUS current 350 | DESCRIPTION 351 | "In relation to eventClass this object describes the lower 352 | limit for a warning or an alerting condition. 353 | If eventCurrentValue is below this limit an event will 354 | be triggered" 355 | DEFVAL { "" } 356 | ::= { tholdEventObjects 12 } 357 | 358 | eventThresholdType OBJECT-TYPE 359 | SYNTAX TcEventThresholdType 360 | MAX-ACCESS accessible-for-notify 361 | STATUS current 362 | DESCRIPTION 363 | "The monitoring type used to verify a threshold 364 | condition: 365 | 366 | highLow - Monitoring if the current value was below a 367 | specific upper and above a specific lower limit 368 | baseline - Monitoring based on deviation in percentage 369 | for the upper and lower bound threshold against a 370 | time reference in the past 371 | timebased - Monitoring if a breach condition was given for a 372 | specific number of times within a specific amount of time 373 | in the past " 374 | DEFVAL { baseline } 375 | ::= { tholdEventObjects 13 } 376 | 377 | eventNotificationType OBJECT-TYPE 378 | SYNTAX TcEventNotificationType 379 | MAX-ACCESS accessible-for-notify 380 | STATUS current 381 | DESCRIPTION 382 | "Describes the several conditions detected by THOLD 383 | which will end in a notification / alert: 384 | 385 | restoral - not available (restoral) 386 | triggera - not available (trigger alert) 387 | notifyra - Notify Alert Retrigger 388 | notifywa - Notify Warning 389 | notifyal - Notify Alert 390 | notifyrs - Notify Restoral 391 | triggerw - not available (trigger warning) 392 | notifyaw - Notify Restoral to Warning: 393 | This occurs if conditions for an alert state are no 394 | longer breached, but the thresholds for a warning state 395 | are still exceeded. " 396 | DEFVAL { notifyal } 397 | ::= { tholdEventObjects 14 } 398 | 399 | eventStatus OBJECT-TYPE 400 | SYNTAX TcEventStatus 401 | MAX-ACCESS accessible-for-notify 402 | STATUS current 403 | DESCRIPTION 404 | "Represents the state of a threshold: 405 | 406 | normal - Threshold has not been exceeded 407 | low - Lower threshold condition breached 408 | high - Upper threshold condition breached " 409 | DEFVAL { high } 410 | ::= { tholdEventObjects 15 } 411 | 412 | eventRealertStatus OBJECT-TYPE 413 | SYNTAX TcEventRealertStatus 414 | MAX-ACCESS accessible-for-notify 415 | STATUS current 416 | DESCRIPTION 417 | "Represents the long-term state of a threshold: 418 | 419 | normal - Threshold has not been exceeded 420 | low - Lower threshold condition still breached 421 | high - Upper threshold condition still breached " 422 | DEFVAL { normal } 423 | ::= { tholdEventObjects 16 } 424 | 425 | eventFailDuration OBJECT-TYPE 426 | SYNTAX Integer32 427 | MAX-ACCESS accessible-for-notify 428 | STATUS current 429 | DESCRIPTION 430 | "Describes the amount of time in seconds the data source 431 | is in a breach condition." 432 | DEFVAL { 0 } 433 | ::= { tholdEventObjects 17 } 434 | 435 | eventFailCount OBJECT-TYPE 436 | SYNTAX Integer32 437 | MAX-ACCESS accessible-for-notify 438 | STATUS current 439 | DESCRIPTION 440 | "Describes the number of times the data source 441 | is in a breach condition." 442 | DEFVAL { 0 } 443 | ::= { tholdEventObjects 18 } 444 | 445 | eventFailDurationTrigger OBJECT-TYPE 446 | SYNTAX Integer32 447 | MAX-ACCESS accessible-for-notify 448 | STATUS current 449 | DESCRIPTION 450 | "Describes the amount of time in seconds the data source 451 | must be in a breach condition for an alert or warning 452 | to be raised. " 453 | DEFVAL { 0 } 454 | ::= { tholdEventObjects 19} 455 | 456 | eventFailCountTrigger OBJECT-TYPE 457 | SYNTAX Integer32 458 | MAX-ACCESS accessible-for-notify 459 | STATUS current 460 | DESCRIPTION 461 | "Describes the number of times the data source 462 | must be in a breach condition for an alert or warning 463 | to be raised. " 464 | DEFVAL { 0 } 465 | ::= { tholdEventObjects 20 } 466 | 467 | -- 468 | -- THOLD Event Notifications 469 | -- 470 | tholdEventNotifications OBJECT-IDENTITY 471 | STATUS current 472 | DESCRIPTION 473 | "reserved for event attributes" 474 | ::= { tholdEvents 2 } 475 | 476 | tholdNotify NOTIFICATION-TYPE 477 | OBJECTS { 478 | eventDateRFC822, 479 | eventClass, 480 | eventSeverity, 481 | eventCategory, 482 | eventSource, 483 | eventDescription, 484 | eventDevice, 485 | eventDeviceIp, 486 | eventDataSource, 487 | eventCurrentValue, 488 | eventHigh, 489 | eventLow, 490 | eventThresholdType, 491 | eventNotificationType, 492 | eventStatus, 493 | eventRealertStatus, 494 | eventFailDuration, 495 | eventFailCount, 496 | eventFailDurationTrigger, 497 | eventFailCountTrigger 498 | } 499 | STATUS current 500 | DESCRIPTION 501 | "The SNMP trap that is generated as a result of an event with Thold." 502 | ::= { tholdEventNotifications 1 } 503 | 504 | -- 505 | -- THOLD MIB Groups 506 | -- 507 | tholdMibGroups OBJECT-IDENTITY 508 | STATUS current 509 | DESCRIPTION 510 | "reserved for group definitions" 511 | ::= { thold 4 } 512 | 513 | tholdEventGroup OBJECT-GROUP 514 | OBJECTS { 515 | eventDateRFC822, 516 | eventClass, 517 | eventSeverity, 518 | eventCategory, 519 | eventSource, 520 | eventDescription, 521 | eventDevice, 522 | eventDeviceIp, 523 | eventDataSource, 524 | eventCurrentValue, 525 | eventHigh, 526 | eventLow, 527 | eventThresholdType, 528 | eventNotificationType, 529 | eventStatus, 530 | eventRealertStatus, 531 | eventFailDuration, 532 | eventFailCount, 533 | eventFailDurationTrigger, 534 | eventFailCountTrigger 535 | } 536 | STATUS current 537 | DESCRIPTION 538 | "A collection of objects providing the THOLD event 539 | defaults." 540 | ::= { tholdMibGroups 1 } 541 | 542 | tholdNotifyGroup NOTIFICATION-GROUP 543 | NOTIFICATIONS { 544 | tholdNotify 545 | } 546 | STATUS current 547 | DESCRIPTION 548 | "The notifications relating to the monitoring operation of Thold." 549 | ::= { tholdMibGroups 2 } 550 | 551 | END -------------------------------------------------------------------------------- /INFO: -------------------------------------------------------------------------------- 1 | ; +-------------------------------------------------------------------------+ 2 | ; | Copyright (C) 2004-2024 The Cacti Group | 3 | ; | | 4 | ; | This program is free software; you can redistribute it and/or | 5 | ; | modify it under the terms of the GNU General Public License | 6 | ; | as published by the Free Software Foundation; either version 2 | 7 | ; | of the License, or (at your option) any later version. | 8 | ; | | 9 | ; | This program is distributed in the hope that it will be useful, | 10 | ; | but WITHOUT ANY WARRANTY; without even the implied warranty of | 11 | ; | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 12 | ; | GNU General Public License for more details. | 13 | ; +-------------------------------------------------------------------------+ 14 | ; | Cacti: The Complete RRDtool-based Graphing Solution | 15 | ; +-------------------------------------------------------------------------+ 16 | ; | This code is designed, written, and maintained by the Cacti Group. See | 17 | ; | about.php and/or the AUTHORS file for specific developer information. | 18 | ; +-------------------------------------------------------------------------+ 19 | ; | http://www.cacti.net/ | 20 | ; +-------------------------------------------------------------------------+ 21 | 22 | [info] 23 | name = thold 24 | version = 1.8.2 25 | longname = Thresholds 26 | author = The Cacti Group 27 | email = 28 | homepage = http://www.cacti.net 29 | compat = 1.2.25 30 | capabilities = online_view:1, online_mgmt:1, offline_view:0, offline_mgmt:0, remote_collect:1 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # thold 2 | 3 | The Cacti thold plugin is designed to be a fault management system driven by 4 | Cacti's Graph information. It provides the facility to inspect data in a Cacti 5 | Graph and the underlying RRDfile, and generate alerts for management and 6 | operations personnel. It provides Email, Syslog, and SNMP Trap or Inform 7 | escalations. In addition, it also can notify personnel of Cacti Device status 8 | changes through Email, Syslog, and either SNMP Trap or Inform. 9 | 10 | NOTE: The Thold plugin that is in GitHub is ONLY compatible with Cacti 1.0.0 and 11 | above! 12 | 13 | ## Installation 14 | 15 | To install the plugin, simply copy the plugin_thold directory to Cacti's plugins 16 | directory and rename it to simply 'thold'. Once this is complete, go to Cacti's 17 | Plugin Management section, and Install and Enable the plugin. Once this is 18 | complete, you can grant users permission to view and create Thresholds. 19 | 20 | Once you have installed thold, you should verify that Email support is 21 | functioning in Cacti by going to Cacti's Console and under Configuration select 22 | Settings, and from there the 'Mail/Reporting/DNS'. From there, you can test 23 | your mail settings to validate that users will receive notifications via email. 24 | 25 | After you have completed that, you should go to the 'Thresholds' Settings tab, 26 | and become familiar with its settings. From there, you can provide overall 27 | control of thold, and set defaults for things like Email bodies, weekend 28 | exemptions, alert log retention, logging, etc. 29 | 30 | As with much of Cacti, settings should be documented in line with the actual 31 | setting. If you find that any of these settings are ambiguous, please create a 32 | pull request with your proposed changes. 33 | 34 | ## Usage 35 | 36 | The Cacti 1.0 version of thold is designed to work with Device Templates. 37 | Therefore, when you configure a Device Template, you can add default Threshold 38 | Templates to that Device Template and when a Device in Cacti is created with 39 | that Device Template, all the required Thresholds will be created automatically. 40 | Of course, creating stand-alone Thresholds is still supported. 41 | 42 | Also new in thold version 1.0 is the ability to create multiple Thresholds per 43 | Data Source. So, you can have a Baseline Threshold say measuring the rate of 44 | change of a file system, while at the same having a Hi/Low and Time Based 45 | thresholds to notify you of free space low type of events. 46 | 47 | Most standalone Thresholds are created from the Graph Management interface in 48 | Cacti. This process starts with first creating a Threshold Template for the 49 | specific Graph Template in question, and then from Graph Management selecting 50 | the Graphs that you wish to apply this Template to. Then, from the Cacti 51 | Actions drop down, select 'Create Threshold from Template' and simply select 52 | your desired Threshold Template. Though this method continues to work today, we 53 | believe that with the support of associating Threshold Templates with Device 54 | Templates, that this method will become less popular over time. 55 | 56 | When creating your first Threshold using thold, you need to be first understand 57 | the Threshold Type. They include: High / Low, Time Based, and Baseline 58 | Deviation. The High / Low are the easiest to understand. If the measured value 59 | falls either above or below the High / Low values, for the Min Trigger Duration 60 | specified in the High / Low section, it will trigger an alert. In the Time 61 | Based Threshold type, the measured value must go above or below the High / Low 62 | values so many times in the measurement window, or the 'Time Period Length'. 63 | Lastly, the Baseline Deviation provides a floating window in the 'Time reference 64 | in the past' to measure change. If the change in the measured value either goes 65 | up or down by a certain value in that time period, an alert will be triggered. 66 | 67 | The Re-Alert Cycle is how often you wish to re-inform either via Email, Syslog, 68 | or SNMP Trap or Inform if the Threshold has not resolved itself before then. 69 | 70 | Thold has multiple Data Manipulation types, including: Exact Value, CDEF, 71 | Percentage, and RPN Expression. The simplest form is the Exact Value data 72 | manipulation where thold simply takes the raw value collected from Cacti's Data 73 | Collector, and applies rules to it. In the case of COUNTER type data, thold 74 | will convert that to a relative value automatically. The CDEF data manipulation 75 | allows you to use some, but not all Cacti CDEF's and apply them to Graph Data. 76 | The CDEF's that work, have to leverage one or more of the special types included 77 | in Cacti's CDEF implementation like 'CURRENT_DATASOURCE' to be relative. The 78 | Percentage Data Manipulation requires you to select the 'primary' Data Source as 79 | the Numerator, and then when selecting 'Percentage', you will be able to select 80 | the Denominator of the percentage calculation. The most involved Data 81 | Manipulation is the RPN Expression type. This Data Manipulation type allows you 82 | to use RPN Expressions to determine the value to be evaluated. It can include 83 | other Data Sources in the Cacti graph in addition to the selected Data Source. 84 | It follows closely RRDtool's RPN logic, and most RRDtool RPN functions are 85 | supported. 86 | 87 | If you plan on using the Threshold Daemon to increase the scalability of your 88 | thresholds, note that you must modify and install the thold_daemon.service file 89 | into your systemd configuration, and then start and test the service. If you 90 | fail to perform these steps, thold will appear to not work as expected. 91 | 92 | Lastly, please note that several forks of the thold plugin are available from 93 | different sources. These forks of thold are not necessarily compatible with the 94 | current version of Cacti's thold plugin. Please be aware of this when 95 | installing thold for the first time. 96 | 97 | ## Authors 98 | 99 | The thold plugin has been in development for well over a decade with increasing 100 | functionality and stability over that time. There have been several 101 | contributors to thold over the years. Chief among them are Jimmy Conner, Larry 102 | Adams, and Andreas Braun. We hope that version 1.0 and beyond are the most 103 | stable and robust versions of thold ever published. We are always looking for 104 | new ideas. So, this won't be the last release of thold, you can rest assured of 105 | that. 106 | 107 | ----------------------------------------------- 108 | Copyright (c) 2004-2024 - The Cacti Group, Inc. 109 | -------------------------------------------------------------------------------- /cli_import.php: -------------------------------------------------------------------------------- 1 | $message) { 168 | if (strpos($message_id, 'thold_message') !== false) { 169 | print strip_tags(str_replace('
', PHP_EOL, $message['message'])) . PHP_EOL; 170 | } 171 | } 172 | } 173 | } 174 | 175 | function display_version() { 176 | global $config; 177 | if (!function_exists('plugin_thold_version')) { 178 | include_once($config['base_path'] . '/plugins/thold/setup.php'); 179 | } 180 | 181 | $info = plugin_thold_version(); 182 | print 'Threshold Command Line Interface, Version ' . $info['version'] . ', ' . COPYRIGHT_YEARS . PHP_EOL; 183 | } 184 | 185 | 186 | /* display_help - displays the usage of the function */ 187 | function display_help () { 188 | display_version(); 189 | 190 | print PHP_EOL; 191 | 192 | print 'usage: cli_thresholds.php --auto-create=N | [--host-ids=\'N1 N2 ...\'] [--graph-template=N] [--thold-template=N] [--graph-ids=\'N1 N2 ...\']' . PHP_EOL . PHP_EOL; 193 | 194 | print 'There are two usage methods:' . PHP_EOL . PHP_EOL; 195 | 196 | print 'The first requires you to specify the host id of the device and all existing Threshold templates' . PHP_EOL; 197 | print 'are applied to hosts.' . PHP_EOL . PHP_EOL; 198 | print '--auto-create=N - Auto Create all Thresholds for this Device id using Templates associated' . PHP_EOL; 199 | print ' the Devices Device Template.' . PHP_EOL . PHP_EOL; 200 | 201 | print 'The second requires you to specify either a series of Devices, a Graph Template, a Threshold Template' . PHP_EOL; 202 | print 'or a series of Graphs or any combination of the above. However, at least one must be specified.' . PHP_EOL; 203 | print 'Threshold Template and Graph IDs of the Graphs to be impacted.' . PHP_EOL . PHP_EOL; 204 | print '--host-ids=\'N1 N2 ...\' - The Devices ID to create Thresholds for' . PHP_EOL; 205 | print '--graph-template=N - The Graph Template to create Thresholds for' . PHP_EOL; 206 | print '--thold-template=N - The Threshold Template to use for creating Thresholds' . PHP_EOL; 207 | print '--graph-ids=\'N1 N2 ...\' - The Threshold Template to use for creating Thresholds' . PHP_EOL . PHP_EOL; 208 | } 209 | 210 | -------------------------------------------------------------------------------- /extras/apply_realms.php: -------------------------------------------------------------------------------- 1 | ' . __esc('Thold', 'thold') . ''; 34 | } 35 | } 36 | 37 | -------------------------------------------------------------------------------- /index.php: -------------------------------------------------------------------------------- 1 | /dev/null` 28 | if [ $? -gt 0 ] 29 | then 30 | echo "ERROR: unable to locate realpath" 31 | echo 32 | echo "Linux: Confirm coreutils installed" 33 | echo "Mac: Brew install coreutils" 34 | echo 35 | exit 1 36 | fi 37 | BASE_PATH=`${REALPATH_BIN} ${0} | sed s#/locales/${SCRIPT_NAME}##` 38 | 39 | # locate xgettext for processing 40 | XGETTEXT_BIN=`which xgettext 2>/dev/null` 41 | if [ $? -gt 0 ] 42 | then 43 | echo "ERROR: Unable to locate xgettext" 44 | echo 45 | echo "Linux: Install GNU gettext" 46 | echo "Mac: Brew install GNU gettext" 47 | echo 48 | exit 1 49 | fi 50 | 51 | # Update main gettext POT file with application strings 52 | echo "Updating Cacti language gettext language file..." 53 | cd ${BASE_PATH} 54 | 55 | ${XGETTEXT_BIN} --no-wrap --copyright-holder="The Cacti Group" --package-name="Cacti" --package-version=`cat include/cacti_version` --msgid-bugs-address="developers@cacti.net" -F -k__gettext -k__ -k__n:1,2 -k__x:1c,2 -k__xn:1c,2,3 -k__esc -k__esc_n:1,2 -k__esc_x:1c,2 -k__esc_xn:1c,2,3 -k__date -o locales/po/cacti.pot `find . -maxdepth 2 -name \*.php` 56 | 57 | sed -i 's/FULL NAME /Cacti Developers /g' locales/po/cacti.pot 58 | sed -i 's/LANGUAGE /Cacti Developers /g' locales/po/cacti.pot 59 | sed -i 's/CHARSET/UTF-8/g' locales/po/cacti.pot 60 | 61 | # Merge any changes to POT file into language files 62 | echo "Merging updates to language files..." 63 | 64 | for file in `ls -1 locales/po/*.po`;do 65 | echo "Updating $file from cacti.pot" 66 | msgmerge --backup off --no-wrap --update -F $file locales/po/cacti.pot 67 | done 68 | 69 | for file in `ls -1 locales/po/*.po`;do 70 | ofile=$(basename --suffix=.po ${file}) 71 | echo "Converting $file to LC_MESSAGES/${ofile}.mo" 72 | msgfmt ${file} -o locales/LC_MESSAGES/${ofile}.mo 73 | done 74 | 75 | exit 0 76 | -------------------------------------------------------------------------------- /locales/index.php: -------------------------------------------------------------------------------- 1 | 0) { 213 | if (empty($heartbeat)) { 214 | $last_notification = read_config_option('thold_daemon_down_notify_time'); 215 | 216 | if ($curtime - $last_notification > $frequency) { 217 | admin_email('Thold Daemon Not Started', 'WARNING: You have elected to use the Thold Daemon, but it appears not to be running. Please correct this right away'); 218 | set_config_option('thold_daemon_down_notify_time', $curtime); 219 | } 220 | } elseif ($now - $heartbeat > 3 * $poller_interval) { 221 | $last_notification = read_config_option('thold_daemon_down_notify_time'); 222 | 223 | if ($curtime - $last_notification > $frequency) { 224 | admin_email('Thold Daemon Down', 'WARNING: You have elected to use the Thold Daemon, but it appears have stopped running. Please correct this right away'); 225 | set_config_option('thold_daemon_down_notify_time', $curtime); 226 | } 227 | } 228 | } 229 | 230 | $threads = read_config_option('thold_max_concurrent_processes'); 231 | 232 | if (read_config_option('remote_storage_method') == 1) { 233 | /* host_status processed by thold server */ 234 | $nhosts = thold_update_host_status(); 235 | 236 | thold_cleanup_log(); 237 | 238 | $total_hosts = db_fetch_cell_prepared('SELECT COUNT(*) 239 | FROM host 240 | WHERE disabled = "" 241 | AND poller_id = ?', 242 | array($config['poller_id'])); 243 | 244 | $down_hosts = db_fetch_cell_prepared('SELECT COUNT(*) 245 | FROM host 246 | WHERE status = 1 247 | AND disabled = "" 248 | AND poller_id = ?', 249 | array($config['poller_id'])); 250 | 251 | $thresholds = db_fetch_cell_prepared('SELECT COUNT(*) 252 | FROM thold_data 253 | INNER JOIN host 254 | ON host.id = thold_data.host_id 255 | WHERE poller_id = ? 256 | AND disabled = ""', 257 | array($config['poller_id'])); 258 | } else { 259 | /* host_status processed by thold server */ 260 | $nhosts = thold_update_host_status(); 261 | 262 | thold_cleanup_log(); 263 | 264 | $total_hosts = db_fetch_cell('SELECT COUNT(*) 265 | FROM host 266 | WHERE disabled=""'); 267 | 268 | $down_hosts = db_fetch_cell('SELECT COUNT(*) 269 | FROM host 270 | WHERE status = 1 271 | AND disabled = ""'); 272 | 273 | $thresholds = db_fetch_cell('SELECT COUNT(*) 274 | FROM thold_data 275 | INNER JOIN host 276 | ON host.id = thold_data.host_id 277 | WHERE disabled = ""'); 278 | } 279 | 280 | thold_prune_old_data(); 281 | 282 | /* record the end time */ 283 | $end = microtime(true); 284 | 285 | /* log statistics */ 286 | $thold_stats = sprintf('Time:%0.2f TotalDevices:%u DownDevices:%u NewDownDevices:%u Threads:%u Thresholds:%u', 287 | $end - $start, $total_hosts, $down_hosts, $nhosts, $threads, $thresholds); 288 | 289 | cacti_log('THOLD POLLER STATS: ' . $thold_stats, false, 'SYSTEM'); 290 | 291 | set_config_option('stats_thold_' . $config['poller_id'], $thold_stats); 292 | } 293 | } 294 | 295 | /** 296 | * display_version - displays version information 297 | */ 298 | function display_version() { 299 | global $config; 300 | 301 | if (!function_exists('plugin_thold_version')) { 302 | include_once($config['base_path'] . '/plugins/thold/setup.php'); 303 | } 304 | 305 | $info = plugin_thold_version(); 306 | 307 | print "Cacti Thold Master Process, Version " . $info['version'] . ", " . COPYRIGHT_YEARS . "\n"; 308 | } 309 | 310 | /** 311 | * display_help - displays the usage of the function 312 | */ 313 | function display_help () { 314 | display_version(); 315 | 316 | print "\nusage: poller_thold.php [--debug] [--force]\n\n"; 317 | print "This binary run various Threshold data collection and\n"; 318 | print "Management function.\n\n"; 319 | print "--force - Force all the service checks to run now\n"; 320 | print "--debug - Display verbose output during execution\n\n"; 321 | } 322 | 323 | -------------------------------------------------------------------------------- /service/README.md: -------------------------------------------------------------------------------- 1 | # thold daemon installation 2 | 3 | ## Introduction 4 | 5 | The thold daemon was designed to improve Cacti's scalability by allowing the 6 | thold check process to take place out of band. By doing so, the time Cacti 7 | spends checking thresholds can be reduced significantly. This service folder 8 | includes initialization scripts for both systemd and initd based systems. To 9 | install the thold daemon as a service, follow the instructions below. 10 | 11 | ## SystemD Based Systems 12 | 13 | Follow the steps below to install the thold daemon on a SystemD system. 14 | 15 | * Verify the location of the thold_daemon.php in the systemd subfolder of the 16 | location of this README.md file. 17 | 18 | * You may have to change mariadb.service to mysql.service depending on your 19 | installed version of MariaDB or MySQL. 20 | 21 | * Update the to point to you Cacti base path. 22 | 23 | * Copy the `thold_daemon.service` file to systemd directory and then reload the 24 | systemd daemon so that it knows the new service is available. 25 | 26 | ```shell 27 | cp thold_daemon.service /etc/systemd/system 28 | systemctl daemon-reload 29 | ``` 30 | 31 | * Edit the `thold_daemon.service` file and update the ExecStart/ExecStop paths 32 | with the location of the `thold_daemon` shell script. By default, this script 33 | is found in the [cacti]/plugin/thold/service/systemd folder, but the service 34 | file is currently hardcoded to expect [cacti] is located at: 35 | 36 | /var/www/html/cacti 37 | 38 | * Ensure that the `thold_daemon` script is marked executable 39 | 40 | ```shell 41 | chmod +x thold_daemon.php 42 | ``` 43 | 44 | * Enable and start the service using either the -now parameter: 45 | 46 | ```shell 47 | systemctl enable --now thold_daemon 48 | ``` 49 | 50 | or issuing two separate commands if you want to start at a later date: 51 | 52 | ```shell 53 | systemctl enable thold_daemon 54 | systemctl start thold_daemon 55 | ``` 56 | 57 | * Running on Windows? Switch to Linux! 58 | 59 | * System V init? Upgrade your Linux! 60 | 61 | ----------------------------------------------- 62 | Copyright (c) 2004-2024 - The Cacti Group, Inc. 63 | -------------------------------------------------------------------------------- /service/index.php: -------------------------------------------------------------------------------- 1 | to your installed Cacti directory 27 | # - Change the mariadb.service to mysql.service if using the MySQL database 28 | # 29 | # Then follow these steps: 30 | # 31 | # - copy this file into /etc/systemd/system directory 32 | # - run 'systemctl daemon-reload' 33 | # - start the service using 'systemctl start thold_daemon' 34 | # - check that the process is running using 'systemctl status thold_daemon' 35 | # - enable the Thold Daemon from Console > Configuration > Settings > Thold/Alerting 36 | 37 | [Unit] 38 | Description=Cacti Threshold Daemon Service 39 | Requires=mariadb.service 40 | After=network.target auditd.service mariadb.service 41 | 42 | [Service] 43 | User=apache 44 | Group=root 45 | Type=forking 46 | ExecStart=/usr/bin/php /plugins/thold/thold_daemon.php 47 | KillMode=process 48 | Restart=on-failure 49 | 50 | [Install] 51 | WantedBy=multi-user.target 52 | -------------------------------------------------------------------------------- /themes/classic/index.php: -------------------------------------------------------------------------------- 1 | 0) { 119 | print 'The Thold Daemon is still running' . PHP_EOL; 120 | exit(1); 121 | } else { 122 | unregister_process('thold', 'parent', 0); 123 | register_process_start('thold', 'parent', 0, $timeout); 124 | } 125 | } 126 | } 127 | 128 | /* do not run the thold daemon on the remote server in central storage mode */ 129 | if (read_config_option('remote_storage_method') != 1 && $config['poller_id'] > 1) { 130 | print 'In Central Storage Mode, the thold_daemon only runs on the main data collector.' . PHP_EOL; 131 | exit(1); 132 | } 133 | 134 | print 'Starting Thold Daemon ... '; 135 | 136 | if (!$foreground) { 137 | if (function_exists('pcntl_fork')) { 138 | // Close the database connection 139 | db_close(); 140 | 141 | // Fork the current process to daemonize 142 | $pid = pcntl_fork(); 143 | 144 | if ($pid == -1) { 145 | // oha ... something went wrong :( 146 | print '[FAILED]' . PHP_EOL; 147 | 148 | return false; 149 | } elseif ($pid == 0) { 150 | // We are the child reconnect 151 | db_connect_real($database_hostname, $database_username, $database_password, $database_default, $database_type, $database_port, $database_retries, $database_ssl, $database_ssl_key, $database_ssl_cert, $database_ssl_ca); 152 | } else { 153 | cacti_log('NOTE: Thold Daemon Started on ' . gethostname(), false, 'THOLD'); 154 | 155 | // We are the parent, output and exit 156 | print '[OK]' . PHP_EOL; 157 | 158 | exit; 159 | } 160 | } else { 161 | // Windows.... awesome! But no worries 162 | print '[WARNING] This system does not support forking.' . PHP_EOL; 163 | } 164 | } else { 165 | print '[NOTE] The Thold Daemon is running in foreground mode.' . PHP_EOL; 166 | } 167 | 168 | sleep(2); 169 | 170 | $processes = read_config_option('thold_max_concurrent_processes'); 171 | 172 | thold_truncate_daemon_data(); 173 | 174 | thold_prime_distribution($processes); 175 | 176 | thold_daemon_debug('Forking Thold Daemon Child Processes'); 177 | 178 | for($i = 1; $i <= $processes; $i++) { 179 | thold_launch_worker($i); 180 | } 181 | 182 | $prev_running = false; 183 | $start_daemon_items = 0; 184 | $counter = 0; 185 | 186 | while (true) { 187 | if (db_check_reconnect()) { 188 | $counter++; 189 | 190 | // force the check for the daemon debug 191 | $daemon_debug = read_config_option('thold_daemon_debug', true); 192 | 193 | if ($counter == 1) { 194 | thold_daemon_debug('Thold Thread Watchdog Start.'); 195 | 196 | set_config_option('thold_daemon_heartbeat', microtime(true)); 197 | } 198 | 199 | $running = thold_poller_running(); 200 | 201 | if ($running && !$prev_running) { 202 | $start = microtime(true); 203 | 204 | thold_daemon_debug('Detected Cacti Poller Start at ' . date('Y-m-d H:i:s')); 205 | 206 | $start_items = db_fetch_cell('SELECT COUNT(*) FROM plugin_thold_daemon_data'); 207 | $prev_running = true; 208 | } elseif (!$running && $prev_running) { 209 | $end_items = db_fetch_cell('SELECT COUNT(*) FROM plugin_thold_daemon_data'); 210 | $prev_running = false; 211 | 212 | $tholds = db_fetch_cell('SELECT COUNT(*) FROM thold_data WHERE thold_enabled = "on"'); 213 | 214 | thold_daemon_debug(sprintf('Detected Cacti Poller End at %s.', date('Y-m-d H:i:s'))); 215 | thold_daemon_debug(sprintf('TotalTholds:%u StartingTholds:%u, EndingTholds:%u', $tholds, $start_items, $end_items)); 216 | 217 | $end = microtime(true); 218 | 219 | cacti_log(sprintf('THOLD DAEMON STATS: TotalTime:%3.2f TotalTholds:%u StartingItems:%u EndingItems:%u', 220 | $end - $start, $tholds, $start_items, $end_items), false, 'SYSTEM'); 221 | } else { 222 | $prev_running = $running; 223 | } 224 | 225 | sleep(1); 226 | 227 | if ($counter == 10) { 228 | $counter = 0; 229 | 230 | $new_processes = read_config_option('thold_max_concurrent_processes', true); 231 | 232 | if ($new_processes != $processes) { 233 | thold_prime_distribution($new_processes, true); 234 | } else { 235 | thold_prime_distribution($new_processes); 236 | } 237 | 238 | thold_heartbeat_processes($processes, $new_processes); 239 | 240 | $processes = $new_processes; 241 | 242 | thold_daemon_debug('Thold Thread Watchdog End. Processed heartbeat.'); 243 | 244 | heartbeat_process('thold', 'parent', 0); 245 | } 246 | 247 | } else { 248 | thold_daemon_debug('WARNING: No database connection. Sleeping for 60 seconds.'); 249 | 250 | sleep(60); 251 | } 252 | } 253 | 254 | function thold_truncate_daemon_data() { 255 | thold_daemon_debug('Truncating historical Threshold Daemon Data'); 256 | 257 | db_execute('TRUNCATE TABLE plugin_thold_daemon_data'); 258 | } 259 | 260 | function thold_poller_running() { 261 | return db_fetch_cell('SELECT COUNT(*) FROM poller_time WHERE end_time = "0000-00-00"'); 262 | } 263 | 264 | /** 265 | * sig_handler - provides a generic means to catch exceptions to the Cacti log. 266 | * 267 | * @param $signo - (int) the signal that was thrown by the interface. 268 | * 269 | * @returns - null 270 | */ 271 | function sig_handler($signo) { 272 | global $config; 273 | 274 | switch ($signo) { 275 | case SIGTERM: 276 | case SIGINT: 277 | /* kill any child processes */ 278 | $processes = db_fetch_assoc('SELECT * FROM processes WHERE tasktype = "thold" AND taskname = "child"'); 279 | 280 | if (cacti_sizeof($processes)) { 281 | foreach($processes as $p) { 282 | thold_daemon_debug(sprintf('Killing Child Process with the pid of %u', $p['pid'])); 283 | posix_kill($p['pid'], SIGTERM); 284 | } 285 | } 286 | 287 | thold_cacti_log('WARNING: Thold Daemon Parent Process with PID[' . getmypid() . '] terminated by user', 0); 288 | 289 | unregister_process('thold', 'parent', 0); 290 | 291 | exit; 292 | default: 293 | /* ignore all other signals */ 294 | } 295 | } 296 | 297 | function thold_launch_worker($thread) { 298 | global $config, $debug; 299 | 300 | $path_php = read_config_option('path_php_binary'); 301 | 302 | $process = $config['base_path'] . 303 | '/plugins/thold/thold_process.php ' . 304 | ' --thread=' . $thread . 305 | ($debug ? ' --debug':'') . 306 | ' > /dev/null'; 307 | 308 | thold_daemon_debug('Starting Process: ' . $path_php . ' ' . $process); 309 | 310 | exec_background($path_php, $process); 311 | } 312 | 313 | function thold_heartbeat_processes($processes, $new_processes) { 314 | global $config; 315 | 316 | $procs = db_fetch_assoc('SELECT * 317 | FROM processes 318 | WHERE tasktype = "thold" 319 | AND taskname = "child" 320 | ORDER BY taskid DESC'); 321 | 322 | $running_processes = cacti_sizeof($procs); 323 | 324 | // Check for a crashed process 325 | $process_num = -1; 326 | foreach($procs as $id => $p) { 327 | // Check for crashed processes first 328 | if ($process_num != -1) { 329 | if ($process_num - 1 != $p['taskid']) { 330 | thold_daemon_debug(sprintf('WARNING: Detected Crashed Thold Thread. Relaunching Crashed Thread %s', $process_num - 1)); 331 | 332 | thold_launch_worker($process_num -1); 333 | 334 | $running_processes++; 335 | } 336 | } else { 337 | // Check for hung processes next 338 | $lastupdate = strtotime($p['last_update']); 339 | $now = time(); 340 | if ($lastupdate + 120 < $now) { 341 | thold_daemon_debug(sprintf('WARNING: Detected Hung Thold Thread. Killing/Relaunching Hung Thread %s', $p['taskid'])); 342 | 343 | posix_kill($p['pid'], SIGTERM); 344 | 345 | thold_launch_worker($p['taskid']); 346 | } 347 | } 348 | 349 | $process_num = $p['taskid']; 350 | } 351 | 352 | foreach($procs as $id => $p) { 353 | if (function_exists('posix_getpgid')) { 354 | $running = posix_getpgid($p['pid']); 355 | } elseif (function_exists('posix_kill')) { 356 | $running = posix_kill($p['pid'], 0); 357 | } 358 | 359 | if (!$running) { 360 | thold_daemon_debug(sprintf('WARNING: Thold Daemon Child[%s] Died!', $p['pid'])); 361 | 362 | cacti_log(sprintf('WARNING: Thold Daemon Child[%s] Died!', $p['pid']), false, 'THOLD'); 363 | 364 | $running_processes--; 365 | unset($procs[$id]); 366 | } 367 | } 368 | 369 | if ($running_processes != $new_processes) { 370 | if ($running_processes > $new_processes) { 371 | thold_daemon_debug(sprintf('Thold Thread Detected Process Count Change. Reducing Process Count by %s', $running_processes - $new_processes)); 372 | 373 | foreach($procs as $id => $p) { 374 | posix_kill($p['pid'], SIGTERM); 375 | $running_processes--; 376 | if ($running_processes == $new_processes) { 377 | break; 378 | } 379 | } 380 | } else { 381 | thold_daemon_debug(sprintf('Thold Thread Detected Process Count Change. Increasing Process Count by %s', $new_processes - $running_processes)); 382 | 383 | while($running_processes < $new_processes) { 384 | $running_processes++; 385 | 386 | thold_launch_worker($running_processes); 387 | } 388 | } 389 | } 390 | } 391 | 392 | function thold_prime_distribution($processes, $truncate = false) { 393 | thold_daemon_debug('Rebalancing Thread Allocation by Device'); 394 | 395 | $seen_processes = db_fetch_cell('SELECT COUNT(DISTINCT thread_id) FROM thold_data'); 396 | 397 | if ($truncate || $seen_processes != $processes) { 398 | db_execute('UPDATE thold_data SET thread_id = 0'); 399 | } 400 | 401 | $not_set_threads = db_fetch_cell('SELECT COUNT(id) FROM thold_data WHERE thread_id = 0'); 402 | 403 | if ($not_set_threads > 0) { 404 | // Get the current distribution of threads 405 | $threads = array_rekey( 406 | db_fetch_assoc('SELECT thread_id, host_id 407 | FROM thold_data 408 | WHERE thread_id > 0'), 409 | 'host_id', 'thread_id' 410 | ); 411 | 412 | $tholds = db_fetch_assoc('SELECT id, host_id 413 | FROM thold_data 414 | WHERE thread_id = 0'); 415 | 416 | $thread_num = 1; 417 | 418 | foreach($tholds as $t) { 419 | if (!isset($threads[$t['host_id']])) { 420 | $threads[$t['host_id']] = $thread_num; 421 | $thread_num++; 422 | } 423 | 424 | if ($thread_num > $processes) { 425 | $thread_num = 1; 426 | } 427 | } 428 | 429 | foreach($threads as $host_id => $thread) { 430 | db_execute_prepared('UPDATE thold_data 431 | SET thread_id = ? 432 | WHERE host_id = ?', 433 | array($thread, $host_id)); 434 | } 435 | } 436 | 437 | thold_daemon_debug('Thread Rebalancing Allocation by Device Completed'); 438 | } 439 | 440 | function thold_daemon_debug($string) { 441 | global $debug; 442 | 443 | // Get the cached value 444 | $daemon_debug = read_config_option('thold_daemon_debug'); 445 | 446 | if ($debug || $daemon_debug) { 447 | $output = date('Y-m-d H:i:s') . ' DEBUG: ' . trim($string); 448 | 449 | print $output . PHP_EOL; 450 | } 451 | } 452 | 453 | function display_version() { 454 | global $config; 455 | 456 | if (!function_exists('plugin_thold_version')) { 457 | include_once($config['base_path'] . '/plugins/thold/setup.php'); 458 | } 459 | 460 | $info = plugin_thold_version(); 461 | print 'Threshold Daemon, Version ' . $info['version'] . ', ' . COPYRIGHT_YEARS . PHP_EOL; 462 | } 463 | 464 | 465 | /* display_help - displays the usage of the function */ 466 | function display_help () { 467 | display_version(); 468 | 469 | print PHP_EOL . 'usage: thold_daemon.php [ --foreground | -f ] [ --debug ]' . PHP_EOL . PHP_EOL; 470 | print 'The Threshold Daemon processor for the Thold Plugin.' . PHP_EOL; 471 | } 472 | 473 | -------------------------------------------------------------------------------- /thold_notify.php: -------------------------------------------------------------------------------- 1 | 0) { 179 | $currentval = thold_build_cdef($thold_data['cdef'], $currentval, $thold_data['local_data_id'], $thold_data['data_template_rrd_id']); 180 | } 181 | 182 | break; 183 | case 2: 184 | if ($thold_data['percent_ds'] != '') { 185 | $currentval = thold_calculate_percent($thold_data, $currentval, $rrd_reindexed); 186 | } 187 | 188 | break; 189 | case 3: 190 | if ($thold_data['expression'] != '') { 191 | $currentval = thold_calculate_expression($thold_data, $currentval, $rrd_reindexed, $rrd_time_reindexed); 192 | } 193 | 194 | break; 195 | case 4: 196 | if ($thold_data['upper_ds'] != '') { 197 | $currentval = thold_calculate_lower_upper($thold_data, $currentval, $rrd_reindexed); 198 | } 199 | 200 | break; 201 | } 202 | 203 | if (is_numeric($currentval)) { 204 | $currentval = round($currentval, 4); 205 | } else { 206 | $currentval = ''; 207 | } 208 | 209 | if (isset($item[$thold_data['name']])) { 210 | $lasttime = $item[$thold_data['name']]; 211 | } else { 212 | $lasttime = $currenttime - $thold_data['rrd_step']; 213 | } 214 | 215 | thold_daemon_debug(sprintf('Checked Name:%s, Graph:%s, Value:%s, Time:%s', $thold_data['thold_name'], $thold_data['local_graph_id'], $currentval, $currenttime), $thread); 216 | 217 | db_execute_prepared('UPDATE thold_data 218 | SET tcheck = 1, lastread = ?, 219 | lasttime = FROM_UNIXTIME(?), oldvalue = ? 220 | WHERE id = ?', 221 | array($currentval, $currenttime, $lasttime, $thold_data['thold_id']) 222 | ); 223 | } 224 | 225 | $tholds = thold_get_thresholds_tholdcheck($thread, $start_time); 226 | 227 | $total_tholds = cacti_sizeof($tholds); 228 | 229 | thold_daemon_debug(sprintf('Found %u Thresholds to check for breech.', $total_tholds), $thread); 230 | 231 | if ($total_tholds) { 232 | foreach ($tholds as $thold) { 233 | thold_check_threshold($thold); 234 | 235 | db_execute_prepared('UPDATE thold_data 236 | SET tcheck = 0 237 | WHERE id = ?', 238 | array($thold['id']) 239 | ); 240 | } 241 | } 242 | 243 | $end = microtime(true); 244 | 245 | if (read_config_option('remote_storage_method') == 1) { 246 | db_execute_prepared('DELETE ptdd 247 | FROM plugin_thold_daemon_data AS ptdd 248 | INNER JOIN thold_data AS td 249 | ON ptdd.id = td.id 250 | WHERE ptdd.poller_id = ? 251 | AND td.thread_id = ? 252 | AND ptdd.time <= FROM_UNIXTIME(?)', 253 | array($config['poller_id'], $thread, $start_time)); 254 | } else { 255 | db_execute_prepared('DELETE ptdd 256 | FROM plugin_thold_daemon_data AS ptdd 257 | INNER JOIN thold_data AS td 258 | ON ptdd.id = td.id 259 | WHERE td.thread_id = ? 260 | AND ptdd.time <= FROM_UNIXTIME(?)', 261 | array($thread, $start_time)); 262 | } 263 | } else { 264 | sleep(5); 265 | 266 | $cnn_id = thold_db_reconnect($cnn_id); 267 | 268 | heartbeat_process('thold', 'child', $thread); 269 | } 270 | } else { 271 | thold_daemon_debug('WARNING: Thold Database Connection Down. Sleeping 60 Seconds', $thread); 272 | sleep(60); 273 | } 274 | } 275 | 276 | /** 277 | * sig_handler - provides a generic means to catch exceptions to the Cacti log. 278 | * 279 | * @param $signo - (int) the signal that was thrown by the interface. 280 | * 281 | * @returns - null 282 | */ 283 | function sig_handler($signo) { 284 | global $thread; 285 | 286 | switch ($signo) { 287 | case SIGTERM: 288 | case SIGINT: 289 | thold_cacti_log('WARNING: Thold Daemon Child Process with PID[' . getmypid() . '] terminated by user', $thread); 290 | unregister_process('thold', 'child', $thread); 291 | 292 | exit; 293 | break; 294 | default: 295 | /* ignore all other signals */ 296 | } 297 | } 298 | 299 | function thold_daemon_debug($message, $thread) { 300 | global $debug; 301 | 302 | $daemon_debug = read_config_option('thold_daemon_debug'); 303 | 304 | if ($debug || $daemon_debug) { 305 | thold_cacti_log($message, $thread); 306 | } 307 | } 308 | 309 | function thold_get_thresholds_tholdcheck($thread, $start_time) { 310 | global $config; 311 | 312 | /* check all thresholds */ 313 | if (read_config_option('remote_storage_method') == 1) { 314 | $sql_query = "SELECT td.*, h.hostname, 315 | h.description, h.notes AS dnotes, h.snmp_engine_id 316 | FROM plugin_thold_daemon_data AS tdd 317 | INNER JOIN thold_data AS td 318 | ON td.id = tdd.id 319 | LEFT JOIN data_template_rrd AS dtr 320 | ON dtr.id = td.data_template_rrd_id 321 | LEFT JOIN host as h 322 | ON td.host_id = h.id 323 | WHERE td.thread_id = ? 324 | AND tdd.poller_id = ? 325 | AND tdd.time <= FROM_UNIXTIME(?) 326 | AND td.thold_enabled = 'on' 327 | AND td.thold_per_enabled = 'on' 328 | AND td.tcheck = 1 329 | AND h.status = 3"; 330 | 331 | $tholds = api_plugin_hook_function('thold_get_live_hosts', 332 | db_fetch_assoc_prepared($sql_query, array($thread, $config['poller_id'], $start_time)) 333 | ); 334 | } else { 335 | $sql_query = "SELECT td.*, h.hostname, 336 | h.description, h.notes AS dnotes, h.snmp_engine_id 337 | FROM plugin_thold_daemon_data AS tdd 338 | INNER JOIN thold_data AS td 339 | ON td.id = tdd.id 340 | LEFT JOIN data_template_rrd AS dtr 341 | ON dtr.id = td.data_template_rrd_id 342 | LEFT JOIN host as h 343 | ON td.host_id = h.id 344 | WHERE td.thread_id = ? 345 | AND tdd.time <= FROM_UNIXTIME(?) 346 | AND td.thold_enabled = 'on' 347 | AND td.thold_per_enabled = 'on' 348 | AND td.tcheck = 1 349 | AND h.status = 3"; 350 | 351 | $tholds = api_plugin_hook_function('thold_get_live_hosts', 352 | db_fetch_assoc_prepared($sql_query, array($thread, $start_time)) 353 | ); 354 | } 355 | 356 | return $tholds; 357 | } 358 | 359 | function thold_get_thresholds_precheck($thread, $start_time) { 360 | global $config; 361 | 362 | if (read_config_option('remote_storage_method') == 1) { 363 | $sql_query = "SELECT tdd.id, tdd.rrd_reindexed, tdd.rrd_time_reindexed, 364 | td.id AS thold_id, td.name_cache AS thold_name, td.local_graph_id, 365 | td.percent_ds, td.expression, td.upper_ds, td.data_type, td.cdef, td.local_data_id, 366 | td.data_template_rrd_id, td.lastread, 367 | UNIX_TIMESTAMP(td.lasttime) AS lasttime, td.oldvalue, 368 | dtr.data_source_name AS name, dtr.data_source_type_id, 369 | dtd.rrd_step, dtr.rrd_maximum 370 | FROM plugin_thold_daemon_data AS tdd 371 | INNER JOIN thold_data AS td 372 | ON td.id = tdd.id 373 | LEFT JOIN data_template_rrd AS dtr 374 | ON dtr.id = td.data_template_rrd_id 375 | LEFT JOIN data_template_data AS dtd 376 | ON dtd.local_data_id = td.local_data_id 377 | WHERE td.thread_id = ? 378 | AND tdd.poller_id = ? 379 | AND tdd.time <= FROM_UNIXTIME(?) 380 | AND dtr.data_source_name != ''"; 381 | 382 | $tholds = db_fetch_assoc_prepared($sql_query, array($thread, $config['poller_id'], $start_time)); 383 | } else { 384 | $sql_query = "SELECT tdd.id, tdd.rrd_reindexed, tdd.rrd_time_reindexed, 385 | td.id AS thold_id, td.name_cache AS thold_name, td.local_graph_id, 386 | td.percent_ds, td.expression, td.upper_ds, td.data_type, td.cdef, td.local_data_id, 387 | td.data_template_rrd_id, td.lastread, 388 | UNIX_TIMESTAMP(td.lasttime) AS lasttime, td.oldvalue, 389 | dtr.data_source_name AS name, dtr.data_source_type_id, 390 | dtd.rrd_step, dtr.rrd_maximum 391 | FROM plugin_thold_daemon_data AS tdd 392 | INNER JOIN thold_data AS td 393 | ON td.id = tdd.id 394 | LEFT JOIN data_template_rrd AS dtr 395 | ON dtr.id = td.data_template_rrd_id 396 | LEFT JOIN data_template_data AS dtd 397 | ON dtd.local_data_id = td.local_data_id 398 | WHERE td.thread_id = ? 399 | AND tdd.time <= FROM_UNIXTIME(?) 400 | AND dtr.data_source_name != ''"; 401 | 402 | $tholds = db_fetch_assoc_prepared($sql_query, array($thread, $start_time)); 403 | } 404 | 405 | return $tholds; 406 | } 407 | 408 | function thold_db_connection(){ 409 | global $cnn_id; 410 | 411 | if (is_object($cnn_id)) { 412 | // Avoid showing errors 413 | restore_error_handler(); 414 | set_error_handler('thold_error_handler'); 415 | 416 | $cacti_version = db_fetch_cell('SELECT cacti FROM version'); 417 | 418 | // Restore Cacti's Error handler 419 | restore_error_handler(); 420 | set_error_handler('CactiErrorHandler'); 421 | 422 | return is_null($cacti_version) ? false : true; 423 | } 424 | 425 | return false; 426 | } 427 | 428 | function thold_db_reconnect($cnn_id = null) { 429 | chdir(dirname(__FILE__)); 430 | 431 | include('../../include/config.php'); 432 | 433 | if (is_object($cnn_id)) { 434 | db_close($cnn_id); 435 | } 436 | 437 | // Avoid showing errors 438 | restore_error_handler(); 439 | set_error_handler('thold_error_handler'); 440 | 441 | // Connect to the database server 442 | $cnn_id = db_connect_real($database_hostname, $database_username, $database_password, $database_default, $database_type, $database_port, $database_ssl); 443 | 444 | // Restore Cacti's Error handler 445 | restore_error_handler(); 446 | set_error_handler('CactiErrorHandler'); 447 | 448 | return $cnn_id; 449 | } 450 | 451 | function thold_cli_debug($string) { 452 | global $debug; 453 | 454 | if ($debug) { 455 | $output = date('Y-m-d H:i:s') . ' DEBUG: ' . trim($string); 456 | 457 | print $output . PHP_EOL; 458 | } 459 | } 460 | 461 | function display_version() { 462 | global $config; 463 | 464 | if (!function_exists('plugin_thold_version')) { 465 | include_once($config['base_path'] . '/plugins/thold/setup.php'); 466 | } 467 | 468 | $info = plugin_thold_version(); 469 | print 'Threshold Processor, Version ' . $info['version'] . ', ' . COPYRIGHT_YEARS . PHP_EOL; 470 | } 471 | 472 | /* display_help - displays the usage of the function */ 473 | function display_help () { 474 | display_version(); 475 | 476 | print PHP_EOL . 'usage: thold_process.php --thread=N [--debug]' . PHP_EOL . PHP_EOL; 477 | print 'The main Threshold Processor for the Thold Plugin.' . PHP_EOL; 478 | } 479 | 480 | --------------------------------------------------------------------------------