From 52455e1128b6cd7fa9c38d1149c5c4ad6842d4b0 Mon Sep 17 00:00:00 2001 From: Aaron Daniels Date: Thu, 10 Mar 2016 17:30:32 +1000 Subject: [PATCH 1/4] Component Update - Status - Align the component status field with the Nagios standard 0=ok, 1=warning, 2=critical - Modify existing modules to report these status' (Cisco-OTV) - Add/Modify Alerting Macros to use these status' - Add the a component status widget - update edit page to report these status' --- doc/Extensions/Component.md | 17 ++-- html/includes/common/component-status.inc.php | 41 +++++++++ html/includes/table/component.inc.php | 17 +++- html/pages/device/edit/component.inc.php | 20 ++++- html/pages/device/routing/cisco-otv.inc.php | 4 +- html/pages/routing/cisco-otv.inc.php | 4 +- includes/component.php | 87 ++++++++++++++++++- includes/discovery/cisco-otv.inc.php | 8 +- includes/polling/cisco-otv.inc.php | 8 +- sql-schema/{122.sql => 124.sql} | 0 10 files changed, 181 insertions(+), 25 deletions(-) create mode 100644 html/includes/common/component-status.inc.php rename sql-schema/{122.sql => 124.sql} (100%) diff --git a/doc/Extensions/Component.md b/doc/Extensions/Component.md index 5fcf3838ff..2d3663b363 100644 --- a/doc/Extensions/Component.md +++ b/doc/Extensions/Component.md @@ -23,7 +23,7 @@ Table of Content: The Component extension provides a generic database storage mechanism for discovery and poller modules. The Driver behind this extension was to provide the features of ports, in a generic manner to discovery/poller modules. -It provides a status (Normal or Alert), the ability to Disable (do not poll), or Ignore (do not Alert). +It provides a status (Nagios convention), the ability to Disable (do not poll), or Ignore (do not Alert). # Database Structure @@ -282,18 +282,23 @@ Please see the [API-Docs](http://docs.librenms.org/API/API-Docs/#api-route-25) f ## Alerting It is intended that discovery/poller modules will detect the status of a component during the polling cycle. -If you are creating a poller module which can detect a fault condition simply set STATUS to 0 and ERROR to a message that indicates the problem. +Status is logged using the Nagios convention for status codes, where: + 0 = Ok, + 1 = Warning, + 2 = Critical +If you are creating a poller module which can detect a fault condition simply set STATUS to something other than 0 and ERROR to a message that indicates the problem. To actually raise an alert, the user will need to create an alert rule. To assist with this several Alerting Macro's have been created: -- ```%macro.component_alert``` - A component that is not disabled or ignored and in alert state. -- ```%macro.component_normal``` - A component that is not disabled or ignored and NOT in alert state. +- ```%macro.component_normal``` - A component that is not disabled or ignored and in a Normal state. +- ```%macro.component_warning``` - A component that is not disabled or ignored and NOT in a Warning state. +- ```%macro.component_critical``` - A component that is not disabled or ignored and NOT in a Critical state. To raise alerts for components, the following rules could be created: -- ```%macros.component_alert = "1"``` - To alert on all components -- ```%macros.component_alert = "1" && %component.type = ""``` - To alert on all components of a particular type. +- ```%macros.component_critical = "1"``` - To alert on all Critical components +- ```%macros.component_critical = "1" && %component.type = ""``` - To alert on all Critical components of a particular type. If there is a particular component you would like excluded from alerting, simply set the ignore field to 1. diff --git a/html/includes/common/component-status.inc.php b/html/includes/common/component-status.inc.php new file mode 100644 index 0000000000..8a0b1c261d --- /dev/null +++ b/html/includes/common/component-status.inc.php @@ -0,0 +1,41 @@ + + + + + + + + + +'; +foreach ($OBJCOMP->getComponentStatus() as $k => $v) { + if ($k == 0) { + $status = 'Ok'; + $color = 'green'; + } + elseif ($k == 1) { + $status = 'Warning'; + $color = 'grey'; + } + else { + $status = 'Critical'; + $color = 'red'; + } + $common_output[] .= ' + + + + +'; +} +$common_output[] .= ' + +
StatusCount

