commit 8ed587cfd4d8e9cd8a5d3a796d9412c85c76c587 Author: Bryan Heden Date: Thu Aug 5 12:48:45 2021 -0500 initial commit diff --git a/report_objects_down/cron.php b/report_objects_down/cron.php new file mode 100644 index 0000000..42a9c3b --- /dev/null +++ b/report_objects_down/cron.php @@ -0,0 +1,84 @@ +#!/bin/env php -q += 24 * 60 * 60) { + + // if another process has updated the last_time_report_sent since we've executed + // we'll send duplicate emails - so do a last minute panic check here + $new_last_time_report_sent = get_option("report_objects_down_last_sent"); + if ($new_last_time_report_sent != $last_time_report_sent) { + if ($debug) { + echo "\nAnother process appears to have handled the report...\n"; + exit(0); + } + } + + if ($debug) { + echo "\nThe time to send the report is now...\n"; + } + + if (!generate_csv_reports()) { + echo "\nUnable to generate CSV reports!\n"; + exit(1); + } + + if (!send_report_email($debug)) { + echo "\nUnable to send CSV reports!\n"; + exit(1); + } + + set_option("report_objects_down_last_sent", time()); + break; + } + + echo "."; + sleep(1); + } +} diff --git a/report_objects_down/index.php b/report_objects_down/index.php new file mode 100644 index 0000000..5ac9fe3 --- /dev/null +++ b/report_objects_down/index.php @@ -0,0 +1,83 @@ + "Objects Down Report"), true); + echo ' +
+
+
+
+ + +
+ +
+
+
+
+ + '; + + $hosts = get_objects_down_hosts($mode); + if ($hosts != false) { + echo "

Hosts

"; + echo ""; + + foreach ($hosts as $host) { + echo ""; + } + + echo "
Host NameHost AddressDuration Down (days)
${host['host_name']}${host['host_address']}${host['duration']}
"; + } + else { + echo '

No Down Hosts

'; + } + + $services = get_objects_down_services($mode); + if ($services != false) { + + echo "

Services

"; + echo ""; + + foreach ($services as $service) { + echo ""; + } + + echo "
Host NameHost AddressService DescriptionStatus InformationDuration Down (hours)
${service['host_name']}${service['host_address']}${service['svc_desc']}${service['svc_status']}${service['duration']}
"; + } + else { + echo '

No Critical Services

