Merge pull request #3821 from adaniels21487/issue-3272-1

Component Update - Status
This commit is contained in:
Neil Lathwood
2016-07-25 20:13:25 +01:00
committed by GitHub
10 changed files with 190 additions and 25 deletions

View File

@ -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).
# <a name="database">Database Structure</a>
@ -282,18 +282,23 @@ Please see the [API-Docs](http://docs.librenms.org/API/API-Docs/#api-route-25) f
## <a name="alert">Alerting</a>
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 = "<Type of Component>"``` - 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 = "<Type of Component>"``` - 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.

View File

@ -0,0 +1,41 @@
<?php
require_once "../includes/component.php";
$OBJCOMP = new component();
$common_output[] = '
<div>
<table id="component-status" class="table table-hover table-condensed table-striped">
<thead>
<tr>
<th data-column-id="status" data-order="desc">Status</th>
<th data-column-id="count">Count</th>
</tr>
</thead>
<tbody>
';
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[] .= '
<tr>
<td><p class="text-left '.$color.'">'.$status.'</p></td>
<td><p class="text-left '.$color.'">'.$v.'</p></td>
</tr>
';
}
$common_output[] .= '
</tbody>
</table>
</div>
';

View File

@ -34,17 +34,30 @@ $COMPONENTS = $OBJCOMP->getComponents($device_id,$options);
$response[] = array(
'id' => '<button type="submit" id="save-form" class="btn btn-success btn-sm" title="Save current component disable/ignore settings">Save</button><button type="submit" id="form-reset" class="btn btn-danger btn-sm" title="Reset form to when the page was loaded">Reset</button>',
'label' => '&nbsp;',
'status' => '<button type="submit" id="alert-select" class="btn btn-default btn-sm" title="Disable alerting on all currently-alerting components">Alerting</button>',
'status' => '<button type="submit" id="warning-select" class="btn btn-default btn-sm" title="Disable alerting on all currently warning components">Warning</button>&nbsp;<button type="submit" id="critical-select" class="btn btn-default btn-sm" title="Disable alerting on all currently critical components">Critical</button>',
'disable' => '<button type="submit" id="disable-toggle" class="btn btn-default btn-sm" title="Toggle polling for all components">Toggle</button><button type="button" id="disable-select" class="btn btn-default btn-sm" title="Disable polling on all components">Select All</button>',
'ignore' => '<button type="submit" id="ignore-toggle" class="btn btn-default btn-sm" title="Toggle alerting for all components">Toggle</button><button type="button" id="ignore-select" class="btn btn-default btn-sm" title="Disable alerting on all components">Select All</button>',
);
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'] ? "<span name='status_".$ID."' class='green'>Normal</span>" : "<span name='status_".$ID."' class='red'>Alert</span>"),
'status' => "<span name='status_".$ID."' class='".$class."'>".$status."</span>",
'disable' => '<input type="checkbox" class="disable-check" name="dis_'.$ID.'"'.($AVP['disabled'] ? 'checked' : '').'>',
'ignore' => '<input type="checkbox" class="ignore-check" name="ign_'.$ID.'"'.($AVP['ignore'] ? 'checked' : '').'>',
);

View File

@ -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

View File

@ -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 = "<span class='green pull-right'>Normal</span>";
$gli = "";
}
@ -33,7 +33,7 @@ foreach ($components as $oid => $overlay) {
<?php
foreach ($components as $aid => $adjacency) {
if (($adjacency['otvtype'] == 'adjacency') && ($adjacency['index'] == $overlay['index'])) {
if ($adjacency['status'] == 1) {
if ($adjacency['status'] == 0) {
$adj_status = "<span class='green pull-right'>Normal</span>";
$gli = "";
}

View File

@ -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 = "<span class='green pull-right'>Normal</span>";
$GLI = "";
}
@ -33,7 +33,7 @@ foreach ($COMPONENTS as $DEVICE_ID => $COMP) {
<?php
foreach ($COMP as $AID => $ADJACENCY) {
if (($ADJACENCY['otvtype'] == 'adjacency') && ($ADJACENCY['index'] == $OVERLAY['index'])) {
if ($ADJACENCY['status'] == 1) {
if ($ADJACENCY['status'] == 0) {
$ADJ_STATUS = "<span class='green pull-right'>Normal</span>";
$GLI = "";
}

View File

@ -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);

View File

@ -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.

View File

@ -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

9
sql-schema/126.sql Normal file
View File

@ -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');