FDB Tables improve performance (#15333)

* FDB Tables improve performance
Reduce unnecessary sql queries, by using a relationship
Cache vendor oui lookups

* Oui already "clean"

* Fix typo
This commit is contained in:
Tony Murray
2023-09-25 19:49:22 -05:00
committed by GitHub
parent 9b159f8646
commit c8041b6699
3 changed files with 42 additions and 57 deletions

View File

@@ -27,6 +27,7 @@ namespace LibreNMS\Util;
use App\Models\Device; use App\Models\Device;
use Illuminate\Support\Arr; use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\DB;
use LibreNMS\Config; use LibreNMS\Config;
@@ -158,10 +159,12 @@ class Rewrite
{ {
$oui = substr($mac, 0, 6); $oui = substr($mac, 0, 6);
$results = DB::table('vendor_ouis') $results = Cache::remember($oui, 21600, function () use ($oui) {
->where('oui', 'like', "$oui%") // possible matches return DB::table('vendor_ouis')
->orderBy('oui', 'desc') // so we can check longer ones first if we have them ->where('oui', 'like', "$oui%") // possible matches
->pluck('vendor', 'oui'); ->orderBy('oui', 'desc') // so we can check longer ones first if we have them
->pluck('vendor', 'oui');
});
if (count($results) == 1) { if (count($results) == 1) {
return Arr::first($results); return Arr::first($results);

View File

@@ -31,6 +31,7 @@ use App\Models\PortsFdb;
use App\Models\Vlan; use App\Models\Vlan;
use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Builder;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\DB;
use LibreNMS\Util\IP; use LibreNMS\Util\IP;
use LibreNMS\Util\Rewrite; use LibreNMS\Util\Rewrite;
@@ -39,7 +40,6 @@ use LibreNMS\Util\Url;
class FdbTablesController extends TableController class FdbTablesController extends TableController
{ {
protected $macCountCache = []; protected $macCountCache = [];
protected $ipCache = [];
protected function rules() protected function rules()
{ {
@@ -67,7 +67,8 @@ class FdbTablesController extends TableController
*/ */
protected function baseQuery($request) protected function baseQuery($request)
{ {
return PortsFdb::hasAccess($request->user())->with(['device', 'port', 'vlan'])->select('ports_fdb.*'); return PortsFdb::hasAccess($request->user())
->with(['device', 'port', 'vlan', 'ipv4Addresses']);
} }
/** /**
@@ -158,17 +159,15 @@ class FdbTablesController extends TableController
*/ */
public function formatItem($fdb_entry) public function formatItem($fdb_entry)
{ {
$ip_info = $this->findIps($fdb_entry->mac_address);
$item = [ $item = [
'device' => $fdb_entry->device ? Url::deviceLink($fdb_entry->device) : '', 'device' => $fdb_entry->device ? Url::deviceLink($fdb_entry->device) : '',
'mac_address' => Rewrite::readableMac($fdb_entry->mac_address), 'mac_address' => Rewrite::readableMac($fdb_entry->mac_address),
'mac_oui' => Rewrite::readableOUI($fdb_entry->mac_address), 'mac_oui' => Rewrite::readableOUI($fdb_entry->mac_address),
'ipv4_address' => $ip_info['ips']->implode(', '), 'ipv4_address' => $fdb_entry->ipv4Addresses->implode(', '),
'interface' => '', 'interface' => '',
'vlan' => $fdb_entry->vlan ? $fdb_entry->vlan->vlan_vlan : '', 'vlan' => $fdb_entry->vlan ? $fdb_entry->vlan->vlan_vlan : '',
'description' => '', 'description' => '',
'dnsname' => $ip_info['dns'], 'dnsname' => $this->resolveDns($fdb_entry->ipv4Addresses),
'first_seen' => 'unknown', 'first_seen' => 'unknown',
'last_seen' => 'unknown', 'last_seen' => 'unknown',
]; ];
@@ -198,9 +197,9 @@ class FdbTablesController extends TableController
/** /**
* @param string $ip * @param string $ip
* @return \Illuminate\Support\Collection * @return Collection
*/ */
protected function findMacs($ip): \Illuminate\Support\Collection protected function findMacs($ip): Collection
{ {
$port_id = \Request::get('port_id'); $port_id = \Request::get('port_id');
$device_id = \Request::get('device_id'); $device_id = \Request::get('device_id');
@@ -217,9 +216,9 @@ class FdbTablesController extends TableController
/** /**
* @param string $vlan * @param string $vlan
* @return \Illuminate\Support\Collection * @return Collection
*/ */
protected function findVlans($vlan): \Illuminate\Support\Collection protected function findVlans($vlan): Collection
{ {
$port_id = \Request::get('port_id'); $port_id = \Request::get('port_id');
$device_id = \Request::get('device_id'); $device_id = \Request::get('device_id');
@@ -238,9 +237,9 @@ class FdbTablesController extends TableController
/** /**
* @param string $ifAlias * @param string $ifAlias
* @return \Illuminate\Support\Collection * @return Collection
*/ */
protected function findPorts($ifAlias): \Illuminate\Support\Collection protected function findPorts($ifAlias): Collection
{ {
$port_id = \Request::get('port_id'); $port_id = \Request::get('port_id');
$device_id = \Request::get('device_id'); $device_id = \Request::get('device_id');
@@ -255,38 +254,22 @@ class FdbTablesController extends TableController
->pluck('port_id'); ->pluck('port_id');
} }
/** private function resolveDns(Collection $ips): string
* @param string $mac_address
* @return array
*/
protected function findIps($mac_address): array
{ {
if (! isset($this->ipCache[$mac_address])) { $dns = 'N/A';
$ips = Ipv4Mac::where('mac_address', $mac_address)
->groupBy('ipv4_address')
->pluck('ipv4_address');
$dns = 'N/A'; // only fetch DNS if the column is visible
if (\Request::get('dns') == 'true') {
// only fetch DNS if the column is visible // don't try too many dns queries, this is the slowest part
if (\Request::get('dns') == 'true') { foreach ($ips->take(3) as $ip) {
// don't try too many dns queries, this is the slowest part $hostname = gethostbyaddr($ip);
foreach ($ips->take(3) as $ip) { if (! IP::isValid($hostname)) {
$hostname = gethostbyaddr($ip); return $hostname;
if (! IP::isValid($hostname)) {
$dns = $hostname;
break;
}
} }
} }
$this->ipCache[$mac_address] = [
'ips' => $ips,
'dns' => $dns,
];
} }
return $this->ipCache[$mac_address]; return $dns;
} }
/** /**
@@ -308,31 +291,24 @@ class FdbTablesController extends TableController
* @param string $vendor * @param string $vendor
* @return array * @return array
*/ */
protected function ouisFromVendor($vendor) protected function ouisFromVendor(string $vendor): array
{ {
$matching_ouis = DB::table('vendor_ouis') return DB::table('vendor_ouis')
->where('vendor', 'LIKE', '%' . $vendor . '%') ->where('vendor', 'LIKE', '%' . $vendor . '%')
->pluck('oui') ->pluck('oui')
->toArray(); ->toArray();
return $matching_ouis;
} }
/** /**
* Get all port ids from vendor OUIs * Get all port ids from vendor OUIs
*
* @param array $vendor_ouis
* @return Builder
*/ */
protected function findPortsByOui($vendor_ouis, $query) protected function findPortsByOui(array $vendor_ouis, Builder $query): Builder
{ {
$condition = ''; $query->where(function (Builder $query) use ($vendor_ouis) {
foreach ($vendor_ouis as $oui) { foreach ($vendor_ouis as $oui) {
$clean_oui = str_replace(':', '', $oui); $query->orWhere('ports_fdb.mac_address', 'LIKE', "$oui%");
$condition .= " ports_fdb.mac_address LIKE '$clean_oui%' OR"; }
} });
$condition = rtrim($condition, ' OR');
$query->whereRaw($condition);
return $query; // Return the query builder instance return $query; // Return the query builder instance
} }

View File

@@ -3,6 +3,7 @@
namespace App\Models; namespace App\Models;
use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
class PortsFdb extends PortRelatedModel class PortsFdb extends PortRelatedModel
{ {
@@ -21,4 +22,9 @@ class PortsFdb extends PortRelatedModel
{ {
return $this->belongsTo(\App\Models\Vlan::class, 'vlan_id', 'vlan_id'); return $this->belongsTo(\App\Models\Vlan::class, 'vlan_id', 'vlan_id');
} }
public function ipv4Addresses(): HasMany
{
return $this->hasMany(\App\Models\Ipv4Mac::class, 'mac_address', 'mac_address');
}
} }