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 @@
+
+
+
+
+ Status
+ Count
+
+
+
+';
+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[] .= '
+
+ '.$status.'
+ '.$v.'
+
+';
+}
+$common_output[] .= '
+
+
+
+';
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' => 'Save Reset ',
'label' => ' ',
- 'status' => 'Alerting ',
+ 'status' => 'Warning Critical ',
'disable' => 'Toggle Select All ',
'ignore' => 'Toggle Select All ',
);
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/126.sql b/sql-schema/126.sql
new file mode 100644
index 0000000000..7ceea3f90e
--- /dev/null
+++ b/sql-schema/126.sql
@@ -0,0 +1,9 @@
+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');