'.$status.'

'.$v.'

+ +'; diff --git a/html/includes/table/component.inc.php b/html/includes/table/component.inc.php index 7a0873648d..debc50b9e2 100644 --- a/html/includes/table/component.inc.php +++ b/html/includes/table/component.inc.php @@ -34,17 +34,30 @@ $COMPONENTS = $OBJCOMP->getComponents($device_id,$options); $response[] = array( 'id' => '', 'label' => ' ', - 'status' => '', + 'status' => ' ', 'disable' => '', 'ignore' => '', ); foreach ($COMPONENTS[$device_id] as $ID => $AVP) { + if ($AVP['status'] == 0) { + $class = "green"; + $status = "Ok"; + } + elseif ($AVP['status'] == 1) { + $class = "grey"; + $status = "Warning"; + } + else { + // Critical + $class = "red"; + $status = "Critical"; + } $response[] = array( 'id' => $ID, 'type' => $AVP['type'], 'label' => $AVP['label'], - 'status' => ($AVP['status'] ? "Normal" : "Alert"), + 'status' => "".$status."", 'disable' => '', 'ignore' => '', ); diff --git a/html/pages/device/edit/component.inc.php b/html/pages/device/edit/component.inc.php index 36f21ab34b..5bf25ba197 100644 --- a/html/pages/device/edit/component.inc.php +++ b/html/pages/device/edit/component.inc.php @@ -44,13 +44,27 @@ event.preventDefault(); $('.ignore-check').prop('checked', true); }); - $('#alert-select').click(function (event) { - // select ignore buttons for all ports which are down + $('#warning-select').click(function (event) { + // select ignore button for all components that are in a warning state. event.preventDefault(); $('[name^="status_"]').each(function () { var name = $(this).attr('name'); var text = $(this).text(); - if (name && text == 'Alert') { + if (name && text == 'Warning') { + // get the component number from the object name + var id = name.split('_')[1]; + // find its corresponding checkbox and toggle it + $('input[name="ign_' + id + '"]').trigger('click'); + } + }); + }); + $('#critical-select').click(function (event) { + // select ignore button for all components that are in a critical state. + event.preventDefault(); + $('[name^="status_"]').each(function () { + var name = $(this).attr('name'); + var text = $(this).text(); + if (name && text == 'Critical') { // get the component number from the object name var id = name.split('_')[1]; // find its corresponding checkbox and toggle it diff --git a/html/pages/device/routing/cisco-otv.inc.php b/html/pages/device/routing/cisco-otv.inc.php index 81fc19511d..c723686eb9 100644 --- a/html/pages/device/routing/cisco-otv.inc.php +++ b/html/pages/device/routing/cisco-otv.inc.php @@ -19,7 +19,7 @@ global $config; // Loop over each component, pulling out the Overlays. foreach ($components as $oid => $overlay) { if ($overlay['otvtype'] == 'overlay') { - if ($overlay['status'] == 1) { + if ($overlay['status'] == 0) { $overlay_status = "Normal"; $gli = ""; } @@ -33,7 +33,7 @@ foreach ($components as $oid => $overlay) { $adjacency) { if (($adjacency['otvtype'] == 'adjacency') && ($adjacency['index'] == $overlay['index'])) { - if ($adjacency['status'] == 1) { + if ($adjacency['status'] == 0) { $adj_status = "Normal"; $gli = ""; } diff --git a/html/pages/routing/cisco-otv.inc.php b/html/pages/routing/cisco-otv.inc.php index d1654fa286..63e9d01463 100644 --- a/html/pages/routing/cisco-otv.inc.php +++ b/html/pages/routing/cisco-otv.inc.php @@ -19,7 +19,7 @@ foreach ($COMPONENTS as $DEVICE_ID => $COMP) { // Loop over each component, pulling out the Overlays. foreach ($COMP as $OID => $OVERLAY) { if ($OVERLAY['otvtype'] == 'overlay') { - if ($OVERLAY['status'] == 1) { + if ($OVERLAY['status'] == 0) { $OVERLAY_STATUS = "Normal"; $GLI = ""; } @@ -33,7 +33,7 @@ foreach ($COMPONENTS as $DEVICE_ID => $COMP) { $ADJACENCY) { if (($ADJACENCY['otvtype'] == 'adjacency') && ($ADJACENCY['index'] == $OVERLAY['index'])) { - if ($ADJACENCY['status'] == 1) { + if ($ADJACENCY['status'] == 0) { $ADJ_STATUS = "Normal"; $GLI = ""; } diff --git a/includes/component.php b/includes/component.php index 180f0a8da2..27713f6e98 100644 --- a/includes/component.php +++ b/includes/component.php @@ -20,7 +20,7 @@ class component { private $reserved = array( 'type' => '', 'label' => '', - 'status' => 1, + 'status' => 0, 'ignore' => 0, 'disabled' => 0, 'error' => '', @@ -148,6 +148,69 @@ class component { return $RESULT; } + public function getComponentStatus($device=null) { + $sql_query = "SELECT status, count(status) as count FROM component WHERE"; + $sql_param = array(); + $add = 0; + + if (!is_null($device)) { + // Add a device filter to the SQL query. + $sql_query .= " `device_id` = ?"; + $sql_param[] = $device; + $add++; + } + + if ($add == 0) { + // No filters, remove " WHERE" -6 + $sql_query = substr($sql_query, 0, strlen($sql_query)-6); + } + $sql_query .= " GROUP BY status"; + d_echo("SQL Query: ".$sql_query); + + // $service is not null, get only what we want. + $result = dbFetchRows($sql_query, $sql_param); + + // Set our defaults to 0 + $count = array(0 => 0, 1 => 0, 2 => 0); + // Rebuild the array in a more convenient method + foreach ($result as $v) { + $count[$v['status']] = $v['count']; + } + + d_echo("Component Count by Status: ".print_r($count,TRUE)."\n"); + return $count; + } + + public function getComponentStatusLog($component=null,$start=null,$end=null) { + if ( ($component == null) || ($start == null) || ($end == null) ) { + // Error... + d_echo("Required arguments are missing. Component: ".$component.", Start: ".$start.", End: ".$end."\n"); + return false; + } + + // Create our return array. + $return = array(); + + // 1. find the previous value, this is the value when $start occurred. + $sql_query = "SELECT status FROM component_statuslog WHERE `component` = ? AND time < ? ORDER BY `id` desc LIMIT 1"; + $sql_param = array($component,$start); + $result = dbFetchRow($sql_query, $sql_param); + if ($result == false) { + $return['initial'] = false; + } + else { + $return['initial'] = $result['status']; + } + + // 2. Then we just need a list of all the entries for the time period. + $sql_query = "SELECT status, time, message FROM component_statuslog WHERE `component` = ? AND time >= ? AND time < ? ORDER BY `time`"; + $sql_param = array($component,$start,$end); + $return['data'] = dbFetchRows($sql_query, $sql_param); + + d_echo("Status Log Data: ".print_r($return,TRUE)."\n"); + return $return; + } + public function createComponent ($device_id,$TYPE) { // Prepare our default values to be inserted. $DATA = $this->reserved; @@ -159,6 +222,9 @@ class component { // Insert a new component into the database. $id = dbInsert($DATA, 'component'); + // Add a default status log entry - we always start ok. + $this->createStatusLogEntry($id,0,'Component Created'); + // Create a default component array based on what was inserted. $ARRAY = array(); $ARRAY[$id] = $DATA; @@ -166,6 +232,17 @@ class component { return $ARRAY; } + public function createStatusLogEntry($component,$status,$message) { + // Add an entry to the statuslog table for a particular component. + $DATA = array( + 'component' => $component, + 'status' => $status, + 'message' => $message, + ); + + return dbInsert($DATA, 'component_statuslog'); + } + public function deleteComponent ($id) { // Delete a component from the database. return dbDelete('component', "`id` = ?",array($id)); @@ -187,6 +264,12 @@ class component { // Ignore type, we cant change that. unset($AVP['type'],$OLD[$device_id][$COMPONENT]['type']); + // If the Status has changed we need to add a log entry + if ($AVP['status'] != $OLD[$device_id][$COMPONENT]['status']) { + d_echo("Status Changed - Old: ".$OLD[$device_id][$COMPONENT]['status'].", New: ".$AVP['status']."\n"); + $this->createStatusLogEntry($COMPONENT,$AVP['status'],$AVP['error']); + } + // Process our reserved components first. $UPDATE = array(); foreach ($this->reserved as $k => $v) { @@ -239,7 +322,7 @@ class component { log_event("Component: ".$AVP[$COMPONENT]['type']."(".$COMPONENT."). Attribute: ".$ATTR.", was modified from: ".$OLD[$COMPONENT][$ATTR].", to: ".$VALUE,$device_id,'component',$COMPONENT); } - } // End Foreach COMPONENT + } // End Foreach AVP // Process our Deletes. $DELETE = array_diff_key($OLD[$device_id][$COMPONENT], $AVP); diff --git a/includes/discovery/cisco-otv.inc.php b/includes/discovery/cisco-otv.inc.php index 53b9658603..066a62c0c2 100644 --- a/includes/discovery/cisco-otv.inc.php +++ b/includes/discovery/cisco-otv.inc.php @@ -116,11 +116,11 @@ if ($device['os_group'] == 'cisco') { // If we have set a message, we have an error, activate alert. if ($message !== false) { $result['error'] = $message; - $result['status'] = 0; + $result['status'] = 2; } else { $result['error'] = ""; - $result['status'] = 1; + $result['status'] = 0; } // Let's log some debugging @@ -154,11 +154,11 @@ if ($device['os_group'] == 'cisco') { // If we have set a message, we have an error, activate alert. if ($message !== false) { $result['error'] = $message; - $result['status'] = 0; + $result['status'] = 1; } else { $result['error'] = ""; - $result['status'] = 1; + $result['status'] = 0; } // Set a default name, if for some unknown reason we cant find the parent VPN. diff --git a/includes/polling/cisco-otv.inc.php b/includes/polling/cisco-otv.inc.php index 6fdaeff3b1..3b3e68a72d 100644 --- a/includes/polling/cisco-otv.inc.php +++ b/includes/polling/cisco-otv.inc.php @@ -106,11 +106,11 @@ if ($device['os_group'] == "cisco") { // If we have set a message, we have an error, activate alert. if ($message !== false) { $array['error'] = $message; - $array['status'] = 0; + $array['status'] = 2; } else { $array['error'] = ""; - $array['status'] = 1; + $array['status'] = 0; } // Time to graph the count of the active VLAN's on this overlay. @@ -154,11 +154,11 @@ if ($device['os_group'] == "cisco") { // If we have set a message, we have an error, activate alert. if ($message !== false) { $array['error'] = $message; - $array['status'] = 0; + $array['status'] = 1; } else { $array['error'] = ""; - $array['status'] = 1; + $array['status'] = 0; } // Let's log some debugging diff --git a/sql-schema/122.sql b/sql-schema/124.sql similarity index 100% rename from sql-schema/122.sql rename to sql-schema/124.sql From 8d01da96e27a6aa7b8e35f72935ae8ad7c901c1e Mon Sep 17 00:00:00 2001 From: Aaron Daniels Date: Fri, 15 Jul 2016 16:26:16 +1000 Subject: [PATCH 2/4] - Fixed SQL rename. --- sql-schema/122.sql | 3 +++ sql-schema/124.sql | 12 +++++++++--- 2 files changed, 12 insertions(+), 3 deletions(-) create mode 100644 sql-schema/122.sql diff --git a/sql-schema/122.sql b/sql-schema/122.sql new file mode 100644 index 0000000000..1610949a10 --- /dev/null +++ b/sql-schema/122.sql @@ -0,0 +1,3 @@ +ALTER TABLE `sensors_to_state_indexes` DROP FOREIGN KEY `sensors_to_state_indexes_ibfk_1`; +ALTER TABLE `sensors_to_state_indexes` DROP FOREIGN KEY `sensors_to_state_indexes_sensor_id_foreign`; +ALTER TABLE `sensors_to_state_indexes` ADD CONSTRAINT `sensors_to_state_indexes_sensor_id_foreign` FOREIGN KEY (`sensor_id`) REFERENCES `sensors` (`sensor_id`) ON DELETE CASCADE; diff --git a/sql-schema/124.sql b/sql-schema/124.sql index 1610949a10..7ceea3f90e 100644 --- a/sql-schema/124.sql +++ b/sql-schema/124.sql @@ -1,3 +1,9 @@ -ALTER TABLE `sensors_to_state_indexes` DROP FOREIGN KEY `sensors_to_state_indexes_ibfk_1`; -ALTER TABLE `sensors_to_state_indexes` DROP FOREIGN KEY `sensors_to_state_indexes_sensor_id_foreign`; -ALTER TABLE `sensors_to_state_indexes` ADD CONSTRAINT `sensors_to_state_indexes_sensor_id_foreign` FOREIGN KEY (`sensor_id`) REFERENCES `sensors` (`sensor_id`) ON DELETE CASCADE; +DROP TABLE IF EXISTS `component_statuslog`; +CREATE TABLE `component_statuslog` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID for each log entry, unique index', `component_id` int(11) unsigned NOT NULL COMMENT 'id from the component table', `status` tinyint(1) NOT NULL DEFAULT '0' COMMENT 'The status that the component was changed TO', `timestamp` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'When the status of the component was changed', PRIMARY KEY (`id`), KEY `device` (`component_id`), CONSTRAINT `component_statuslog_ibfk_1` FOREIGN KEY (`component_id`) REFERENCES `component` (`id`) ON DELETE CASCADE ON UPDATE CASCADE ) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='log of status changes to a component.'; +ALTER TABLE `component` CHANGE `status` `status` TINYINT(1) NOT NULL DEFAULT '0' COMMENT 'The status of the component, retreived from the device'; +UPDATE `config` SET `config_name`='alert.macros.rule.component_warning',`config_descr`='Component status Warning' WHERE `config_name`='alert.macros.rule.component_normal'; +UPDATE `config` SET `config_name`='alert.macros.rule.component_normal',`config_descr`='Component status Normal' WHERE `config_name`='alert.macros.rule.component_alert'; +INSERT INTO `config` (`config_name`,`config_value`,`config_default`,`config_descr`,`config_group`,`config_group_order`,`config_sub_group`,`config_sub_group_order`,`config_hidden`,`config_disabled`) VALUES ('alert.macros.rule.component_critical','(%component.status = 2 && %macros.component)','(%component.status = 2 && %macros.component)','Component status Critical','alerting',0,'macros',0,'1','0'); +UPDATE component SET status=2 WHERE status=0; +UPDATE component SET status=0 WHERE status=1; +INSERT INTO `widgets` (`widget_title`,`widget`,`base_dimensions`) VALUES ('Component Status','component-status','3,2'); From 1d579e8debb0b92c42fa58ff2bc708e89eefa292 Mon Sep 17 00:00:00 2001 From: Aaron Daniels Date: Mon, 18 Jul 2016 07:14:03 +1000 Subject: [PATCH 3/4] - Moved SQL for upstream changes. --- sql-schema/{124.sql => 125.sql} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename sql-schema/{124.sql => 125.sql} (100%) diff --git a/sql-schema/124.sql b/sql-schema/125.sql similarity index 100% rename from sql-schema/124.sql rename to sql-schema/125.sql From 4c0c560652b3d8de4c8c0546411d76728d97abac Mon Sep 17 00:00:00 2001 From: Aaron Daniels Date: Mon, 25 Jul 2016 06:47:34 +1000 Subject: [PATCH 4/4] - Moved SQL for upstream changes --- sql-schema/{125.sql => 126.sql} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename sql-schema/{125.sql => 126.sql} (100%) diff --git a/sql-schema/125.sql b/sql-schema/126.sql similarity index 100% rename from sql-schema/125.sql rename to sql-schema/126.sql