'; + } +} diff --git a/report_objects_down/report_objects_down.common.php b/report_objects_down/report_objects_down.common.php new file mode 100644 index 0000000..2d9ff5a --- /dev/null +++ b/report_objects_down/report_objects_down.common.php @@ -0,0 +1,278 @@ +hoststatus as $host) { + + // TODO: should we check on state type as well? + if ($host->current_state != STATE_DOWN) { + continue; + } + + $host_arr['host_name'] = (string) $host->name; + $host_arr['host_address'] = (string) $host->address; + + $state_change_time = strtotime($host->last_state_change); + + // something went wrong with strtotime.. + // likely means it's just always been down i suspect + if ($state_change_time == false || $state_change_time == -1) { + $state_change_time = strtotime($host->status_update_time); + + // something seriously wrong at this point.. + if ($state_change_time == false || $state_change_time == -1) { + continue; + } + } + + $host_arr['duration'] = round((time() - $state_change_time) / (24 * 60 * 60), 2); + + $arr[] = $host_arr; + } + + return $arr; +} + + +/* + Part 2 – Services Down – Data to include: + Host Name + IP Address + Service that is down + Status Information – ie. SNMP CRITICAL - *0* + Duration it has been down + + Simple enough… Now the tricky part. For part 2, I would like excluded from the report the following: + [x] If notifications are disabled for the service, exclude from the report + [x] If the service is down due to “Plugin Timed Out …”, exclude from the report + [x] If the service is down due to the host being down, exclude from the report + Include only from the last 24 hours so day and night time alarms are taken into account +*/ +/* + mode: + 1. Do nothing really, and just report on critical services that are currently critical (my favorite option) - and see if they complain and can come up with better explanation + 2. If the duration that the service has been down is greater than 24 hours, exclude it from the report (my second favorite option) +*/ +// returns array of array(hostname, address, service, statustext, duration) - where the above conditions are met +// or return false and print an error +function get_objects_down_services($mode = 1, $return_headers = false) +{ + $arr = array(); + + $services_xml = get_xml_service_status(array()); + + if (!$services_xml) { + echo "Something went wrong getting service status!\n"; + return false; + } + + if ($return_headers == true) { + $arr[] = array("Host Name", "Host Address", "Service Description", "Service Status", "Duration (hours)"); + } + + foreach ($services_xml->servicestatus as $svc) { + + $svc_arr = array(); + + if ($svc->current_state != STATE_CRITICAL) { + continue; + } + if ($svc->notifications_enabled != 1) { + continue; + } + + $svc_arr['host_name'] = (string) $svc->host_name; + $svc_arr['host_address'] = (string) $svc->host_address; + $svc_arr['svc_desc'] = (string) $svc->name; + $svc_arr['svc_status'] = (string) $svc->status_text; + + if (strpos($svc_arr['svc_status'], "Plugin timed out") !== false) { + continue; + } + + $state_change_time = strtotime($svc->last_state_change); + + $host_xml = get_xml_host_status(array("name" => $svc_arr['host_name'])); + + // definitely should have found a host.. + if (!$host_xml) { + continue; + } + // definitely should have found only one host.. + else if ($host_xml->recordcount != 1) { + continue; + } + else { + // skip services whos host is down + if ($host_xml->hoststatus->current_state == STATE_DOWN) { + continue; + } + } + + // something went wrong with strtotime.. + // likely means it's just always been down i suspect + if ($state_change_time == false || $state_change_time == -1) { + $state_change_time = strtotime($svc->status_update_time); + + // something seriously wrong at this point.. + if ($state_change_time == false || $state_change_time == -1) { + continue; + } + } + + $svc_arr['duration'] = round((time() - $state_change_time) / (60 * 60), 2); + + // skip if we need to + if ($mode == 2) { + if ($svc_arr['duration'] > (24 * 60 * 60)) { + continue; + } + } + + $arr[] = $svc_arr; + } + + return $arr; +} + + +function get_csv_output($which) +{ + if ($which != "hosts" && $which != "services") { + return ""; + } + + $arr_func = "get_objects_down_${which}"; + + $data = $arr_func($mode = 2, $headers = true); + if ($data == false) { + return ""; + } + + $csv = ""; + + foreach ($data as $row) { + foreach ($row as $cell) { + $csv .= $cell . ","; + } + $csv .= "\n"; + } + + return $csv; +} + + +function generate_csv_reports() +{ + $hosts = get_csv_output("hosts"); + //echo "
$hosts
"; + $services = get_csv_output("services"); + //echo "
$services
"; + + if ($hosts == false) { + echo "\nUnable to retrieve hosts csv report..\n"; + } + if ($services == false) { + echo "\nUnable to retrieve services csv report..\n"; + } + if ($hosts == false || $services == false) { + echo "testing..\n"; + return false; + } + + if (file_exists("/usr/local/nagiosxi/tmp/rod_hosts.csv")) { + if (!unlink("/usr/local/nagiosxi/tmp/rod_hosts.csv")) { + echo "\nUnable to delete /usr/local/nagiosxi/tmp/rod_hosts.csv file\n"; + return false; + } + } + if (file_exists("/usr/local/nagiosxi/tmp/rod_services.csv")) { + if (!unlink("/usr/local/nagiosxi/tmp/rod_services.csv")) { + echo "\nUnable to delete /usr/local/nagiosxi/tmp/rod_services.csv file\n"; + return false; + } + } + + if (!file_put_contents("/usr/local/nagiosxi/tmp/rod_hosts.csv", $hosts)) { + echo "\nUnable to write /usr/local/nagiosxi/tmp/rod_hosts.csv file\n"; + return false; + } + if (!file_put_contents("/usr/local/nagiosxi/tmp/rod_services.csv", $services)) { + echo "\nUnable to write /usr/local/nagiosxi/tmp/rod_services.csv file\n"; + return false; + } + + return true; +} + + +function send_report_email($debug = false) +{ + $settings_raw = get_option("report_objects_down_options"); + if ($settings_raw == "") { + $settings = array(); + } + else { + $settings = unserialize($settings_raw); + } + + $recipients = grab_array_var($settings, "recipients", false); + + if (empty($recipients)) { + echo "\nNo recipients specified!\n"; + return false; + } + + $recipients = explode(",", $recipients); + foreach ($recipients as $recipient) { + + $options = array( + "referer" => "report_objects_down", + "to" => $recipient, + "subject" => "Objects Down Report - " . date('d-m-Y'), + "high_priority" => true, + "message" => "Objects Down Report", + "plaintext" => "Objects Down Report", + "attachment" => array( + array( + "/usr/local/nagiosxi/tmp/rod_hosts.csv", + "hosts.csv" + ), + array( + "/usr/local/nagiosxi/tmp/rod_services.csv", + "services.csv" + ), + ), + ); + + $dbg = ""; + send_email($options, $dbg, $options["referer"], $options["plaintext"]); + + if ($debug) { + echo "send_email debug: $dbg\n"; + } + } + + return true; +} diff --git a/report_objects_down/report_objects_down.inc.php b/report_objects_down/report_objects_down.inc.php new file mode 100644 index 0000000..580fd76 --- /dev/null +++ b/report_objects_down/report_objects_down.inc.php @@ -0,0 +1,177 @@ +Error: This component requires Nagios XI 5.6.0 or later."; + } + + register_component("report_objects_down", array( + COMPONENT_NAME => "report_objects_down", + COMPONENT_AUTHOR => "hedenface", + COMPONENT_DESCRIPTION => $desc, + COMPONENT_TITLE => "Objects Down Report", + COMPONENT_VERSION => "0.0.0", + COMPONENT_DATE => "05/07/2019", + COMPONENT_CONFIGFUNCTION => "report_objects_down_config", + ) + ); + + if (!report_objects_down_check_version()) { + return; + } + + register_callback(CALLBACK_MENUS_INITIALIZED, 'report_objects_down_component_addmenu'); +} + + +function report_objects_down_check_version() +{ + if (function_exists('get_product_release')) { + if (get_product_release() >= 5600) { + return true; + } + } + + return false; +} + + +function report_objects_down_component_addmenu() +{ + $component_url = get_component_url_base("report_objects_down"); + + $menu_section = find_menu_item(MENU_REPORTS, "menu-reports-nagiosxi", "id"); + if ($menu_section == null) { + return false; + } + + $order = grab_array_var($menu_section, "order", ""); + $new_order = $order + 0.1; + if ($new_order < 0) { + return false; + } + + add_menu_item(MENU_REPORTS, array( + "type" => "link", + "title" => "Objects Down", + "id" => "menu-reports-nagiosxi", + "order" => $new_order, + "opts" => array( + "href" => $component_url . "/index.php", + ) + )); + + add_menu_item(MENU_REPORTS, array( + "type" => "linkspacer", + "id" => "menu-reports-avail-report-spacer", + "order" => $new_order + 0.1 + )); +} + + +function report_objects_down_config($mode = "", $inargs, &$outargs, &$result) +{ + $result = 0; + $output = ""; + + $component_name = "report_objects_down"; + + switch ($mode) { + + case COMPONENT_CONFIGMODE_GETSETTINGSHTML: + + $settings_raw = get_option("report_objects_down_options"); + if ($settings_raw == "") { + $settings = array(); + } + else { + $settings = unserialize($settings_raw); + } + + // initial values + $time_of_day_to_send = grab_array_var($settings, "time", "5:00"); + $recipients = grab_array_var($settings, "recipients", false); + + // values passed to us + $time_of_day_to_send = grab_array_var($inargs, "time", $time_of_day_to_send); + $recipients = grab_array_var($inargs, "recipients", $recipients); + + $output = ' +

Use the Objects Down Report configuration to adjust the time of day (Monday - Friday) that the report is sent, and who it is sent to.

+
Objects Down Report
+ + + + + + + + + +
+ + + +
+ + + +
'; + + break; + + case COMPONENT_CONFIGMODE_SAVESETTINGS: + + // get variables + $time_of_day_to_send = grab_array_var($inargs, "time", "5:00"); + $recipients = grab_array_var($inargs, "recipients", ""); + + // validate variables + $errors = 0; + $errmsg = array(); + if (!is_valid_time_of_day($time_of_day_to_send)) { + $errmsg[$errors++] = "Invalid time specified [00-23]:[00-59]"; + } + + // handle errors + if ($errors > 0) { + $outargs[COMPONENT_ERROR_MESSAGES] = $errmsg; + $result = 1; + return ''; + } + + // save settings + $settings = array( + "time" => $time_of_day_to_send, + "recipients" => $recipients, + ); + set_option("report_objects_down_options", serialize($settings)); + + break; + + default: + break; + } + + return $output; +} + + +function is_valid_time_of_day($time) +{ + if (strlen($time) < 5) { + $time = "0" . $time; + } + + $format = "Y-m-d H:i:s"; + $date = date("Y-m-d") . " $time:00"; + $dateObj = DateTime::createFromFormat($format, $date); + return $dateObj && $dateObj->format($format) == $date; +} diff --git a/report_objects_down/report_objects_down.inc.php.save b/report_objects_down/report_objects_down.inc.php.save new file mode 100644 index 0000000..ddbda55 --- /dev/null +++ b/report_objects_down/report_objects_down.inc.php.save @@ -0,0 +1,177 @@ +Error: This component requires Nagios XI 5.6.0 or later."; + } + + register_component("report_objects_down", array( + COMPONENT_NAME => "report_objects_down", + COMPONENT_AUTHOR => "hedenface", + COMPONENT_DESCRIPTION => $desc, + COMPONENT_TITLE => "Objects Down Report", + COMPONENT_VERSION => "0.0.0", + COMPONENT_DATE => "05/07/2019", + COMPONENT_CONFIGFUNCTION => "report_objects_down_config", + ) + ); + + if (!report_objects_down_check_version()) { + return; + } + + register_callback(CALLBACK_MENUS_INITIALIZED, 'report_objects_down_component_addmenu'); +} + + +function report_objects_down_check_version() +{ + if (function_exists('get_product_release')) { + if (get_product_release() >= 5600) { + return true; + } + } + + return false; +} + + +function report_objects_down_component_addmenu() +{ + $component_url = get_component_url_base("report_objects_down"); + + $menu_section = find_menu_item(MENU_REPORTS, "menu-reports-nagiosxi", "id"); + if ($menu_section == null) { + return false; + } + + $order = grab_array_var($menu_section, "order", ""); + $new_order = $order + 0.1; + if ($new_order < 0) { + return false; + } + + add_menu_item(MENU_REPORTS, array( + "type" => "link", + "title" => "Objects Down", + "id" => "menu-reports-nagiosxi", + "order" => $new_order, + "opts" => array( + "href" => $component_url . "/index.php", + ) + )); + + add_menu_item(MENU_REPORTS, array( + "type" => "linkspacer", + "id" => "menu-reports-avail-report-spacer", + "order" => $new_order + 0.1 + )); +} + + +function report_objects_down_config($mode = "", $inargs, &$outargs, &$result) +{ + $result = 0; + $output = ""; + + $component_name = "report_objects_down"; + + switch ($mode) { + + case COMPONENT_CONFIGMODE_GETSETTINGSHTML: + + $settings_raw = get_option("report_objects_down_options"); + if ($settings_raw == "") { + $settings = array(); + } + else { + $settings = unserialize($settings_raw); + } + + // initial values + $time_of_day_to_send = grab_array_var($settings, "time", "5:00"); + $recipients = grab_array_var($settings, "recipients", false); + + // values passed to us + $time_of_day_to_send = grab_array_var($inargs, "time", $time_of_day_to_send); + $recipients = grab_array_var($inargs, "recipients", $recipients); + + $output = ' +

Use the Objects Down Report configuration to adjust the time of day (Monday - Friday) that the report is sent, and who it is sent to.

+
Objects Down Report
+ + + + + + + + + +
+ + + +
+ + + +
'; + + break; + + case COMPONENT_CONFIGMODE_SAVESETTINGS: + + // get variables + $time_of_day_to_send = grab_array_var($inargs, "time", "5:00"); + $recipients = grab_array_var($inargs, "recipients", ""); + + // validate variables + $errors = 0; + $errmsg = array(); + if (!is_valid_time_of_day($time_of_day_to_send)) { + $errmsg[$errors++] = "Invalid time specified [00-23]:[00-59]"; + } + + // handle errors + if ($errors > 0) { + $outargs[COMPONENT_ERROR_MESSAGES] = $errmsg; + $result = 1; + return ''; + } + + // save settings + $settings = array( + "time" => $time_of_day_to_send, + "recipients" => $recipients, + ); + set_option("report_objects_down_options", serialize($settings)); + + break; + + default: + break; + } + + return $output; +} + + +function is_valid_time_of_day($time) +{ + if (strlen($time) < 5) { + $time = "0" . $time; + } + + $format = "Y-m-d H:i:s"; + $date = date("Y-m-d") . " $time:00"; + $dateObj = DateTime::createFromFormat($format, $date); + return $dateObj && $dateObj->format($format) == $date; +} diff --git a/report_objects_down/setup.sh b/report_objects_down/setup.sh new file mode 100755 index 0000000..4f861ca --- /dev/null +++ b/report_objects_down/setup.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +if [ -f cron.php ]; then + cp cron.php /usr/local/nagiosxi/cron/report_objects_down.cron.php + chown nagios:nagios /usr/local/nagiosxi/cron/report_objects_down.cron.php + echo "* * * * * nagios /usr/bin/php -q /usr/local/nagiosxi/cron/report_objects_down.cron.php >> /usr/local/nagiosxi/var/report_objects_down.log 2>&1" >> /etc/cron.d/nagiosxi +else + echo "Please run this setup.sh from within the component directory" + exit 1 +fi