2016-08-21 13:07:14 +00:00
< ? php
/**
* Component . php
*
* LibreNMS module to Interface with the Component System
*
* This program is free software : you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation , either version 3 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
2021-02-08 23:29:04 +00:00
* along with this program . If not , see < https :// www . gnu . org / licenses />.
2016-08-21 13:07:14 +00:00
*
2021-02-08 23:29:04 +00:00
* @ link https :// www . librenms . org
2021-09-10 18:09:53 +00:00
*
2016-08-21 13:07:14 +00:00
* @ copyright 2015 Aaron Daniels < aaron @ daniels . id . au >
* @ author Aaron Daniels < aaron @ daniels . id . au >
*/
namespace LibreNMS ;
2020-05-08 05:30:56 +00:00
use App\Models\ComponentPref ;
use App\Models\ComponentStatusLog ;
2022-11-09 08:47:19 +00:00
use App\Models\Eventlog ;
2020-05-08 05:30:56 +00:00
use Illuminate\Support\Arr ;
2023-08-05 17:12:36 +00:00
use LibreNMS\Enum\Severity ;
2020-05-08 05:30:56 +00:00
use Log ;
2016-08-28 22:32:55 +00:00
class Component
{
2016-08-21 13:07:14 +00:00
/*
* These fields are used in the component table . They are returned in the array
* so that they can be modified but they can not be set as user attributes . We
* also set their default values .
*/
2020-05-08 05:30:56 +00:00
private $reserved = [
'type' => '' ,
'label' => '' ,
'status' => 0 ,
'ignore' => 0 ,
'disabled' => 0 ,
'error' => '' ,
];
2016-08-21 13:07:14 +00:00
2017-01-19 08:19:11 +00:00
public function getComponentCount ( $device_id = null )
{
2020-05-08 05:30:56 +00:00
$counts = \App\Models\Component :: query () -> when ( $device_id , function ( $query , $device_id ) {
2021-04-08 13:14:49 +00:00
return $query -> where ( 'device_id' , $device_id );
2020-05-08 05:30:56 +00:00
}) -> selectRaw ( 'type, count(*) as count' ) -> groupBy ( 'type' ) -> pluck ( 'count' , 'type' );
2017-01-19 08:19:11 +00:00
2020-05-08 05:30:56 +00:00
return $counts -> isEmpty () ? false : $counts -> all ();
2017-01-19 08:19:11 +00:00
}
2020-05-08 05:30:56 +00:00
2016-08-28 22:32:55 +00:00
public function getComponentType ( $TYPE = null )
{
2016-08-21 13:07:14 +00:00
if ( is_null ( $TYPE )) {
$SQL = 'SELECT DISTINCT `type` as `name` FROM `component` ORDER BY `name`' ;
2020-05-08 05:30:56 +00:00
$row = dbFetchRow ( $SQL , []);
2016-08-28 22:32:55 +00:00
} else {
2016-08-21 13:07:14 +00:00
$SQL = 'SELECT DISTINCT `type` as `name` FROM `component` WHERE `type` = ? ORDER BY `name`' ;
2020-05-08 05:30:56 +00:00
$row = dbFetchRow ( $SQL , [ $TYPE ]);
2016-08-21 13:07:14 +00:00
}
if ( ! isset ( $row )) {
// We didn't find any component types
return false ;
2016-08-28 22:32:55 +00:00
} else {
2016-08-21 13:07:14 +00:00
// We found some..
return $row ;
}
}
2020-05-08 05:30:56 +00:00
public function getComponents ( $device_id = null , $options = [])
2016-08-28 22:32:55 +00:00
{
2020-05-08 05:30:56 +00:00
$query = \App\Models\Component :: query ()
-> with ( 'prefs' );
2016-08-21 13:07:14 +00:00
// Device_id is shorthand for filter C.device_id = $device_id.
if ( ! is_null ( $device_id )) {
2020-05-08 05:30:56 +00:00
$options [ 'filter' ][ 'device_id' ] = [ '=' , $device_id ];
2016-08-21 13:07:14 +00:00
}
// Type is shorthand for filter type = $type.
if ( isset ( $options [ 'type' ])) {
2020-05-08 05:30:56 +00:00
$options [ 'filter' ][ 'type' ] = [ '=' , $options [ 'type' ]];
2016-08-21 13:07:14 +00:00
}
2020-05-08 05:30:56 +00:00
$validFields = [ 'device_id' , 'type' , 'id' , 'label' , 'status' , 'disabled' , 'ignore' , 'error' ];
2016-08-21 13:07:14 +00:00
// filter field => array(operator,value)
// Filters results based on the field, operator and value
2020-05-08 05:30:56 +00:00
foreach ( array_intersect_key ( $options [ 'filter' ], array_flip ( $validFields )) as $field => $filter ) {
$op = $filter [ 0 ];
$value = $op == 'LIKE' ? " % { $filter [ 1 ] } % " : $filter [ 1 ];
$query -> where ( $field , $op , $value );
2016-08-21 13:07:14 +00:00
}
// sort column direction
// Add SQL sorting to the results
if ( isset ( $options [ 'sort' ])) {
2020-05-08 05:30:56 +00:00
[ $column , $direction ] = explode ( ' ' , $options [ 'sort' ]);
$query -> orderBy ( $column , $direction );
2016-08-21 13:07:14 +00:00
}
// limit array(start,count)
if ( isset ( $options [ 'limit' ])) {
2020-05-08 05:30:56 +00:00
$query -> offset ( $options [ 'limit' ][ 0 ]) -> limit ( $options [ 'limit' ][ 1 ]);
2016-08-21 13:07:14 +00:00
}
2020-05-08 05:30:56 +00:00
// get and format results as expected by receivers
return $query -> get () -> groupBy ( 'device_id' ) -> map ( function ( $group ) {
return $group -> keyBy ( 'id' ) -> map ( function ( $component ) {
return $component -> prefs -> pluck ( 'value' , 'attribute' )
-> merge ( $component -> only ( array_keys ( $this -> reserved )));
});
}) -> toArray ();
2016-08-21 13:07:14 +00:00
}
2016-08-28 22:32:55 +00:00
public function getComponentStatus ( $device = null )
{
2016-08-21 13:07:14 +00:00
$sql_query = 'SELECT status, count(status) as count FROM component WHERE' ;
2020-05-08 05:30:56 +00:00
$sql_param = [];
2016-08-21 13:07:14 +00:00
$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
2020-05-08 05:30:56 +00:00
$sql_query = substr ( $sql_query , 0 , strlen ( $sql_query ) - 6 );
2016-08-21 13:07:14 +00:00
}
$sql_query .= ' GROUP BY status' ;
2020-05-08 05:30:56 +00:00
d_echo ( 'SQL Query: ' . $sql_query );
2016-08-21 13:07:14 +00:00
// $service is not null, get only what we want.
$result = dbFetchRows ( $sql_query , $sql_param );
// Set our defaults to 0
2020-05-08 05:30:56 +00:00
$count = [ 0 => 0 , 1 => 0 , 2 => 0 ];
2016-08-21 13:07:14 +00:00
// Rebuild the array in a more convenient method
foreach ( $result as $v ) {
$count [ $v [ 'status' ]] = $v [ 'count' ];
}
2020-05-08 05:30:56 +00:00
d_echo ( 'Component Count by Status: ' . print_r ( $count , true ) . " \n " );
2020-09-21 12:54:51 +00:00
2016-08-21 13:07:14 +00:00
return $count ;
}
2017-11-13 20:43:28 +00:00
public function getComponentStatusLog ( $component_id , $start , $end )
2016-08-28 22:32:55 +00:00
{
2017-11-13 20:43:28 +00:00
if (( $component_id == null ) || ( $start == null ) || ( $end == null )) {
2016-08-21 13:07:14 +00:00
// Error...
2020-05-08 05:30:56 +00:00
d_echo ( 'Required arguments are missing. Component ID: ' . $component_id . ', Start: ' . $start . ', End: ' . $end . " \n " );
2020-09-21 12:54:51 +00:00
2016-08-21 13:07:14 +00:00
return false ;
}
// Create our return array.
2020-05-08 05:30:56 +00:00
$return = [];
2016-08-21 13:07:14 +00:00
// 1. find the previous value, this is the value when $start occurred.
2017-11-13 20:43:28 +00:00
$sql_query = 'SELECT status FROM `component_statuslog` WHERE `component_id` = ? AND `timestamp` < ? ORDER BY `id` desc LIMIT 1' ;
2020-05-08 05:30:56 +00:00
$sql_param = [ $component_id , $start ];
2016-08-21 13:07:14 +00:00
$result = dbFetchRow ( $sql_query , $sql_param );
if ( $result == false ) {
$return [ 'initial' ] = false ;
2016-08-28 22:32:55 +00:00
} else {
2016-08-21 13:07:14 +00:00
$return [ 'initial' ] = $result [ 'status' ];
}
// 2. Then we just need a list of all the entries for the time period.
2017-11-13 20:43:28 +00:00
$sql_query = 'SELECT status, `timestamp`, message FROM `component_statuslog` WHERE `component_id` = ? AND `timestamp` >= ? AND `timestamp` < ? ORDER BY `timestamp`' ;
2020-05-08 05:30:56 +00:00
$sql_param = [ $component_id , $start , $end ];
2016-08-21 13:07:14 +00:00
$return [ 'data' ] = dbFetchRows ( $sql_query , $sql_param );
2020-05-08 05:30:56 +00:00
d_echo ( 'Status Log Data: ' . print_r ( $return , true ) . " \n " );
2020-09-21 12:54:51 +00:00
2016-08-21 13:07:14 +00:00
return $return ;
}
2020-05-08 05:30:56 +00:00
public function createComponent ( $device_id , $type )
2016-08-28 22:32:55 +00:00
{
2020-05-08 05:30:56 +00:00
$component = \App\Models\Component :: create ([ 'device_id' => $device_id , 'type' => $type ]);
2016-08-21 13:07:14 +00:00
// Add a default status log entry - we always start ok.
2020-05-08 05:30:56 +00:00
$this -> createStatusLogEntry ( $component -> id , 0 , 'Component Created' );
2016-08-21 13:07:14 +00:00
// Create a default component array based on what was inserted.
2020-05-08 05:30:56 +00:00
return [ $component -> id => $component -> only ( array_keys ( $this -> reserved ))];
2016-08-21 13:07:14 +00:00
}
2017-11-13 20:43:28 +00:00
public function createStatusLogEntry ( $component_id , $status , $message )
2016-08-28 22:32:55 +00:00
{
2020-05-08 05:30:56 +00:00
try {
return ComponentStatusLog :: create ([ 'component_id' => $component_id , 'status' => $status , 'message' => $message ]) -> id ;
} catch ( \Exception $e ) {
Log :: debug ( 'Failed to create component status log' );
}
return 0 ;
2016-08-21 13:07:14 +00:00
}
2016-08-28 22:32:55 +00:00
public function deleteComponent ( $id )
{
2016-08-21 13:07:14 +00:00
// Delete a component from the database.
2020-05-08 05:30:56 +00:00
return \App\Models\Component :: destroy ( $id );
2016-08-21 13:07:14 +00:00
}
2020-05-08 05:30:56 +00:00
public function setComponentPrefs ( $device_id , $updated )
2016-08-28 22:32:55 +00:00
{
2020-05-08 05:30:56 +00:00
$updated = Arr :: wrap ( $updated );
2022-05-16 07:57:58 +00:00
\App\Models\Component :: whereIntegerInRaw ( 'id' , array_keys ( $updated ))
2020-05-08 05:30:56 +00:00
-> with ( 'prefs' )
-> get ()
2021-03-23 22:08:42 +00:00
-> each ( function ( \App\Models\Component $component ) use ( $device_id , $updated ) {
2020-06-05 01:36:43 +00:00
$update = $updated [ $component -> id ];
unset ( $update [ 'type' ]); // can't change type
2020-05-08 05:30:56 +00:00
// update component attributes
2020-06-05 01:36:43 +00:00
$component -> fill ( $update );
2020-05-08 05:30:56 +00:00
if ( $component -> isDirty ()) {
// Log the update to the Eventlog.
$message = " Component $component->id has been modified: " ;
$message .= collect ( $component -> getDirty ()) -> map ( function ( $value , $key ) {
return " $key => $value " ;
}) -> implode ( ',' );
// If the Status has changed we need to add a log entry
if ( $component -> isDirty ( 'status' )) {
Log :: debug ( 'Status Changed - Old: ' . $component -> getOriginal ( 'status' ) . " , New: $component->status\n " );
$this -> createStatusLogEntry ( $component -> id , $component -> status , $component -> error );
2016-08-21 13:07:14 +00:00
}
2020-05-08 05:30:56 +00:00
$component -> save ();
2016-08-21 13:07:14 +00:00
2023-08-05 17:12:36 +00:00
Eventlog :: log ( $message , $component -> device_id , 'component' , Severity :: Notice , $component -> id );
2016-08-21 13:07:14 +00:00
}
2020-05-08 05:30:56 +00:00
// update preferences
$prefs = collect ( $updated [ $component -> id ]) -> filter ( function ( $value , $attr ) {
return ! array_key_exists ( $attr , $this -> reserved );
});
$invalid = $component -> prefs -> keyBy ( 'id' );
foreach ( $prefs as $attribute => $value ) {
$existing = $component -> prefs -> firstWhere ( 'attribute' , $attribute );
if ( $existing ) {
$invalid -> forget ( $existing -> id );
$existing -> fill ([ 'value' => $value ]);
if ( $existing -> isDirty ()) {
2023-08-05 17:12:36 +00:00
Eventlog :: log ( " Component: $component->type ( $component->id ). Attribute: $attribute , was modified from: " . $existing -> getOriginal ( 'value' ) . " , to: $value " , $device_id , 'component' , Severity :: Notice , $component -> id );
2020-05-08 05:30:56 +00:00
$existing -> save ();
}
} else {
$component -> prefs () -> save ( new ComponentPref ([ 'attribute' => $attribute , 'value' => $value ]));
2023-08-05 17:12:36 +00:00
Eventlog :: log ( " Component: $component->type ( $component->id ). Attribute: $attribute , was added with value: $value " , $component -> device_id , 'component' , Severity :: Notice , $component -> id );
2020-05-08 05:30:56 +00:00
}
2016-08-21 13:07:14 +00:00
}
2020-05-08 05:30:56 +00:00
foreach ( $invalid as $pref ) {
$pref -> delete ();
2023-08-05 17:12:36 +00:00
Eventlog :: log ( " Component: $component->type ( $component->id ). Attribute: $pref->attribute , was deleted. " , $component -> device_id , 'component' , Severity :: Warning );
2020-05-08 05:30:56 +00:00
}
});
2016-08-21 13:07:14 +00:00
return true ;
}
2017-03-30 03:54:02 +00:00
/**
* Get the component id for the first component in the array
* Only set $device_id if using the array from getCompenents (), which is keyed by device_id
*
2021-09-08 21:35:56 +00:00
* @ param array $component_array
* @ param int $device_id
2017-03-30 03:54:02 +00:00
* @ return int the component id
*/
public function getFirstComponentID ( $component_array , $device_id = null )
{
if ( ! is_null ( $device_id ) && isset ( $component_array [ $device_id ])) {
$component_array = $component_array [ $device_id ];
}
foreach ( $component_array as $id => $array ) {
return $id ;
}
2020-09-21 12:54:51 +00:00
2017-03-30 03:54:02 +00:00
return - 1 ;
}
2016-08-21 13:07:14 +00:00
}