mirror of
https://github.com/librenms/librenms.git
synced 2024-10-07 16:52:45 +00:00
* Add transceivers module Move os specific code to OS Fix errors and updated connector names Add RouterOS, a lot less data there. Add Comware Add Exa, required a transformer function (mw to dBm) Add Junos, revision was too short Just starting on ui Graphs, and more ui some polling fixes collapse header for small screens refactor a bit Missed graphs Transceivers icon inline Use @once on popup javascript update db_schema.yaml Don't show transceivers in basic view basic view could use a review Apply fixes from StyleCI API functions Comware don't fail if port is missing Apply fixes from StyleCI Add alert rules to collection Device Overview Attempt to fix bad alert rule, probably needs more Fix up Comware and remove old sensors Mark transceiver metrics without thresholds as Unknown Routeros cleanup Exa cleanup Handle missing port Graph allow filter by channel More translations Add transceiver graphs to port graphs Add Cisco support, use entPhyscial module data if available Fix OcNos divisors Labels on transceiver page Show encoding if available Hacky OcNos port mapping Fix up Junos optics and remove old sensors FS switch support Metric casts to prevent thrashing Extra transform_function support Add link to transceivers page from overview Change default sort to group by type, then channel move some code out of overview blade template Fix bad type ocnos Apply fixes from StyleCI Add scales to graphs Add some test data Default sort order for metrcis in SQL applied by default Transceiver metrics threshold manual settings via WebUI and API Fixes to channels verbiage Fix severity calculations Add cable field for SM/MM/Copper Apply fixes from StyleCI update test data Show DDM Update DB schema file Extend serial field to 32, even though devices shouldn't be able to have one longer than 16 Missing import Add status field to database, that way we can support snmp implementations that only return an enum Add missing files Fix db_schema Fix style Fixes Style fix Work around phpstan issue Update transceivers.blade.php Missed getStatus() call Prevent extra dots when channels are not changed Update module to match upstream Save ocnos metrics as sensors Move to regular sensors add entity physical index Update UI to sensors WIP Apply fixes from StyleCI Forgot one change Update ui to use sensors Remove transceiver metrics Remove metric os discover code fs-switch pending Remove transceiver metrics for fs-centec Exa link up Revert all test data Fix up transceiver module interface Remove unused Convert class comware cache and transceiver type Fix some transceiver metrics filtering and formatting issues Consolidate display formatting Coalesce commare hh3cTransceiverTable walks to prevent double walk Use group to identify transceiver sensors Fixup routeros Fix up cisco update db_schema Small addition to docs Improve overview layout and add graph popup Update Junos update css files ddm should be nullable Increase the field length for type and model Cisco Improve detection when there is an intermediary container Add transceiver test data Apply fixes from StyleCI Fix incorrect test data Improve display formatting Fix test data Apply fixes from StyleCI Fix up more data Fix up more data Fix incorrect return type in routeros Update ocnos data * Remove some remaining references to transceiver_metrics table
1089 lines
31 KiB
PHP
1089 lines
31 KiB
PHP
<?php
|
|
|
|
namespace App\Models;
|
|
|
|
use App\View\SimpleTemplate;
|
|
use Carbon\Carbon;
|
|
use Fico7489\Laravel\Pivot\Traits\PivotEventTrait;
|
|
use Illuminate\Database\Eloquent\Builder;
|
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
|
use Illuminate\Database\Eloquent\ModelNotFoundException;
|
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
|
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
|
use Illuminate\Database\Eloquent\Relations\HasMany;
|
|
use Illuminate\Database\Eloquent\Relations\HasManyThrough;
|
|
use Illuminate\Database\Eloquent\Relations\HasOne;
|
|
use Illuminate\Database\Eloquent\Relations\MorphToMany;
|
|
use Illuminate\Database\Query\JoinClause;
|
|
use Illuminate\Support\Arr;
|
|
use Illuminate\Support\Str;
|
|
use LibreNMS\Exceptions\InvalidIpException;
|
|
use LibreNMS\Util\IP;
|
|
use LibreNMS\Util\IPv4;
|
|
use LibreNMS\Util\IPv6;
|
|
use LibreNMS\Util\Rewrite;
|
|
use LibreNMS\Util\Time;
|
|
use LibreNMS\Util\Url;
|
|
use Permissions;
|
|
|
|
/**
|
|
* @property-read int|null $ports_count
|
|
* @property-read int|null $sensors_count
|
|
* @property-read int|null $wirelessSensors_count
|
|
*
|
|
* @method static \Database\Factories\DeviceFactory factory(...$parameters)
|
|
*/
|
|
class Device extends BaseModel
|
|
{
|
|
use PivotEventTrait, HasFactory;
|
|
|
|
public $timestamps = false;
|
|
protected $primaryKey = 'device_id';
|
|
protected $fillable = [
|
|
'authalgo',
|
|
'authlevel',
|
|
'authname',
|
|
'authpass',
|
|
'community',
|
|
'cryptoalgo',
|
|
'cryptopass',
|
|
'disable_notify',
|
|
'disabled',
|
|
'features',
|
|
'hardware',
|
|
'hostname',
|
|
'display',
|
|
'icon',
|
|
'ignore',
|
|
'ignore_status',
|
|
'ip',
|
|
'location_id',
|
|
'notes',
|
|
'os',
|
|
'override_sysLocation',
|
|
'overwrite_ip',
|
|
'poller_group',
|
|
'port',
|
|
'port_association_mode',
|
|
'purpose',
|
|
'retries',
|
|
'serial',
|
|
'snmp_disable',
|
|
'snmp_max_repeaters',
|
|
'snmpver',
|
|
'status',
|
|
'status_reason',
|
|
'sysDescr',
|
|
'sysName',
|
|
'sysObjectID',
|
|
'timeout',
|
|
'transport',
|
|
'type',
|
|
'version',
|
|
'uptime',
|
|
];
|
|
|
|
protected $casts = [
|
|
'inserted' => 'datetime',
|
|
'last_discovered' => 'datetime',
|
|
'last_polled' => 'datetime',
|
|
'last_ping' => 'datetime',
|
|
'status' => 'boolean',
|
|
];
|
|
|
|
// ---- Helper Functions ----
|
|
|
|
public static function findByHostname(string $hostname): ?Device
|
|
{
|
|
return static::where('hostname', $hostname)->first();
|
|
}
|
|
|
|
/**
|
|
* Returns IP/Hostname where polling will be targeted to
|
|
*
|
|
* @param string|array $device hostname which will be triggered
|
|
* array $device associative array with device data
|
|
* @return string IP/Hostname to which Device polling is targeted
|
|
*/
|
|
public static function pollerTarget($device)
|
|
{
|
|
if (! is_array($device)) {
|
|
$ret = static::where('hostname', $device)->first(['hostname', 'overwrite_ip']);
|
|
if (empty($ret)) {
|
|
return $device;
|
|
}
|
|
$overwrite_ip = $ret->overwrite_ip;
|
|
$hostname = $ret->hostname;
|
|
} elseif (array_key_exists('overwrite_ip', $device)) {
|
|
$overwrite_ip = $device['overwrite_ip'];
|
|
$hostname = $device['hostname'];
|
|
} else {
|
|
return $device['hostname'];
|
|
}
|
|
|
|
return $overwrite_ip ?: $hostname;
|
|
}
|
|
|
|
public static function findByIp(?string $ip): ?Device
|
|
{
|
|
if (! IP::isValid($ip)) {
|
|
return null;
|
|
}
|
|
|
|
$device = static::where('hostname', $ip)->orWhere('ip', inet_pton($ip))->first();
|
|
|
|
if ($device) {
|
|
return $device;
|
|
}
|
|
|
|
try {
|
|
$ipv4 = new IPv4($ip);
|
|
$port = Ipv4Address::where('ipv4_address', (string) $ipv4)
|
|
->with('port', 'port.device')
|
|
->firstOrFail()->port;
|
|
if ($port) {
|
|
return $port->device;
|
|
}
|
|
} catch (InvalidIpException $e) {
|
|
//
|
|
} catch (ModelNotFoundException $e) {
|
|
//
|
|
}
|
|
|
|
try {
|
|
$ipv6 = new IPv6($ip);
|
|
$port = Ipv6Address::where('ipv6_address', $ipv6->uncompressed())
|
|
->with(['port', 'port.device'])
|
|
->firstOrFail()->port;
|
|
if ($port) {
|
|
return $port->device;
|
|
}
|
|
} catch (InvalidIpException $e) {
|
|
//
|
|
} catch (ModelNotFoundException $e) {
|
|
//
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
public function hasSnmpInfo(): bool
|
|
{
|
|
if ($this->snmpver == 'v3') {
|
|
if ($this->authlevel == 'authNoPriv') {
|
|
return ! empty($this->authname) && ! empty($this->authpass);
|
|
}
|
|
|
|
if ($this->authlevel == 'authPriv') {
|
|
return ! empty($this->authname)
|
|
&& ! empty($this->authpass)
|
|
&& ! empty($this->cryptoalgo)
|
|
&& ! empty($this->cryptopass);
|
|
}
|
|
|
|
return $this->authlevel !== 'noAuthNoPriv'; // reject if not noAuthNoPriv
|
|
}
|
|
|
|
if ($this->snmpver == 'v2c' || $this->snmpver == 'v1') {
|
|
return ! empty($this->community);
|
|
}
|
|
|
|
return false; // no known snmpver
|
|
}
|
|
|
|
/**
|
|
* Get VRF contexts to poll.
|
|
* If no contexts are found, return the default context ''
|
|
*
|
|
* @return array
|
|
*/
|
|
public function getVrfContexts(): array
|
|
{
|
|
return $this->vrfLites->isEmpty() ? [''] : $this->vrfLites->pluck('context_name')->all();
|
|
}
|
|
|
|
/**
|
|
* Get the display name of this device based on the display format string
|
|
* The default is {{ $hostname }} controlled by the device_display_default setting
|
|
*/
|
|
public function displayName(): string
|
|
{
|
|
$hostname_is_ip = IP::isValid($this->hostname);
|
|
|
|
return SimpleTemplate::parse($this->display ?: \LibreNMS\Config::get('device_display_default', '{{ $hostname }}'), [
|
|
'hostname' => $this->hostname,
|
|
'sysName' => $this->sysName ?: $this->hostname,
|
|
'sysName_fallback' => $hostname_is_ip ? $this->sysName : $this->hostname,
|
|
'ip' => $this->overwrite_ip ?: ($hostname_is_ip ? $this->hostname : $this->ip),
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Returns the device name if not already displayed
|
|
*/
|
|
public function name(): string
|
|
{
|
|
$display = $this->displayName();
|
|
|
|
if (! Str::contains($display, $this->hostname)) {
|
|
return (string) $this->hostname;
|
|
} elseif (! Str::contains($display, $this->sysName)) {
|
|
return (string) $this->sysName;
|
|
}
|
|
|
|
return '';
|
|
}
|
|
|
|
public function isUnderMaintenance()
|
|
{
|
|
if (! $this->device_id) {
|
|
return false;
|
|
}
|
|
|
|
$query = AlertSchedule::isActive()
|
|
->where(function (Builder $query) {
|
|
$query->whereHas('devices', function (Builder $query) {
|
|
$query->where('alert_schedulables.alert_schedulable_id', $this->device_id);
|
|
});
|
|
|
|
if ($this->groups->isNotEmpty()) {
|
|
$query->orWhereHas('deviceGroups', function (Builder $query) {
|
|
$query->whereIntegerInRaw('alert_schedulables.alert_schedulable_id', $this->groups->pluck('id'));
|
|
});
|
|
}
|
|
|
|
if ($this->location) {
|
|
$query->orWhereHas('locations', function (Builder $query) {
|
|
$query->where('alert_schedulables.alert_schedulable_id', $this->location->id);
|
|
});
|
|
}
|
|
});
|
|
|
|
return $query->exists();
|
|
}
|
|
|
|
/**
|
|
* Get the shortened display name of this device.
|
|
* Length is always overridden by shorthost_target_length.
|
|
*
|
|
* @param int $length length to shorten to, will not break up words so may be longer
|
|
* @return string
|
|
*/
|
|
public function shortDisplayName($length = 12)
|
|
{
|
|
$name = $this->displayName();
|
|
|
|
// IP addresses should not be shortened
|
|
if (IP::isValid($name)) {
|
|
return $name;
|
|
}
|
|
|
|
$length = \LibreNMS\Config::get('shorthost_target_length', $length);
|
|
if ($length < strlen($name)) {
|
|
$take = max(substr_count($name, '.', 0, $length), 1);
|
|
|
|
return implode('.', array_slice(explode('.', $name), 0, $take));
|
|
}
|
|
|
|
return $name;
|
|
}
|
|
|
|
/**
|
|
* Get the current DeviceOutage if there is one (if device is down)
|
|
*/
|
|
public function getCurrentOutage(): ?DeviceOutage
|
|
{
|
|
return $this->relationLoaded('outages')
|
|
? $this->outages->whereNull('up_again')->sortBy('going_down', descending: true)->first()
|
|
: $this->outages()->whereNull('up_again')->orderBy('going_down', 'desc')->first();
|
|
}
|
|
|
|
/**
|
|
* Get the time this device went down
|
|
*/
|
|
public function downSince(): Carbon
|
|
{
|
|
$deviceOutage = $this->getCurrentOutage();
|
|
|
|
if ($deviceOutage) {
|
|
return Carbon::createFromTimestamp((int) $deviceOutage->going_down);
|
|
}
|
|
|
|
return $this->last_polled ?? Carbon::now();
|
|
}
|
|
|
|
/**
|
|
* Check if user can access this device.
|
|
*
|
|
* @param User $user
|
|
* @return bool
|
|
*/
|
|
public function canAccess($user)
|
|
{
|
|
if (! $user) {
|
|
return false;
|
|
}
|
|
|
|
if ($user->hasGlobalRead()) {
|
|
return true;
|
|
}
|
|
|
|
return Permissions::canAccessDevice($this->device_id, $user->user_id);
|
|
}
|
|
|
|
public function formatDownUptime($short = false): string
|
|
{
|
|
$time = ($this->status == 1) ? $this->uptime : $this->last_polled?->diffInSeconds();
|
|
|
|
return Time::formatInterval($time, $short);
|
|
}
|
|
|
|
/**
|
|
* @return string
|
|
*/
|
|
public function logo()
|
|
{
|
|
$base_name = pathinfo($this->icon, PATHINFO_FILENAME);
|
|
$options = [
|
|
"images/logos/$base_name.svg",
|
|
"images/logos/$base_name.png",
|
|
"images/os/$base_name.svg",
|
|
"images/os/$base_name.png",
|
|
];
|
|
|
|
foreach ($options as $file) {
|
|
if (is_file(public_path() . "/$file")) {
|
|
return asset($file);
|
|
}
|
|
}
|
|
|
|
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();
|
|
}
|
|
|
|
public function getAttrib($name, $default = null)
|
|
{
|
|
return $this->attribs->pluck('attrib_value', 'attrib_type')->get($name, $default);
|
|
}
|
|
|
|
public function setAttrib($name, $value)
|
|
{
|
|
$attrib = $this->attribs->first(function ($item) use ($name) {
|
|
return $item->attrib_type === $name;
|
|
});
|
|
|
|
if (! $attrib) {
|
|
$attrib = new DeviceAttrib(['attrib_type' => $name]);
|
|
$this->attribs->push($attrib);
|
|
}
|
|
|
|
$attrib->attrib_value = $value;
|
|
|
|
return (bool) $this->attribs()->save($attrib);
|
|
}
|
|
|
|
public function forgetAttrib($name)
|
|
{
|
|
$attrib_index = $this->attribs->search(function ($attrib) use ($name) {
|
|
return $attrib->attrib_type === $name;
|
|
});
|
|
|
|
if ($attrib_index !== false) {
|
|
$deleted = (bool) $this->attribs->get($attrib_index)->delete();
|
|
// only forget the attrib_index after delete, otherwise delete() will fail fatally with:
|
|
// Symfony\\Component\\Debug\Exception\\FatalThrowableError(code: 0): Call to a member function delete() on null
|
|
$this->attribs->forget((string) $attrib_index);
|
|
|
|
return $deleted;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public function getAttribs()
|
|
{
|
|
return $this->attribs->pluck('attrib_value', 'attrib_type')->toArray();
|
|
}
|
|
|
|
/**
|
|
* Update the location to the correct location and update GPS if needed
|
|
*
|
|
* @param \App\Models\Location|string $new_location location data
|
|
* @param bool $doLookup try to lookup the GPS coordinates
|
|
*/
|
|
public function setLocation($new_location, bool $doLookup = false)
|
|
{
|
|
$new_location = $new_location instanceof Location ? $new_location : new Location(['location' => $new_location]);
|
|
$new_location->location = $new_location->location ? Rewrite::location($new_location->location) : null;
|
|
$coord = array_filter($new_location->only(['lat', 'lng']));
|
|
|
|
if (! $this->override_sysLocation) {
|
|
if (! $new_location->location) { // disassociate if the location name is empty
|
|
$this->location()->dissociate();
|
|
|
|
return;
|
|
}
|
|
|
|
if (! $this->relationLoaded('location') || $this->location?->location !== $new_location->location) {
|
|
if (! $new_location->exists) { // don't fetch if new location persisted to the DB, just use it
|
|
$new_location = Location::firstOrCreate(['location' => $new_location->location], $coord);
|
|
}
|
|
$this->location()->associate($new_location);
|
|
}
|
|
}
|
|
|
|
// set coordinates
|
|
if ($this->location && ! $this->location->fixed_coordinates) {
|
|
$this->location->fill($coord);
|
|
if ($doLookup && empty($coord)) { // only if requested and coordinates not passed explicitly
|
|
$this->location->lookupCoordinates($this->hostname);
|
|
}
|
|
}
|
|
}
|
|
|
|
// ---- Accessors/Mutators ----
|
|
|
|
public function getIconAttribute($icon): string
|
|
{
|
|
return Str::start(Url::findOsImage($this->os, $this->features, $icon), 'images/os/');
|
|
}
|
|
|
|
public function getIpAttribute($ip): ?string
|
|
{
|
|
if (empty($ip)) {
|
|
return null;
|
|
}
|
|
|
|
// @ suppresses warning, inet_ntop() returns false if it fails
|
|
return @inet_ntop($ip) ?: null;
|
|
}
|
|
|
|
public function setIpAttribute($ip): void
|
|
{
|
|
$this->attributes['ip'] = $ip ? inet_pton($ip) : null;
|
|
}
|
|
|
|
public function setStatusAttribute($status): void
|
|
{
|
|
$this->attributes['status'] = (int) $status;
|
|
}
|
|
|
|
public function setSysDescrAttribute(?string $sysDescr): void
|
|
{
|
|
$this->attributes['sysDescr'] = $sysDescr === null ? null : trim(str_replace(chr(218), "\n", $sysDescr), "\\\" \r\n\t\0");
|
|
}
|
|
|
|
public function setSysNameAttribute(?string $sysName): void
|
|
{
|
|
$this->attributes['sysName'] = $sysName === null ? null : str_replace("\n", '', strtolower(trim($sysName)));
|
|
}
|
|
|
|
// ---- Query scopes ----
|
|
|
|
public function scopeIsUp($query)
|
|
{
|
|
return $query->where([
|
|
['status', '=', 1],
|
|
['ignore', '=', 0],
|
|
['disable_notify', '=', 0],
|
|
['disabled', '=', 0],
|
|
]);
|
|
}
|
|
|
|
public function scopeIsActive($query)
|
|
{
|
|
return $query->where([
|
|
['ignore', '=', 0],
|
|
['disabled', '=', 0],
|
|
]);
|
|
}
|
|
|
|
public function scopeIsDown($query)
|
|
{
|
|
return $query->where([
|
|
['status', '=', 0],
|
|
['disable_notify', '=', 0],
|
|
['ignore', '=', 0],
|
|
['disabled', '=', 0],
|
|
]);
|
|
}
|
|
|
|
public function scopeIsIgnored($query)
|
|
{
|
|
return $query->where([
|
|
['ignore', '=', 1],
|
|
['disabled', '=', 0],
|
|
]);
|
|
}
|
|
|
|
public function scopeNotIgnored($query)
|
|
{
|
|
return $query->where([
|
|
['ignore', '=', 0],
|
|
]);
|
|
}
|
|
|
|
public function scopeIsDisabled($query)
|
|
{
|
|
return $query->where([
|
|
['disabled', '=', 1],
|
|
]);
|
|
}
|
|
|
|
public function scopeIsDisableNotify($query)
|
|
{
|
|
return $query->where([
|
|
['disable_notify', '=', 1],
|
|
]);
|
|
}
|
|
|
|
public function scopeIsNotDisabled($query)
|
|
{
|
|
return $query->where([
|
|
['disable_notify', '=', 0],
|
|
['disabled', '=', 0],
|
|
]);
|
|
}
|
|
|
|
public function scopeWhereAttributeDisabled(Builder $query, string $attribute): Builder
|
|
{
|
|
return $query->leftJoin('devices_attribs', function (JoinClause $query) use ($attribute) {
|
|
$query->on('devices.device_id', 'devices_attribs.device_id')
|
|
->where('devices_attribs.attrib_type', $attribute);
|
|
})->where(function (Builder $query) {
|
|
$query->whereNull('devices_attribs.attrib_value')
|
|
->orWhere('devices_attribs.attrib_value', '!=', 'true');
|
|
});
|
|
}
|
|
|
|
public function scopeWhereUptime($query, $uptime, $modifier = '<')
|
|
{
|
|
return $query->where([
|
|
['uptime', '>', 0],
|
|
['uptime', $modifier, $uptime],
|
|
]);
|
|
}
|
|
|
|
public function scopeCanPing(Builder $query): Builder
|
|
{
|
|
return $this->scopeWhereAttributeDisabled($query->where('disabled', 0), 'override_icmp_disable');
|
|
}
|
|
|
|
public function scopeHasAccess($query, User $user)
|
|
{
|
|
return $this->hasDeviceAccess($query, $user);
|
|
}
|
|
|
|
public function scopeInDeviceGroup($query, $deviceGroup)
|
|
{
|
|
return $query->whereIn(
|
|
$query->qualifyColumn('device_id'), function ($query) use ($deviceGroup) {
|
|
$query->select('device_id')
|
|
->from('device_group_device')
|
|
->whereIn('device_group_id', Arr::wrap($deviceGroup));
|
|
}
|
|
);
|
|
}
|
|
|
|
public function scopeNotInDeviceGroup($query, $deviceGroup)
|
|
{
|
|
return $query->whereNotIn(
|
|
$query->qualifyColumn('device_id'), function ($query) use ($deviceGroup) {
|
|
$query->select('device_id')
|
|
->from('device_group_device')
|
|
->whereIn('device_group_id', Arr::wrap($deviceGroup));
|
|
}
|
|
);
|
|
}
|
|
|
|
public function scopeInServiceTemplate($query, $serviceTemplate)
|
|
{
|
|
return $query->whereIn(
|
|
$query->qualifyColumn('device_id'), function ($query) use ($serviceTemplate) {
|
|
$query->select('device_id')
|
|
->from('service_templates_device')
|
|
->where('service_template_id', $serviceTemplate);
|
|
}
|
|
);
|
|
}
|
|
|
|
public function scopeNotInServiceTemplate($query, $serviceTemplate)
|
|
{
|
|
return $query->whereNotIn(
|
|
$query->qualifyColumn('device_id'), function ($query) use ($serviceTemplate) {
|
|
$query->select('device_id')
|
|
->from('service_templates_device')
|
|
->where('service_template_id', $serviceTemplate);
|
|
}
|
|
);
|
|
}
|
|
|
|
public function scopeWhereDeviceSpec(Builder $query, ?string $deviceSpec): Builder
|
|
{
|
|
if (empty($deviceSpec)) {
|
|
return $query;
|
|
} elseif ($deviceSpec == 'all') {
|
|
return $query;
|
|
} elseif ($deviceSpec == 'even') {
|
|
return $query->whereRaw('device_id % 2 = 0');
|
|
} elseif ($deviceSpec == 'odd') {
|
|
return $query->whereRaw('device_id % 2 = 1');
|
|
} elseif (is_numeric($deviceSpec)) {
|
|
return $query->where('device_id', $deviceSpec);
|
|
} elseif (str_contains($deviceSpec, '*')) {
|
|
return $query->where('hostname', 'like', str_replace('*', '%', $deviceSpec));
|
|
}
|
|
|
|
return $query->where('hostname', $deviceSpec);
|
|
}
|
|
|
|
// ---- Define Relationships ----
|
|
|
|
public function accessPoints(): HasMany
|
|
{
|
|
return $this->hasMany(AccessPoint::class, 'device_id');
|
|
}
|
|
|
|
public function alerts(): HasMany
|
|
{
|
|
return $this->hasMany(\App\Models\Alert::class, 'device_id');
|
|
}
|
|
|
|
public function alertLogs(): HasMany
|
|
{
|
|
return $this->hasMany(\App\Models\AlertLog::class, 'device_id');
|
|
}
|
|
|
|
public function alertSchedules(): MorphToMany
|
|
{
|
|
return $this->morphToMany(\App\Models\AlertSchedule::class, 'alert_schedulable', 'alert_schedulables', 'schedule_id', 'schedule_id');
|
|
}
|
|
|
|
public function applications(): HasMany
|
|
{
|
|
return $this->hasMany(\App\Models\Application::class, 'device_id');
|
|
}
|
|
|
|
public function attribs(): HasMany
|
|
{
|
|
return $this->hasMany(\App\Models\DeviceAttrib::class, 'device_id');
|
|
}
|
|
|
|
public function availability(): HasMany
|
|
{
|
|
return $this->hasMany(\App\Models\Availability::class, 'device_id');
|
|
}
|
|
|
|
public function bgppeers(): HasMany
|
|
{
|
|
return $this->hasMany(\App\Models\BgpPeer::class, 'device_id');
|
|
}
|
|
|
|
public function cefSwitching(): HasMany
|
|
{
|
|
return $this->hasMany(\App\Models\CefSwitching::class, 'device_id');
|
|
}
|
|
|
|
public function children(): BelongsToMany
|
|
{
|
|
return $this->belongsToMany(self::class, 'device_relationships', 'parent_device_id', 'child_device_id');
|
|
}
|
|
|
|
public function components(): HasMany
|
|
{
|
|
return $this->hasMany(\App\Models\Component::class, 'device_id');
|
|
}
|
|
|
|
public function diskIo(): HasMany
|
|
{
|
|
return $this->hasMany(\App\Models\DiskIo::class, 'device_id');
|
|
}
|
|
|
|
public function hostResources(): HasMany
|
|
{
|
|
return $this->hasMany(HrDevice::class, 'device_id');
|
|
}
|
|
|
|
public function hostResourceValues(): HasOne
|
|
{
|
|
return $this->hasOne(HrSystem::class, 'device_id');
|
|
}
|
|
|
|
public function entityPhysical(): HasMany
|
|
{
|
|
return $this->hasMany(EntPhysical::class, 'device_id');
|
|
}
|
|
|
|
public function entityState(): HasMany
|
|
{
|
|
return $this->hasMany(EntityState::class, 'device_id');
|
|
}
|
|
|
|
public function eventlogs(): HasMany
|
|
{
|
|
return $this->hasMany(\App\Models\Eventlog::class, 'device_id', 'device_id');
|
|
}
|
|
|
|
public function graphs(): HasMany
|
|
{
|
|
return $this->hasMany(\App\Models\DeviceGraph::class, 'device_id');
|
|
}
|
|
|
|
public function groups(): BelongsToMany
|
|
{
|
|
return $this->belongsToMany(\App\Models\DeviceGroup::class, 'device_group_device', 'device_id', 'device_group_id');
|
|
}
|
|
|
|
public function ipsecTunnels(): HasMany
|
|
{
|
|
return $this->hasMany(IpsecTunnel::class, 'device_id');
|
|
}
|
|
|
|
public function ipv4(): HasManyThrough
|
|
{
|
|
return $this->hasManyThrough(\App\Models\Ipv4Address::class, \App\Models\Port::class, 'device_id', 'port_id', 'device_id', 'port_id');
|
|
}
|
|
|
|
public function ipv6(): HasManyThrough
|
|
{
|
|
return $this->hasManyThrough(\App\Models\Ipv6Address::class, \App\Models\Port::class, 'device_id', 'port_id', 'device_id', 'port_id');
|
|
}
|
|
|
|
public function isisAdjacencies(): HasMany
|
|
{
|
|
return $this->hasMany(\App\Models\IsisAdjacency::class, 'device_id', 'device_id');
|
|
}
|
|
|
|
public function links(): HasMany
|
|
{
|
|
return $this->hasMany(\App\Models\Link::class, 'local_device_id');
|
|
}
|
|
|
|
public function remoteLinks(): HasMany
|
|
{
|
|
return $this->hasMany(\App\Models\Link::class, 'remote_device_id');
|
|
}
|
|
|
|
public function allLinks(): \Illuminate\Support\Collection
|
|
{
|
|
return $this->links->merge($this->remoteLinks);
|
|
}
|
|
|
|
public function location(): BelongsTo
|
|
{
|
|
return $this->belongsTo(\App\Models\Location::class, 'location_id', 'id');
|
|
}
|
|
|
|
public function macs(): HasMany
|
|
{
|
|
return $this->hasMany(Ipv4Mac::class, 'device_id');
|
|
}
|
|
|
|
public function maps(): HasManyThrough
|
|
{
|
|
return $this->hasManyThrough(CustomMap::class, CustomMapNode::class, 'device_id', 'custom_map_id', 'device_id', 'custom_map_id')
|
|
->distinct();
|
|
}
|
|
|
|
public function mefInfo(): HasMany
|
|
{
|
|
return $this->hasMany(MefInfo::class, 'device_id');
|
|
}
|
|
|
|
public function muninPlugins(): HasMany
|
|
{
|
|
return $this->hasMany(\App\Models\MuninPlugin::class, 'device_id');
|
|
}
|
|
|
|
public function netscalerVservers(): HasMany
|
|
{
|
|
return $this->hasMany(NetscalerVserver::class, 'device_id');
|
|
}
|
|
|
|
public function ospfAreas(): HasMany
|
|
{
|
|
return $this->hasMany(\App\Models\OspfArea::class, 'device_id');
|
|
}
|
|
|
|
public function ospfInstances(): HasMany
|
|
{
|
|
return $this->hasMany(\App\Models\OspfInstance::class, 'device_id');
|
|
}
|
|
|
|
public function ospfNbrs(): HasMany
|
|
{
|
|
return $this->hasMany(\App\Models\OspfNbr::class, 'device_id');
|
|
}
|
|
|
|
public function ospfPorts(): HasMany
|
|
{
|
|
return $this->hasMany(\App\Models\OspfPort::class, 'device_id');
|
|
}
|
|
|
|
public function packages(): HasMany
|
|
{
|
|
return $this->hasMany(\App\Models\Package::class, 'device_id', 'device_id');
|
|
}
|
|
|
|
public function parents(): BelongsToMany
|
|
{
|
|
return $this->belongsToMany(self::class, 'device_relationships', 'child_device_id', 'parent_device_id');
|
|
}
|
|
|
|
public function ports(): HasMany
|
|
{
|
|
return $this->hasMany(\App\Models\Port::class, 'device_id', 'device_id');
|
|
}
|
|
|
|
public function portsAdsl(): HasManyThrough
|
|
{
|
|
return $this->hasManyThrough(\App\Models\PortAdsl::class, \App\Models\Port::class, 'device_id', 'port_id');
|
|
}
|
|
|
|
public function portsFdb(): HasMany
|
|
{
|
|
return $this->hasMany(\App\Models\PortsFdb::class, 'device_id', 'device_id');
|
|
}
|
|
|
|
public function portsNac(): HasMany
|
|
{
|
|
return $this->hasMany(\App\Models\PortsNac::class, 'device_id', 'device_id');
|
|
}
|
|
|
|
public function portsStack(): HasMany
|
|
{
|
|
return $this->hasMany(\App\Models\PortStack::class, 'device_id', 'device_id');
|
|
}
|
|
|
|
public function portsStp(): HasMany
|
|
{
|
|
return $this->hasMany(\App\Models\PortStp::class, 'device_id', 'device_id');
|
|
}
|
|
|
|
public function portsVdsl(): HasManyThrough
|
|
{
|
|
return $this->hasManyThrough(\App\Models\PortVdsl::class, \App\Models\Port::class, 'device_id', 'port_id');
|
|
}
|
|
|
|
public function portsVlan(): HasMany
|
|
{
|
|
return $this->hasMany(\App\Models\PortVlan::class, 'device_id', 'device_id');
|
|
}
|
|
|
|
public function processes(): HasMany
|
|
{
|
|
return $this->hasMany(\App\Models\Process::class, 'device_id');
|
|
}
|
|
|
|
public function processors(): HasMany
|
|
{
|
|
return $this->hasMany(\App\Models\Processor::class, 'device_id');
|
|
}
|
|
|
|
public function routes(): HasMany
|
|
{
|
|
return $this->hasMany(Route::class, 'device_id');
|
|
}
|
|
|
|
public function rules(): BelongsToMany
|
|
{
|
|
return $this->belongsToMany(\App\Models\AlertRule::class, 'alert_device_map', 'device_id', 'rule_id');
|
|
}
|
|
|
|
public function sensors(): HasMany
|
|
{
|
|
return $this->hasMany(\App\Models\Sensor::class, 'device_id');
|
|
}
|
|
|
|
public function serviceTemplates(): BelongsToMany
|
|
{
|
|
return $this->belongsToMany(\App\Models\ServiceTemplate::class, 'service_templates_device', 'device_id', 'service_template_id');
|
|
}
|
|
|
|
public function services(): HasMany
|
|
{
|
|
return $this->hasMany(\App\Models\Service::class, 'device_id');
|
|
}
|
|
|
|
public function storage(): HasMany
|
|
{
|
|
return $this->hasMany(\App\Models\Storage::class, 'device_id');
|
|
}
|
|
|
|
public function stpInstances(): HasMany
|
|
{
|
|
return $this->hasMany(Stp::class, 'device_id');
|
|
}
|
|
|
|
public function stpPorts(): HasMany
|
|
{
|
|
return $this->hasMany(\App\Models\PortStp::class, 'device_id');
|
|
}
|
|
|
|
public function mempools(): HasMany
|
|
{
|
|
return $this->hasMany(\App\Models\Mempool::class, 'device_id');
|
|
}
|
|
|
|
public function mplsLsps(): HasMany
|
|
{
|
|
return $this->hasMany(\App\Models\MplsLsp::class, 'device_id');
|
|
}
|
|
|
|
public function mplsLspPaths(): HasMany
|
|
{
|
|
return $this->hasMany(\App\Models\MplsLspPath::class, 'device_id');
|
|
}
|
|
|
|
public function mplsSdps(): HasMany
|
|
{
|
|
return $this->hasMany(\App\Models\MplsSdp::class, 'device_id');
|
|
}
|
|
|
|
public function mplsServices(): HasMany
|
|
{
|
|
return $this->hasMany(\App\Models\MplsService::class, 'device_id');
|
|
}
|
|
|
|
public function mplsSaps(): HasMany
|
|
{
|
|
return $this->hasMany(\App\Models\MplsSap::class, 'device_id');
|
|
}
|
|
|
|
public function mplsSdpBinds(): HasMany
|
|
{
|
|
return $this->hasMany(\App\Models\MplsSdpBind::class, 'device_id');
|
|
}
|
|
|
|
public function mplsTunnelArHops(): HasMany
|
|
{
|
|
return $this->hasMany(\App\Models\MplsTunnelArHop::class, 'device_id');
|
|
}
|
|
|
|
public function mplsTunnelCHops(): HasMany
|
|
{
|
|
return $this->hasMany(\App\Models\MplsTunnelCHop::class, 'device_id');
|
|
}
|
|
|
|
public function outages(): HasMany
|
|
{
|
|
return $this->hasMany(DeviceOutage::class, 'device_id');
|
|
}
|
|
|
|
public function printerSupplies(): HasMany
|
|
{
|
|
return $this->hasMany(PrinterSupply::class, 'device_id');
|
|
}
|
|
|
|
public function pseudowires(): HasMany
|
|
{
|
|
return $this->hasMany(Pseudowire::class, 'device_id');
|
|
}
|
|
|
|
public function rServers(): HasMany
|
|
{
|
|
return $this->hasMany(LoadbalancerRserver::class, 'device_id');
|
|
}
|
|
|
|
public function slas(): HasMany
|
|
{
|
|
return $this->hasMany(Sla::class, 'device_id');
|
|
}
|
|
|
|
public function syslogs(): HasMany
|
|
{
|
|
return $this->hasMany(\App\Models\Syslog::class, 'device_id', 'device_id');
|
|
}
|
|
|
|
public function tnmsNeInfo(): HasMany
|
|
{
|
|
return $this->hasMany(TnmsneInfo::class, 'device_id');
|
|
}
|
|
|
|
public function transceivers(): HasMany
|
|
{
|
|
return $this->hasMany(Transceiver::class, 'device_id');
|
|
}
|
|
|
|
public function users(): BelongsToMany
|
|
{
|
|
// FIXME does not include global read
|
|
return $this->belongsToMany(\App\Models\User::class, 'devices_perms', 'device_id', 'user_id');
|
|
}
|
|
|
|
public function vminfo(): HasMany
|
|
{
|
|
return $this->hasMany(\App\Models\Vminfo::class, 'device_id');
|
|
}
|
|
|
|
public function vlans(): HasMany
|
|
{
|
|
return $this->hasMany(\App\Models\Vlan::class, 'device_id');
|
|
}
|
|
|
|
public function vrfLites(): HasMany
|
|
{
|
|
return $this->hasMany(\App\Models\VrfLite::class, 'device_id');
|
|
}
|
|
|
|
public function vrfs(): HasMany
|
|
{
|
|
return $this->hasMany(\App\Models\Vrf::class, 'device_id');
|
|
}
|
|
|
|
public function vServers(): HasMany
|
|
{
|
|
return $this->hasMany(LoadbalancerVserver::class, 'device_id');
|
|
}
|
|
|
|
public function wirelessSensors(): HasMany
|
|
{
|
|
return $this->hasMany(\App\Models\WirelessSensor::class, 'device_id');
|
|
}
|
|
}
|