mirror of
https://github.com/librenms/librenms.git
synced 2024-10-07 16:52:45 +00:00
Allow ping checks to be ran separately from polling (#8821)
Allows ping checks at intervals not tied to the poller. Pointless if you are not alerting on device status. I updated the rrdstep.php script to treat ping-perf files separately and made it so it only converts if needed. Docs here: https://docs.librenms.org/Extensions/Fast-Ping-Check/ DO NOT DELETE THIS TEXT #### Please note > Please read this information carefully. You can run `./scripts/pre-commit.php` to check your code before submitting. - [x] Have you followed our [code guidelines?](http://docs.librenms.org/Developing/Code-Guidelines/) #### Testers If you would like to test this pull request then please run: `./scripts/github-apply <pr_id>`, i.e `./scripts/github-apply 5926`
This commit is contained in:
committed by
Neil Lathwood
parent
344dfb9797
commit
9bc0c542a5
46
app/Console/Commands/Ping.php
Normal file
46
app/Console/Commands/Ping.php
Normal file
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Jobs\PingCheck;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
class Ping extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'ping {--d|debug} {groups?* | Optional List of distributed poller groups to poll}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Check if devices are up or down via icmp';
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$this->alert("Do not use this command yet, use ./ping.php");
|
||||
exit();
|
||||
|
||||
PingCheck::dispatch(new PingCheck($this->argument('groups')));
|
||||
}
|
||||
}
|
@@ -14,6 +14,7 @@ class Kernel extends ConsoleKernel
|
||||
*/
|
||||
protected $commands = [
|
||||
Commands\Release::class,
|
||||
Commands\Ping::class,
|
||||
];
|
||||
|
||||
/**
|
||||
|
305
app/Jobs/PingCheck.php
Normal file
305
app/Jobs/PingCheck.php
Normal file
@@ -0,0 +1,305 @@
|
||||
<?php
|
||||
/**
|
||||
* PingCheck.php
|
||||
*
|
||||
* Device up/down icmp check job
|
||||
*
|
||||
* 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
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @package LibreNMS
|
||||
* @link http://librenms.org
|
||||
* @copyright 2018 Tony Murray
|
||||
* @author Tony Murray <murraytony@gmail.com>
|
||||
*/
|
||||
|
||||
namespace App\Jobs;
|
||||
|
||||
use App\Models\Device;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Collection;
|
||||
use LibreNMS\Config;
|
||||
use LibreNMS\RRD\RrdDefinition;
|
||||
use Symfony\Component\Process\Process;
|
||||
|
||||
class PingCheck implements ShouldQueue
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
private $process;
|
||||
private $rrd_tags;
|
||||
|
||||
/** @var \Illuminate\Database\Eloquent\Collection $devices List of devices keyed by hostname */
|
||||
private $devices;
|
||||
/** @var array $groups List of device group ids to check */
|
||||
private $groups = [];
|
||||
|
||||
// working data for loop
|
||||
/** @var Collection $tiered */
|
||||
private $tiered;
|
||||
/** @var Collection $current */
|
||||
private $current;
|
||||
private $current_tier;
|
||||
/** @var Collection $deferred */
|
||||
private $deferred;
|
||||
|
||||
/**
|
||||
* Create a new job instance.
|
||||
*
|
||||
* @param array $groups List of distributed poller groups to check
|
||||
*/
|
||||
public function __construct($groups = [])
|
||||
{
|
||||
if (is_array($groups)) {
|
||||
$this->groups = $groups;
|
||||
}
|
||||
|
||||
// define rrd tags
|
||||
$rrd_step = Config::get('ping_rrd_step', Config::get('rrd.step', 300));
|
||||
$rrd_def = RrdDefinition::make()->addDataset('ping', 'GAUGE', 0, 65535, $rrd_step * 2);
|
||||
$this->rrd_tags = ['rrd_def' => $rrd_def, 'rrd_step' => $rrd_step];
|
||||
|
||||
// set up fping process
|
||||
$timeout = Config::get('fping_options.timeout', 500); // must be smaller than period
|
||||
$retries = Config::get('fping_options.retries', 2); // how many retries on failure
|
||||
|
||||
$cmd = ['fping', '-f', '-', '-e', '-t', $timeout, '-r', $retries];
|
||||
|
||||
$wait = Config::get('rrd_step', 300) * 2;
|
||||
|
||||
$this->process = new Process($cmd, null, null, null, $wait);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the job.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$ping_start = microtime(true);
|
||||
|
||||
$this->fetchDevices();
|
||||
|
||||
d_echo($this->process->getCommandLine() . PHP_EOL);
|
||||
|
||||
// send hostnames to stdin to avoid overflowing cli length limits
|
||||
$ordered_device_list = $this->tiered->get(1, collect())->keys()// root nodes before standalone nodes
|
||||
->merge($this->devices->keys())
|
||||
->unique()
|
||||
->implode(PHP_EOL);
|
||||
|
||||
$this->process->setInput($ordered_device_list);
|
||||
$this->process->start(); // start as early as possible
|
||||
|
||||
foreach ($this->process as $type => $line) {
|
||||
d_echo($line);
|
||||
|
||||
if (Process::ERR === $type) {
|
||||
// Check for devices we couldn't resolve dns for
|
||||
if (preg_match('/^(?<hostname>[^\s]+): (?:Name or service not known|Temporary failure in name resolution)/', $line, $errored)) {
|
||||
$this->recordData([
|
||||
'hostname' => $errored['hostname'],
|
||||
'status' => 'unreachable'
|
||||
]);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (preg_match(
|
||||
'/^(?<hostname>[^\s]+) is (?<status>alive|unreachable)(?: \((?<rtt>[\d.]+) ms\))?/',
|
||||
$line,
|
||||
$captured
|
||||
)) {
|
||||
$this->recordData($captured);
|
||||
|
||||
$this->processTier();
|
||||
}
|
||||
}
|
||||
|
||||
// check for any left over devices
|
||||
if ($this->deferred->isNotEmpty()) {
|
||||
d_echo("Leftover devices, this shouldn't happen: " . $this->deferred->flatten(1)->implode('hostname', ', ') . PHP_EOL);
|
||||
d_echo("Devices left in tier: " . collect($this->current)->implode('hostname', ', ') . PHP_EOL);
|
||||
}
|
||||
|
||||
if (\App::runningInConsole()) {
|
||||
printf("Pinged %s devices in %.2fs\n", $this->devices->count(), microtime(true) - $ping_start);
|
||||
}
|
||||
}
|
||||
|
||||
private function fetchDevices()
|
||||
{
|
||||
if (isset($this->devices)) {
|
||||
return $this->devices;
|
||||
}
|
||||
|
||||
global $vdebug;
|
||||
|
||||
/** @var Builder $query */
|
||||
$query = Device::canPing()
|
||||
->select(['devices.device_id', 'hostname', 'status', 'status_reason', 'last_ping', 'last_ping_timetaken', 'max_depth'])
|
||||
->orderBy('max_depth');
|
||||
|
||||
if ($this->groups) {
|
||||
$query->whereIn('poller_group', $this->groups);
|
||||
}
|
||||
|
||||
$this->devices = $query->get()->keyBy('hostname');
|
||||
|
||||
// working collections
|
||||
$this->tiered = $this->devices->groupBy('max_depth', true);
|
||||
$this->deferred = collect();
|
||||
|
||||
// start with tier 1 (the root nodes, 0 is standalone)
|
||||
$this->current_tier = 1;
|
||||
$this->current = $this->tiered->get($this->current_tier, collect());
|
||||
|
||||
if ($vdebug) {
|
||||
$this->tiered->each(function (Collection $tier, $index) {
|
||||
echo "Tier $index (" . $tier->count() . "): ";
|
||||
echo $tier->implode('hostname', ', ');
|
||||
echo PHP_EOL;
|
||||
});
|
||||
}
|
||||
|
||||
return $this->devices;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this tier is complete and move to the next tier
|
||||
* If we moved to the next tier, check if we can report any of our deferred results
|
||||
*/
|
||||
private function processTier()
|
||||
{
|
||||
global $vdebug;
|
||||
|
||||
if ($this->current->isNotEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->current_tier++; // next tier
|
||||
|
||||
if (!$this->tiered->has($this->current_tier)) {
|
||||
// out of devices
|
||||
return;
|
||||
}
|
||||
|
||||
if ($vdebug) {
|
||||
echo "Out of devices at this tier, moving to tier $this->current_tier\n";
|
||||
}
|
||||
|
||||
$this->current = $this->tiered->get($this->current_tier);
|
||||
|
||||
// update and remove devices in the current tier
|
||||
foreach ($this->deferred->pull($this->current_tier, []) as $data) {
|
||||
$this->recordData($data);
|
||||
}
|
||||
|
||||
// try to process the new tier in case we took care of all the devices
|
||||
$this->processTier();
|
||||
}
|
||||
|
||||
/**
|
||||
* If the device is on the current tier, record the data and remove it
|
||||
* $data should have keys: hostname, status, and conditionally rtt
|
||||
*
|
||||
* @param $data
|
||||
*/
|
||||
private function recordData($data)
|
||||
{
|
||||
global $vdebug;
|
||||
|
||||
if ($vdebug) {
|
||||
echo "Attempting to record data for {$data['hostname']}... ";
|
||||
}
|
||||
|
||||
/** @var Device $device */
|
||||
$device = $this->devices->get($data['hostname']);
|
||||
|
||||
// process the data if this is a standalone device or in the current tier
|
||||
if ($device->max_depth === 0 || $this->current->has($device->hostname)) {
|
||||
if ($vdebug) {
|
||||
echo "Success\n";
|
||||
}
|
||||
|
||||
// mark up only if snmp is not down too
|
||||
$device->status = ($data['status'] == 'alive' && $device->status_reason != 'snmp');
|
||||
$device->last_ping = Carbon::now();
|
||||
$device->last_ping_timetaken = isset($data['rtt']) ? $data['rtt'] : 0;
|
||||
|
||||
if ($device->isDirty('status')) {
|
||||
// if changed, update reason
|
||||
$device->status_reason = $device->status ? '' : 'icmp';
|
||||
$type = $device->status ? 'up' : 'down';
|
||||
log_event('Device status changed to ' . ucfirst($type) . " from icmp check.", $device->toArray(), $type);
|
||||
|
||||
echo "Device $device->hostname changed status to $type, running alerts\n";
|
||||
RunRules($device->device_id);
|
||||
}
|
||||
$device->save(); // only saves if needed (which is every time because of last_ping)
|
||||
|
||||
// add data to rrd
|
||||
data_update($device->toArray(), 'ping-perf', $this->rrd_tags, ['ping' => $device->last_ping_timetaken]);
|
||||
|
||||
// done with this device
|
||||
$this->complete($device->hostname);
|
||||
d_echo("Recorded data for $device->hostname (tier $device->max_depth)\n");
|
||||
} else {
|
||||
if ($vdebug) {
|
||||
echo "Deferred\n";
|
||||
}
|
||||
|
||||
$this->defer($data);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Done processing $hostname, remove it from our active data
|
||||
*
|
||||
* @param $hostname
|
||||
*/
|
||||
private function complete($hostname)
|
||||
{
|
||||
$this->current->offsetUnset($hostname);
|
||||
$this->deferred->each->offsetUnset($hostname);
|
||||
}
|
||||
|
||||
/**
|
||||
* Defer this data processing until all parent devices are complete
|
||||
*
|
||||
*
|
||||
* @param $data
|
||||
*/
|
||||
private function defer($data)
|
||||
{
|
||||
$device = $this->devices->get($data['hostname']);
|
||||
|
||||
if ($this->deferred->has($device->max_depth)) {
|
||||
// add this data to the proper tier, unless it already exists...
|
||||
$tier = $this->deferred->get($device->max_depth);
|
||||
if (!$tier->has($device->hostname)) {
|
||||
$tier->put($device->hostname, $data);
|
||||
}
|
||||
} else {
|
||||
// create a new tier containing this data
|
||||
$this->deferred->put($device->max_depth, collect([$device->hostname => $data]));
|
||||
}
|
||||
}
|
||||
}
|
@@ -2,11 +2,18 @@
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Fico7489\Laravel\Pivot\Traits\PivotEventTrait;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Query\JoinClause;
|
||||
|
||||
class Device extends BaseModel
|
||||
{
|
||||
use PivotEventTrait;
|
||||
|
||||
public $timestamps = false;
|
||||
protected $primaryKey = 'device_id';
|
||||
protected $fillable = ['hostname', 'ip', 'status', 'status_reason'];
|
||||
protected $casts = ['status' => 'boolean'];
|
||||
|
||||
/**
|
||||
* Initialize this class
|
||||
@@ -20,6 +27,56 @@ class Device extends BaseModel
|
||||
$device->ports()->delete();
|
||||
$device->syslogs()->delete();
|
||||
$device->eventlogs()->delete();
|
||||
|
||||
// handle device dependency updates
|
||||
$device->children->each->updateMaxDepth($device->device_id);
|
||||
});
|
||||
|
||||
// handle device dependency updates
|
||||
static::updated(function (Device $device) {
|
||||
if ($device->isDirty('max_depth')) {
|
||||
$device->children->each->updateMaxDepth();
|
||||
}
|
||||
});
|
||||
|
||||
static::pivotAttached(function (Device $device, $relationName, $pivotIds, $pivotIdsAttributes) {
|
||||
if ($relationName == 'parents') {
|
||||
// a parent attached to this device
|
||||
|
||||
// update the parent's max depth incase it used to be standalone
|
||||
Device::whereIn('device_id', $pivotIds)->get()->each->validateStandalone();
|
||||
|
||||
// make sure this device's max depth is updated
|
||||
$device->updateMaxDepth();
|
||||
} elseif ($relationName == 'children') {
|
||||
// a child device attached to this device
|
||||
|
||||
// if this device used to be standalone, we need to udpate max depth
|
||||
$device->validateStandalone();
|
||||
|
||||
// make sure the child's max depth is updated
|
||||
Device::whereIn('device_id', $pivotIds)->get()->each->updateMaxDepth();
|
||||
}
|
||||
});
|
||||
|
||||
static::pivotDetached(function (Device $device, $relationName, $pivotIds) {
|
||||
if ($relationName == 'parents') {
|
||||
// this device detached from a parent
|
||||
|
||||
// update this devices max depth
|
||||
$device->updateMaxDepth();
|
||||
|
||||
// parent may now be standalone, update old parent
|
||||
Device::whereIn('device_id', $pivotIds)->get()->each->validateStandalone();
|
||||
} elseif ($relationName == 'children') {
|
||||
// a child device detached from this device
|
||||
|
||||
// update the detached child's max_depth
|
||||
Device::whereIn('device_id', $pivotIds)->get()->each->updateMaxDepth();
|
||||
|
||||
// this device may be standalone, update it
|
||||
$device->validateStandalone();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -47,6 +104,53 @@ class Device extends BaseModel
|
||||
return asset('images/os/generic.svg');
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the max_depth field based on parents
|
||||
* Performs SQL query, so make sure all parents are saved first
|
||||
*
|
||||
* @param int $exclude exclude a device_id from being considered (used for deleting)
|
||||
*/
|
||||
public function updateMaxDepth($exclude = null)
|
||||
{
|
||||
// optimize for memory instead of time
|
||||
$query = $this->parents()->getQuery();
|
||||
if (!is_null($exclude)) {
|
||||
$query->where('device_id', '!=', $exclude);
|
||||
}
|
||||
|
||||
$count = $query->count();
|
||||
if ($count === 0) {
|
||||
if ($this->children()->count() === 0) {
|
||||
$this->max_depth = 0; // no children or parents
|
||||
} else {
|
||||
$this->max_depth = 1; // has children
|
||||
}
|
||||
} else {
|
||||
$parents_max_depth = $query->max('max_depth');
|
||||
$this->max_depth = $parents_max_depth + 1;
|
||||
}
|
||||
|
||||
$this->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Device dependency check to see if this node is standalone or not.
|
||||
* Standalone is a special case where the device has no parents or children and is denoted by a max_depth of 0
|
||||
*
|
||||
* Only checks on root nodes (where max_depth is 1 or 0)
|
||||
*
|
||||
*/
|
||||
public function validateStandalone()
|
||||
{
|
||||
if ($this->max_depth === 0 && $this->children()->count() > 0) {
|
||||
$this->max_depth = 1; // has children
|
||||
} elseif ($this->max_depth === 1 && $this->parents()->count() === 0) {
|
||||
$this->max_depth = 0; // no children or parents
|
||||
}
|
||||
|
||||
$this->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
@@ -70,9 +174,9 @@ class Device extends BaseModel
|
||||
public function getIconAttribute($icon)
|
||||
{
|
||||
if (isset($icon)) {
|
||||
return asset("images/os/$icon");
|
||||
return "images/os/$icon";
|
||||
}
|
||||
return asset('images/os/generic.svg');
|
||||
return 'images/os/generic.svg';
|
||||
}
|
||||
public function getIpAttribute($ip)
|
||||
{
|
||||
@@ -88,6 +192,11 @@ class Device extends BaseModel
|
||||
$this->attributes['ip'] = inet_pton($ip);
|
||||
}
|
||||
|
||||
public function setStatusAttribute($status)
|
||||
{
|
||||
$this->attributes['status'] = (int)$status;
|
||||
}
|
||||
|
||||
// ---- Query scopes ----
|
||||
|
||||
public function scopeIsUp($query)
|
||||
@@ -138,6 +247,19 @@ class Device extends BaseModel
|
||||
]);
|
||||
}
|
||||
|
||||
public function scopeCanPing(Builder $query)
|
||||
{
|
||||
return $query->where('disabled', 0)
|
||||
->leftJoin('devices_attribs', function (JoinClause $query) {
|
||||
$query->on('devices.device_id', 'devices_attribs.device_id')
|
||||
->where('devices_attribs.attrib_type', 'override_icmp_disable');
|
||||
})
|
||||
->where(function (Builder $query) {
|
||||
$query->whereNull('devices_attribs.attrib_value')
|
||||
->orWhere('devices_attribs.attrib_value', '!=', 'true');
|
||||
});
|
||||
}
|
||||
|
||||
public function scopeHasAccess($query, User $user)
|
||||
{
|
||||
return $this->hasDeviceAccess($query, $user);
|
||||
@@ -165,6 +287,11 @@ class Device extends BaseModel
|
||||
return $this->hasMany('App\Models\CefSwitching', 'device_id');
|
||||
}
|
||||
|
||||
public function children()
|
||||
{
|
||||
return $this->belongsToMany('App\Models\Device', 'device_relationships', 'parent_device_id', 'child_device_id');
|
||||
}
|
||||
|
||||
public function components()
|
||||
{
|
||||
return $this->hasMany('App\Models\Component', 'device_id');
|
||||
@@ -190,6 +317,16 @@ class Device extends BaseModel
|
||||
return $this->hasMany('App\Models\Package', 'device_id', 'device_id');
|
||||
}
|
||||
|
||||
public function parents()
|
||||
{
|
||||
return $this->belongsToMany('App\Models\Device', 'device_relationships', 'child_device_id', 'parent_device_id');
|
||||
}
|
||||
|
||||
public function perf()
|
||||
{
|
||||
return $this->hasMany('App\Models\DevicePerf', 'device_id');
|
||||
}
|
||||
|
||||
public function ports()
|
||||
{
|
||||
return $this->hasMany('App\Models\Port', 'device_id', 'device_id');
|
||||
|
65
app/Models/DevicePerf.php
Normal file
65
app/Models/DevicePerf.php
Normal file
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
/**
|
||||
* DevicePerf.php
|
||||
*
|
||||
* -Description-
|
||||
*
|
||||
* 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
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @package LibreNMS
|
||||
* @link http://librenms.org
|
||||
* @copyright 2018 Tony Murray
|
||||
* @author Tony Murray <murraytony@gmail.com>
|
||||
*/
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class DevicePerf extends BaseModel
|
||||
{
|
||||
protected $table = 'device_perf';
|
||||
protected $fillable = ['device_id', 'timestamp', 'xmt', 'rcv', 'loss', 'min', 'max', 'avg'];
|
||||
protected $casts = [
|
||||
'xmt' => 'integer',
|
||||
'rcv' => 'integer',
|
||||
'loss' => 'integer',
|
||||
'min' => 'float',
|
||||
'max' => 'float',
|
||||
'avg' => 'float',
|
||||
];
|
||||
public $timestamps = false;
|
||||
const CREATED_AT = 'timestamp';
|
||||
protected $attributes = [
|
||||
'min' => 0,
|
||||
'max' => 0,
|
||||
'avg' => 0,
|
||||
];
|
||||
|
||||
protected static function boot()
|
||||
{
|
||||
parent::boot();
|
||||
|
||||
static::creating(function ($model) {
|
||||
$model->timestamp = $model->freshTimestamp();
|
||||
});
|
||||
}
|
||||
|
||||
// ---- Define Relationships ----
|
||||
|
||||
public function device()
|
||||
{
|
||||
return $this->belongsTo('App\Models\Device', 'device_id', 'device_id');
|
||||
}
|
||||
}
|
@@ -45,6 +45,7 @@
|
||||
"laravel/laravel": "5.4.*",
|
||||
"oriceon/toastr-5-laravel": "dev-master",
|
||||
"wpb/string-blade-compiler": "3.4.x-dev",
|
||||
"fico7489/laravel-pivot": "*",
|
||||
|
||||
"vlucas/phpdotenv": "2.4.0",
|
||||
"doctrine/inflector": "1.1.*",
|
||||
|
144
composer.lock
generated
144
composer.lock
generated
@@ -4,7 +4,7 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "d962cf418393daf777aa51f999b40933",
|
||||
"content-hash": "f57760426989ee971f25f10f73d7d661",
|
||||
"packages": [
|
||||
{
|
||||
"name": "amenadiel/jpgraph",
|
||||
@@ -419,6 +419,56 @@
|
||||
],
|
||||
"time": "2018-02-23T01:58:20+00:00"
|
||||
},
|
||||
{
|
||||
"name": "fico7489/laravel-pivot",
|
||||
"version": "2.0.6",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/fico7489/laravel-pivot.git",
|
||||
"reference": "f4197fb797b0c544e18ee47d8a9407b2ade0930c"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/fico7489/laravel-pivot/zipball/f4197fb797b0c544e18ee47d8a9407b2ade0930c",
|
||||
"reference": "f4197fb797b0c544e18ee47d8a9407b2ade0930c",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"illuminate/database": "5.4.*"
|
||||
},
|
||||
"require-dev": {
|
||||
"orchestra/testbench": "3.4.*",
|
||||
"phpunit/phpunit": "~5.0"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Fico7489\\Laravel\\Pivot\\": "src"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Filip Horvat",
|
||||
"email": "filip.horvat@am2studio.hr",
|
||||
"homepage": "http://am2studio.hr",
|
||||
"role": "Developer"
|
||||
}
|
||||
],
|
||||
"description": "This package introduces new eloquent events for sync(), attach(), detach() or updateExistingPivot() methods on BelongsToMany relation.",
|
||||
"homepage": "https://github.com/fico7489/laravel-pivot",
|
||||
"keywords": [
|
||||
"eloquent events",
|
||||
"eloquent extra events",
|
||||
"laravel BelongsToMany events",
|
||||
"laravel pivot events",
|
||||
"laravel sync events"
|
||||
],
|
||||
"time": "2018-03-08T16:05:59+00:00"
|
||||
},
|
||||
{
|
||||
"name": "guzzlehttp/guzzle",
|
||||
"version": "6.3.3",
|
||||
@@ -2080,21 +2130,22 @@
|
||||
},
|
||||
{
|
||||
"name": "ramsey/uuid",
|
||||
"version": "3.7.3",
|
||||
"version": "3.8.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/ramsey/uuid.git",
|
||||
"reference": "44abcdad877d9a46685a3a4d221e3b2c4b87cb76"
|
||||
"reference": "d09ea80159c1929d75b3f9c60504d613aeb4a1e3"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/ramsey/uuid/zipball/44abcdad877d9a46685a3a4d221e3b2c4b87cb76",
|
||||
"reference": "44abcdad877d9a46685a3a4d221e3b2c4b87cb76",
|
||||
"url": "https://api.github.com/repos/ramsey/uuid/zipball/d09ea80159c1929d75b3f9c60504d613aeb4a1e3",
|
||||
"reference": "d09ea80159c1929d75b3f9c60504d613aeb4a1e3",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"paragonie/random_compat": "^1.0|^2.0",
|
||||
"php": "^5.4 || ^7.0"
|
||||
"paragonie/random_compat": "^1.0|^2.0|9.99.99",
|
||||
"php": "^5.4 || ^7.0",
|
||||
"symfony/polyfill-ctype": "^1.8"
|
||||
},
|
||||
"replace": {
|
||||
"rhumsaa/uuid": "self.version"
|
||||
@@ -2102,16 +2153,17 @@
|
||||
"require-dev": {
|
||||
"codeception/aspect-mock": "^1.0 | ~2.0.0",
|
||||
"doctrine/annotations": "~1.2.0",
|
||||
"goaop/framework": "1.0.0-alpha.2 | ^1.0 | ^2.1",
|
||||
"goaop/framework": "1.0.0-alpha.2 | ^1.0 | ~2.1.0",
|
||||
"ircmaxell/random-lib": "^1.1",
|
||||
"jakub-onderka/php-parallel-lint": "^0.9.0",
|
||||
"mockery/mockery": "^0.9.9",
|
||||
"moontoast/math": "^1.1",
|
||||
"php-mock/php-mock-phpunit": "^0.3|^1.1",
|
||||
"phpunit/phpunit": "^4.7|^5.0",
|
||||
"phpunit/phpunit": "^4.7|^5.0|^6.5",
|
||||
"squizlabs/php_codesniffer": "^2.3"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-ctype": "Provides support for PHP Ctype functions",
|
||||
"ext-libsodium": "Provides the PECL libsodium extension for use with the SodiumRandomGenerator",
|
||||
"ext-uuid": "Provides the PECL UUID extension for use with the PeclUuidTimeGenerator and PeclUuidRandomGenerator",
|
||||
"ircmaxell/random-lib": "Provides RandomLib for use with the RandomLibAdapter",
|
||||
@@ -2156,7 +2208,7 @@
|
||||
"identifier",
|
||||
"uuid"
|
||||
],
|
||||
"time": "2018-01-20T00:28:24+00:00"
|
||||
"time": "2018-07-19T23:38:55+00:00"
|
||||
},
|
||||
{
|
||||
"name": "rmccue/requests",
|
||||
@@ -2310,16 +2362,16 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/console",
|
||||
"version": "v3.4.12",
|
||||
"version": "v3.4.13",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/console.git",
|
||||
"reference": "1b97071a26d028c9bd4588264e101e14f6e7cd00"
|
||||
"reference": "e54f84c50e3b12972e7750edfc5ca84b2284c44e"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/console/zipball/1b97071a26d028c9bd4588264e101e14f6e7cd00",
|
||||
"reference": "1b97071a26d028c9bd4588264e101e14f6e7cd00",
|
||||
"url": "https://api.github.com/repos/symfony/console/zipball/e54f84c50e3b12972e7750edfc5ca84b2284c44e",
|
||||
"reference": "e54f84c50e3b12972e7750edfc5ca84b2284c44e",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -2375,11 +2427,11 @@
|
||||
],
|
||||
"description": "Symfony Console Component",
|
||||
"homepage": "https://symfony.com",
|
||||
"time": "2018-05-23T05:02:55+00:00"
|
||||
"time": "2018-07-10T14:02:11+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/css-selector",
|
||||
"version": "v3.4.12",
|
||||
"version": "v3.4.13",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/css-selector.git",
|
||||
@@ -2432,16 +2484,16 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/debug",
|
||||
"version": "v3.4.12",
|
||||
"version": "v3.4.13",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/debug.git",
|
||||
"reference": "47e6788c5b151cf0cfdf3329116bf33800632d75"
|
||||
"reference": "0e3ca9cbde90fffec8038f4d4e16fd4046bbd018"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/debug/zipball/47e6788c5b151cf0cfdf3329116bf33800632d75",
|
||||
"reference": "47e6788c5b151cf0cfdf3329116bf33800632d75",
|
||||
"url": "https://api.github.com/repos/symfony/debug/zipball/0e3ca9cbde90fffec8038f4d4e16fd4046bbd018",
|
||||
"reference": "0e3ca9cbde90fffec8038f4d4e16fd4046bbd018",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -2484,11 +2536,11 @@
|
||||
],
|
||||
"description": "Symfony Debug Component",
|
||||
"homepage": "https://symfony.com",
|
||||
"time": "2018-06-25T11:10:40+00:00"
|
||||
"time": "2018-06-26T08:45:54+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/dotenv",
|
||||
"version": "v3.4.12",
|
||||
"version": "v3.4.13",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/dotenv.git",
|
||||
@@ -2545,7 +2597,7 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/event-dispatcher",
|
||||
"version": "v3.4.12",
|
||||
"version": "v3.4.13",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/event-dispatcher.git",
|
||||
@@ -2608,7 +2660,7 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/finder",
|
||||
"version": "v3.4.12",
|
||||
"version": "v3.4.13",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/finder.git",
|
||||
@@ -2657,16 +2709,16 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/http-foundation",
|
||||
"version": "v3.4.12",
|
||||
"version": "v3.4.13",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/http-foundation.git",
|
||||
"reference": "1c28679fcbb0d9b35e4fd49fbb74d2ca4ea17bce"
|
||||
"reference": "2b8e08c085e2dc7449ee6d55a238be87d3727c96"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/http-foundation/zipball/1c28679fcbb0d9b35e4fd49fbb74d2ca4ea17bce",
|
||||
"reference": "1c28679fcbb0d9b35e4fd49fbb74d2ca4ea17bce",
|
||||
"url": "https://api.github.com/repos/symfony/http-foundation/zipball/2b8e08c085e2dc7449ee6d55a238be87d3727c96",
|
||||
"reference": "2b8e08c085e2dc7449ee6d55a238be87d3727c96",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -2707,20 +2759,20 @@
|
||||
],
|
||||
"description": "Symfony HttpFoundation Component",
|
||||
"homepage": "https://symfony.com",
|
||||
"time": "2018-06-21T11:10:19+00:00"
|
||||
"time": "2018-07-19T07:08:28+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/http-kernel",
|
||||
"version": "v3.4.12",
|
||||
"version": "v3.4.13",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/http-kernel.git",
|
||||
"reference": "cb7edcdc47cab3c61c891e6e55337f8dd470d820"
|
||||
"reference": "22a1d000d45f09966a363223548a150aec759e61"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/http-kernel/zipball/cb7edcdc47cab3c61c891e6e55337f8dd470d820",
|
||||
"reference": "cb7edcdc47cab3c61c891e6e55337f8dd470d820",
|
||||
"url": "https://api.github.com/repos/symfony/http-kernel/zipball/22a1d000d45f09966a363223548a150aec759e61",
|
||||
"reference": "22a1d000d45f09966a363223548a150aec759e61",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -2796,7 +2848,7 @@
|
||||
],
|
||||
"description": "Symfony HttpKernel Component",
|
||||
"homepage": "https://symfony.com",
|
||||
"time": "2018-06-25T12:29:19+00:00"
|
||||
"time": "2018-07-23T16:37:31+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-ctype",
|
||||
@@ -2973,16 +3025,16 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/process",
|
||||
"version": "v3.4.12",
|
||||
"version": "v3.4.13",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/process.git",
|
||||
"reference": "acc5a37c706ace827962851b69705b24e71ca17c"
|
||||
"reference": "f741672edfcfe3a2ea77569d419006f23281d909"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/process/zipball/acc5a37c706ace827962851b69705b24e71ca17c",
|
||||
"reference": "acc5a37c706ace827962851b69705b24e71ca17c",
|
||||
"url": "https://api.github.com/repos/symfony/process/zipball/f741672edfcfe3a2ea77569d419006f23281d909",
|
||||
"reference": "f741672edfcfe3a2ea77569d419006f23281d909",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -3018,7 +3070,7 @@
|
||||
],
|
||||
"description": "Symfony Process Component",
|
||||
"homepage": "https://symfony.com",
|
||||
"time": "2018-05-30T04:24:30+00:00"
|
||||
"time": "2018-07-09T09:01:07+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/routing",
|
||||
@@ -3161,16 +3213,16 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/var-dumper",
|
||||
"version": "v3.4.12",
|
||||
"version": "v3.4.13",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/var-dumper.git",
|
||||
"reference": "e173954a28a44a32c690815fbe4d0f2eac43accb"
|
||||
"reference": "c501f46bb1eaf4c8d65ba070ab65a1986da1cd7f"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/var-dumper/zipball/e173954a28a44a32c690815fbe4d0f2eac43accb",
|
||||
"reference": "e173954a28a44a32c690815fbe4d0f2eac43accb",
|
||||
"url": "https://api.github.com/repos/symfony/var-dumper/zipball/c501f46bb1eaf4c8d65ba070ab65a1986da1cd7f",
|
||||
"reference": "c501f46bb1eaf4c8d65ba070ab65a1986da1cd7f",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -3226,11 +3278,11 @@
|
||||
"debug",
|
||||
"dump"
|
||||
],
|
||||
"time": "2018-06-15T07:47:49+00:00"
|
||||
"time": "2018-07-09T08:21:26+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/yaml",
|
||||
"version": "v2.8.42",
|
||||
"version": "v2.8.43",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/yaml.git",
|
||||
@@ -4969,7 +5021,7 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/class-loader",
|
||||
"version": "v3.4.12",
|
||||
"version": "v3.4.13",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/class-loader.git",
|
||||
|
28
daily.php
28
daily.php
@@ -6,6 +6,8 @@
|
||||
* (c) 2013 LibreNMS Contributors
|
||||
*/
|
||||
|
||||
use App\Models\Device;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
use LibreNMS\Config;
|
||||
use LibreNMS\Exceptions\LockException;
|
||||
use LibreNMS\Util\MemcacheLock;
|
||||
@@ -294,3 +296,29 @@ if ($options['f'] === 'refresh_os_cache') {
|
||||
echo 'Clearing OS cache' . PHP_EOL;
|
||||
unlink(Config::get('install_dir') . '/cache/os_defs.cache');
|
||||
}
|
||||
|
||||
if ($options['f'] === 'recalculate_device_dependencies') {
|
||||
// fix broken dependency max_depth calculation in case things weren't done though eloquent
|
||||
|
||||
try {
|
||||
if (Config::get('distributed_poller')) {
|
||||
MemcacheLock::lock('recalculate_device_dependencies', 0, 86000);
|
||||
}
|
||||
\LibreNMS\DB\Eloquent::boot();
|
||||
|
||||
// update all root nodes and recurse, chunk so we don't blow up
|
||||
Device::doesntHave('parents')->with('children')->chunk(100, function (Collection $devices) {
|
||||
// anonymous recursive function
|
||||
$recurse = function (Device $device) use (&$recurse) {
|
||||
$device->updateMaxDepth();
|
||||
|
||||
$device->children->each($recurse);
|
||||
};
|
||||
|
||||
$devices->each($recurse);
|
||||
});
|
||||
} catch (LockException $e) {
|
||||
echo $e->getMessage() . PHP_EOL;
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
|
1
daily.sh
1
daily.sh
@@ -255,6 +255,7 @@ main () {
|
||||
# Cleanups
|
||||
local options=("refresh_alert_rules"
|
||||
"refresh_os_cache"
|
||||
"recalculate_device_dependencies"
|
||||
"syslog"
|
||||
"eventlog"
|
||||
"authlog"
|
||||
|
64
doc/Extensions/Fast-Ping-Check.md
Normal file
64
doc/Extensions/Fast-Ping-Check.md
Normal file
@@ -0,0 +1,64 @@
|
||||
source: Extensions/Fast-Ping-Check.md
|
||||
|
||||
## Fast up/down checking
|
||||
|
||||
Normally, LibreNMS sends an ICMP ping to the device before polling to check if it is up or down.
|
||||
This check is tied to the poller frequency, which is normally 5 minutes. This means it may take up to 5 minutes
|
||||
to find out if a device is down.
|
||||
|
||||
Some users may want to know if devices stop responding to ping more quickly than that. LibreNMS offers a ping.php script
|
||||
to run ping checks as quickly as possible without increasing snmp load on your devices by switching to 1 minute polling.
|
||||
|
||||
> **WARNING**: If you do not have an alert rule that alerts on device status, enabling this will be a waste of resources.
|
||||
> You can find one in the [Alert Rules Collection](../Alerting/Rules.md#Alert Rules Collection).
|
||||
|
||||
|
||||
|
||||
### Setting the ping check to 1 minute
|
||||
|
||||
1. Change the ping_rrd_step setting in config.php
|
||||
```php
|
||||
$config['ping_rrd_step'] = 60;
|
||||
```
|
||||
|
||||
2. Update the rrd files to change the step (step is hardcoded at file creation in rrd files)
|
||||
```bash
|
||||
./scripts/rrdstep.php -h all
|
||||
```
|
||||
|
||||
3. Add the following line to /etc/cron.d/librenms.nonroot.cron to allow 1 minute ping checks
|
||||
|
||||
```
|
||||
* * * * * librenms /opt/librenms/ping.php >> /dev/null 2>&1
|
||||
```
|
||||
|
||||
#### Sub minute ping check
|
||||
|
||||
Cron only has a resolution of one minute, so we have to use a trick to allow sub minute checks.
|
||||
We add two entries, but add a delay before one.
|
||||
|
||||
>Alerts are only run every minute, so you will have to modify them as well. Remove the original alerts.php entry.
|
||||
|
||||
1. Set ping_rrd_step
|
||||
```php
|
||||
$config['ping_rrd_step'] = 30;
|
||||
```
|
||||
|
||||
2. Update the rrd files
|
||||
```bash
|
||||
./scripts/rrdstep.php -h all
|
||||
```
|
||||
|
||||
3. Update cron (removing any other ping.php or alert.php entries)
|
||||
```
|
||||
* * * * * librenms /opt/librenms/ping.php >> /dev/null 2>&1
|
||||
* * * * * librenms sleep 30 && /opt/librenms/ping.php >> /dev/null 2>&1
|
||||
* * * * * librenms sleep 15 && /opt/librenms/alerts.php >> /dev/null 2>&1
|
||||
* * * * * librenms sleep 45 && /opt/librenms/alerts.php >> /dev/null 2>&1
|
||||
```
|
||||
|
||||
### Device dependencies
|
||||
|
||||
The ping.php script respects device dependencies, but the main poller does not (for technical reasons).
|
||||
However, using this script does not disable the icmp check in the poller and a child may be reported as
|
||||
down before the parent.
|
@@ -16,7 +16,7 @@
|
||||
|
||||
use LibreNMS\Authentication\Auth;
|
||||
|
||||
$init_modules = array('web', 'auth', 'alerts', 'alerts-cli');
|
||||
$init_modules = array('web', 'auth', 'alerts', 'eloquent');
|
||||
require realpath(__DIR__ . '/..') . '/includes/init.php';
|
||||
|
||||
set_debug(isset($_REQUEST['debug']) ? $_REQUEST['debug'] : false);
|
||||
|
@@ -15,39 +15,34 @@
|
||||
use LibreNMS\Authentication\Auth;
|
||||
|
||||
if (!Auth::user()->hasGlobalAdmin()) {
|
||||
$status = array('status' => 1, 'message' => 'You need to be admin');
|
||||
$status = ['status' => 1, 'message' => 'You need to be admin'];
|
||||
} else {
|
||||
if ($_POST['device_id']) {
|
||||
if (!is_numeric($_POST['device_id'])) {
|
||||
$status = array('status' => 1, 'message' => 'Wrong device id!');
|
||||
$status = ['status' => 1, 'message' => 'Wrong device id!'];
|
||||
} else {
|
||||
if (dbDelete('device_relationships', '`child_device_id` = ?', array($_POST['device_id']))) {
|
||||
$status = array('status' => 0, 'message' => 'Device dependency has been deleted.');
|
||||
$device = \App\Models\Device::find($_POST['device_id']);
|
||||
if ($device->parents()->detach()) {
|
||||
$status = ['status' => 0, 'message' => 'Device dependency has been deleted.'];
|
||||
} else {
|
||||
$status = array('status' => 1, 'message' => 'Device dependency cannot be deleted.');
|
||||
$status = ['status' => 1, 'message' => 'Device dependency cannot be deleted.'];
|
||||
}
|
||||
}
|
||||
} elseif ($_POST['parent_ids']) {
|
||||
$error = false;
|
||||
$status = ['status' => 0, 'message' => 'Device dependencies has been deleted'];
|
||||
foreach ($_POST['parent_ids'] as $parent) {
|
||||
if (is_numeric($parent) && $parent != 0) {
|
||||
if (!dbDelete('device_relationships', ' `parent_device_id` = ?', array($parent))) {
|
||||
$error = true;
|
||||
$status = array('status' => 1, 'message' => 'Device dependency cannot be deleted.');
|
||||
$device = \App\Models\Device::find($_POST['device_id']);
|
||||
if (!$device->children()->detach()) {
|
||||
$status = ['status' => 1, 'message' => 'Device dependency cannot be deleted.'];
|
||||
}
|
||||
} elseif ($parent == 0) {
|
||||
$status = array('status' => 1, 'message' => 'No dependency to delete.');
|
||||
$error = true;
|
||||
$status = ['status' => 1, 'message' => 'No dependency to delete.'];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$error) {
|
||||
$status = array('status' => 0, 'message' => 'Device dependencies has been deleted');
|
||||
} else {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
header('Content-Type: application/json');
|
||||
echo _json_encode($status);
|
||||
echo json_encode($status);
|
||||
|
@@ -15,46 +15,35 @@
|
||||
use LibreNMS\Authentication\Auth;
|
||||
|
||||
if (!Auth::user()->hasGlobalAdmin()) {
|
||||
$status = array('status' => 1, 'message' => 'You need to be admin');
|
||||
$status = ['status' => 1, 'message' => 'You need to be admin'];
|
||||
} else {
|
||||
foreach ($_POST['parent_ids'] as $parent) {
|
||||
$parent_ids = (array)$_POST['parent_ids'];
|
||||
$device_ids = (array)$_POST['device_ids'];
|
||||
|
||||
foreach ($parent_ids as $parent) {
|
||||
if (!is_numeric($parent)) {
|
||||
$status = array('status' => 1, 'message' => 'Parent ID must be an integer!');
|
||||
$status = ['status' => 1, 'message' => 'Parent ID must be an integer!'];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (count($_POST['parent_ids']) > 1 && in_array('0', $_POST['parent_ids'])) {
|
||||
$status = array('status' => 1, 'message' => 'Multiple parents cannot contain None-Parent!');
|
||||
if (count($parent_ids) > 1 && in_array('0', $parent_ids)) {
|
||||
$status = ['status' => 1, 'message' => 'Multiple parents cannot contain None-Parent!'];
|
||||
}
|
||||
|
||||
// A bit of an effort to reuse this code with dependency editing and the dependency wizard (editing multiple hosts at the same time)
|
||||
$device_arr = array();
|
||||
foreach ($_POST['device_ids'] as $dev) {
|
||||
if (!is_numeric($dev)) {
|
||||
$status = array('status' => 1, 'message' => 'Device ID must be an integer!');
|
||||
foreach ($device_ids as $device_id) {
|
||||
if (!is_numeric($device_id)) {
|
||||
$status = ['status' => 1, 'message' => 'Device ID must be an integer!'];
|
||||
break;
|
||||
} elseif (in_array($dev, $_POST['parent_ids'])) {
|
||||
$status = array('status' => 1, 'message' => 'A device cannot depend itself');
|
||||
} elseif (in_array($device_id, $parent_ids)) {
|
||||
$status = ['status' => 1, 'message' => 'A device cannot depend itself'];
|
||||
break;
|
||||
}
|
||||
$insert = array();
|
||||
foreach ($_POST['parent_ids'] as $parent) {
|
||||
if (is_numeric($parent) && $parent != 0) {
|
||||
$insert[] = array('parent_device_id' => $parent, 'child_device_id' => $dev);
|
||||
} elseif ($parent == 0) {
|
||||
// In case we receive a mixed array with $parent = 0 (which shouldn't happen)
|
||||
// Empty the insert array so we remove any previous dependency so 'None' takes precedence
|
||||
$insert = array();
|
||||
break;
|
||||
}
|
||||
}
|
||||
dbDelete('device_relationships', '`child_device_id` = ?', array($dev));
|
||||
if (!empty($insert)) {
|
||||
dbBulkInsert($insert, 'device_relationships');
|
||||
}
|
||||
|
||||
\App\Models\Device::find($device_id)->parents()->sync($parent_ids);
|
||||
|
||||
$status = array('status' => 0, 'message' => 'Device dependencies have been saved');
|
||||
}
|
||||
}
|
||||
header('Content-Type: application/json');
|
||||
echo _json_encode($status);
|
||||
echo json_encode($status);
|
||||
|
@@ -1,5 +1,6 @@
|
||||
<?php
|
||||
|
||||
use App\Models\Device;
|
||||
use LibreNMS\Authentication\Auth;
|
||||
|
||||
if ($_POST['editing']) {
|
||||
@@ -7,13 +8,9 @@ if ($_POST['editing']) {
|
||||
$updated = 0;
|
||||
|
||||
if (isset($_POST['parent_id'])) {
|
||||
$parent_id = array_diff((array)$_POST['parent_id'], ['0']);
|
||||
$res = dbDelete('device_relationships', '`child_device_id` = ?', array($device['device_id']));
|
||||
if (!in_array('0', $pr)) {
|
||||
foreach ($parent_id as $pr) {
|
||||
dbInsert(array('parent_device_id' => $pr, 'child_device_id' => $device['device_id']), 'device_relationships');
|
||||
}
|
||||
}
|
||||
$parents = array_diff((array)$_POST['parent_id'], ['0']);
|
||||
// TODO avoid loops!
|
||||
Device::find($device['device_id'])->parents()->sync($parents);
|
||||
}
|
||||
|
||||
$override_sysLocation_bool = mres($_POST['override_sysLocation']);
|
||||
|
@@ -24,6 +24,7 @@ use LibreNMS\Exceptions\LockException;
|
||||
use LibreNMS\Exceptions\SnmpVersionUnsupportedException;
|
||||
use LibreNMS\Util\IP;
|
||||
use LibreNMS\Util\MemcacheLock;
|
||||
use Monolog\Logger;
|
||||
|
||||
/**
|
||||
* Set debugging output
|
||||
|
@@ -472,6 +472,7 @@ devices:
|
||||
- { Field: override_sysLocation, Type: tinyint(1), 'Null': true, Extra: '', Default: '0' }
|
||||
- { Field: notes, Type: text, 'Null': true, Extra: '' }
|
||||
- { Field: port_association_mode, Type: int(11), 'Null': false, Extra: '', Default: '1' }
|
||||
- { Field: max_depth, Type: int(11), 'Null': false, Extra: '', Default: '0' }
|
||||
Indexes:
|
||||
PRIMARY: { Name: PRIMARY, Columns: [device_id], Unique: true, Type: BTREE }
|
||||
status: { Name: status, Columns: [status], Unique: false, Type: BTREE }
|
||||
|
@@ -53,6 +53,7 @@ pages:
|
||||
- Extensions/Dashboards.md
|
||||
- 5. Advanced Setup:
|
||||
- Support/1-Minute-Polling.md
|
||||
- Fast Ping Checking: Extensions/Fast-Ping-Check.md
|
||||
- Configuration docs: Support/Configuration.md
|
||||
- Authentication Options: Extensions/Authentication.md
|
||||
- Two-Factor Auth: Extensions/Two-Factor-Auth.md
|
||||
|
45
ping.php
Executable file
45
ping.php
Executable file
@@ -0,0 +1,45 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
|
||||
use App\Jobs\PingCheck;
|
||||
|
||||
$init_modules = ['alerts', 'laravel', 'nodb'];
|
||||
require __DIR__ . '/includes/init.php';
|
||||
|
||||
$options = getopt('hdvg:');
|
||||
|
||||
if (isset($options['h'])) {
|
||||
echo <<<'END'
|
||||
ping.php: Usage ping.php [-d] [-v] [-g group(s)]
|
||||
-d enable debug output
|
||||
-v enable verbose debug output
|
||||
-g only ping devices for this poller group, may be comma separated list
|
||||
|
||||
END;
|
||||
exit;
|
||||
}
|
||||
|
||||
set_debug(isset($options['d']));
|
||||
|
||||
if (isset($options['v'])) {
|
||||
global $vdebug;
|
||||
$vdebug = true;
|
||||
}
|
||||
|
||||
if (isset($options['g'])) {
|
||||
$groups = explode(',', $options['g']);
|
||||
} else {
|
||||
$groups = [];
|
||||
}
|
||||
|
||||
if ($config['noinfluxdb'] !== true && $config['influxdb']['enable'] === true) {
|
||||
$influxdb = influxdb_connect();
|
||||
} else {
|
||||
$influxdb = false;
|
||||
}
|
||||
|
||||
rrdtool_initialize();
|
||||
|
||||
PingCheck::dispatch(new PingCheck($groups));
|
||||
|
||||
rrdtool_close();
|
@@ -24,6 +24,8 @@
|
||||
* @author Neil Lathwood <neil@lathwood.co.uk>
|
||||
*/
|
||||
|
||||
use LibreNMS\Config;
|
||||
|
||||
$init_modules = array();
|
||||
require realpath(__DIR__ . '/..') . '/includes/init.php';
|
||||
|
||||
@@ -49,26 +51,42 @@ if (empty($hostname)) {
|
||||
exit;
|
||||
}
|
||||
|
||||
$step = $config['rrd']['step'];
|
||||
$heartbeat = $config['rrd']['heartbeat'];
|
||||
$rrdtool = $config['rrdtool'];
|
||||
$tmp_path = $config['temp_dir'];
|
||||
$system_step = Config::get('rrd.step', 300);
|
||||
$icmp_step = Config::get('ping_rrd_step', $step);
|
||||
$system_heartbeat = Config::get('rrd.heartbeat', $step * 2);
|
||||
$rrdtool = Config::get('rrdtool', 'rrdtool');
|
||||
$tmp_path = Config::get('temp_dir', '/tmp');
|
||||
|
||||
if ($hostname === 'all') {
|
||||
$hostname = '*';
|
||||
}
|
||||
$files = glob(get_rrd_dir($hostname) . '/*.rrd');
|
||||
|
||||
$run = readline("Are you sure you want to run this command [N/y]: ");
|
||||
if (!($run == 'y' || $run == 'Y')) {
|
||||
echo "Exiting....." . PHP_EOL;
|
||||
exit;
|
||||
}
|
||||
$converted = 0;
|
||||
$skipped = 0;
|
||||
$failed = 0;
|
||||
|
||||
foreach ($files as $file) {
|
||||
$random = $tmp_path.'/'.mt_rand() . '.xml';
|
||||
$tmp = explode('/', $file);
|
||||
$rrd_file = array_pop($tmp);
|
||||
$rrd_file = basename($file, '.rrd');
|
||||
|
||||
if ($rrd_file == 'ping-perf') {
|
||||
$step = $icmp_step;
|
||||
$heartbeat = $icmp_step * 2;
|
||||
} else {
|
||||
$step = $system_step;
|
||||
$heartbeat = $system_heartbeat;
|
||||
}
|
||||
|
||||
$rrd_info = shell_exec("$rrdtool info $file");
|
||||
preg_match('/step = (\d+)/', $rrd_info, $matches);
|
||||
|
||||
if ($matches[1] == $step) {
|
||||
d_echo("Skipping $file, step is already $step.\n");
|
||||
$skipped++;
|
||||
continue;
|
||||
}
|
||||
|
||||
echo "Converting $file: ";
|
||||
$command = "$rrdtool dump $file > $random &&
|
||||
sed -i 's/<step>\([0-9]*\)/<step>$step/' $random &&
|
||||
@@ -78,7 +96,11 @@ foreach ($files as $file) {
|
||||
exec($command, $output, $code);
|
||||
if ($code === 0) {
|
||||
echo "[OK]\n";
|
||||
$converted++;
|
||||
} else {
|
||||
echo "\033[FAIL]\n";
|
||||
$failed++;
|
||||
}
|
||||
}
|
||||
|
||||
echo "Converted: $converted Failed: $failed Skipped: $skipped\n";
|
||||
|
1
sql-schema/257.sql
Normal file
1
sql-schema/257.sql
Normal file
@@ -0,0 +1 @@
|
||||
ALTER TABLE devices ADD max_depth int DEFAULT 0 NOT NULL;
|
Reference in New Issue
Block a user