Ports UI update (#16115)

* WIP Device Ports porting to Laravel

* WIP port links

* Port Links WIP

* Port Links

* in_array -> isset

* Add request to DeviceTab data

* Add initial Pagination

* Missing select component

* Collapsed and expandable port neighbors
New expandable component

* Port sorting

* Fix port transfer

* Use menu entries to filter ports

* Add translatable strings

* style fixes and cleanup

* update css

* graph views and tidy controller
basic port link view

* cleanup

* port row blade to reuse in legacy port view

* Legacy tab url handling
work properly in subdirectory
remove includes from sub tab directory to prevent oddity

* fallback to detail list when the view doesn't exist

* Use named variable to simplify

* Fix issue from file that was a symlink

* Submenu handle sub items and query string urls

* extract pageLinks to improve readability

* fix typo

* Apply fixes from StyleCI

* phpstan was not happy using the relationship HasMany query

* Don't allow *bps etc to be on a second line

* Improve table on small screens

* Fix sort

---------

Co-authored-by: Tony Murray <murrant@users.noreply.github.com>
This commit is contained in:
Tony Murray
2024-06-16 11:29:06 -05:00
committed by GitHub
parent a717e084f0
commit 075ba4c932
86 changed files with 1135 additions and 990 deletions

View File

@@ -26,6 +26,7 @@
namespace App\Http\Controllers\Device\Tabs;
use App\Models\Device;
use Illuminate\Http\Request;
use LibreNMS\Interfaces\UI\DeviceTab;
class AccessPointsController implements DeviceTab
@@ -50,7 +51,7 @@ class AccessPointsController implements DeviceTab
return __('Access Points');
}
public function data(Device $device): array
public function data(Device $device, Request $request): array
{
return [];
}

View File

@@ -26,6 +26,7 @@
namespace App\Http\Controllers\Device\Tabs;
use App\Models\Device;
use Illuminate\Http\Request;
use LibreNMS\Interfaces\UI\DeviceTab;
class AlertStatsController implements DeviceTab
@@ -50,7 +51,7 @@ class AlertStatsController implements DeviceTab
return __('Alert Stats');
}
public function data(Device $device): array
public function data(Device $device, Request $request): array
{
return [];
}

View File

@@ -26,6 +26,7 @@
namespace App\Http\Controllers\Device\Tabs;
use App\Models\Device;
use Illuminate\Http\Request;
use LibreNMS\Interfaces\UI\DeviceTab;
class AlertsController implements DeviceTab
@@ -50,7 +51,7 @@ class AlertsController implements DeviceTab
return __('Alerts');
}
public function data(Device $device): array
public function data(Device $device, Request $request): array
{
return [];
}

View File

@@ -26,6 +26,7 @@
namespace App\Http\Controllers\Device\Tabs;
use App\Models\Device;
use Illuminate\Http\Request;
use LibreNMS\Interfaces\UI\DeviceTab;
class AppsController implements DeviceTab
@@ -50,7 +51,7 @@ class AppsController implements DeviceTab
return __('Apps');
}
public function data(Device $device): array
public function data(Device $device, Request $request): array
{
return [];
}

View File

@@ -26,6 +26,7 @@
namespace App\Http\Controllers\Device\Tabs;
use App\Models\Device;
use Illuminate\Http\Request;
class CaptureController implements \LibreNMS\Interfaces\UI\DeviceTab
{
@@ -49,7 +50,7 @@ class CaptureController implements \LibreNMS\Interfaces\UI\DeviceTab
return __('Capture');
}
public function data(Device $device): array
public function data(Device $device, Request $request): array
{
return [];
}

View File

@@ -26,6 +26,7 @@
namespace App\Http\Controllers\Device\Tabs;
use App\Models\Device;
use Illuminate\Http\Request;
use LibreNMS\Config;
use LibreNMS\Interfaces\UI\DeviceTab;
@@ -51,7 +52,7 @@ class CollectdController implements DeviceTab
return __('CollectD');
}
public function data(Device $device): array
public function data(Device $device, Request $request): array
{
return [];
}

View File

@@ -26,6 +26,7 @@
namespace App\Http\Controllers\Device\Tabs;
use App\Models\Device;
use Illuminate\Http\Request;
class EditController implements \LibreNMS\Interfaces\UI\DeviceTab
{
@@ -49,7 +50,7 @@ class EditController implements \LibreNMS\Interfaces\UI\DeviceTab
return __('Edit');
}
public function data(Device $device): array
public function data(Device $device, Request $request): array
{
return [];
}

View File

@@ -26,6 +26,7 @@
namespace App\Http\Controllers\Device\Tabs;
use App\Models\Device;
use Illuminate\Http\Request;
use LibreNMS\Interfaces\UI\DeviceTab;
class GraphsController implements DeviceTab
@@ -50,7 +51,7 @@ class GraphsController implements DeviceTab
return __('Graphs');
}
public function data(Device $device): array
public function data(Device $device, Request $request): array
{
return [];
}

View File

@@ -26,6 +26,7 @@
namespace App\Http\Controllers\Device\Tabs;
use App\Models\Device;
use Illuminate\Http\Request;
use LibreNMS\Interfaces\UI\DeviceTab;
class HealthController implements DeviceTab
@@ -50,7 +51,7 @@ class HealthController implements DeviceTab
return __('Health');
}
public function data(Device $device): array
public function data(Device $device, Request $request): array
{
return [];
}

View File

@@ -27,6 +27,7 @@ namespace App\Http\Controllers\Device\Tabs;
use App\Facades\DeviceCache;
use App\Models\Device;
use Illuminate\Http\Request;
use LibreNMS\Config;
use LibreNMS\Interfaces\UI\DeviceTab;
@@ -67,7 +68,7 @@ class InventoryController implements DeviceTab
return __('Inventory');
}
public function data(Device $device): array
public function data(Device $device, Request $request): array
{
return [
'tab' => $this->type, // inject to load correct legacy file

View File

@@ -27,10 +27,10 @@ namespace App\Http\Controllers\Device\Tabs;
use App\Models\Device;
use Carbon\Carbon;
use Illuminate\Http\Request;
use LibreNMS\Config;
use LibreNMS\Interfaces\UI\DeviceTab;
use LibreNMS\Util\Smokeping;
use Request;
class LatencyController implements DeviceTab
{
@@ -54,10 +54,10 @@ class LatencyController implements DeviceTab
return __('Latency');
}
public function data(Device $device): array
public function data(Device $device, Request $request): array
{
$from = Request::get('dtpickerfrom', Carbon::now(session('preferences.timezone'))->subDays(2)->format(Config::get('dateformat.byminute')));
$to = Request::get('dtpickerto', Carbon::now(session('preferences.timezone'))->format(Config::get('dateformat.byminute')));
$from = $request->get('dtpickerfrom', Carbon::now(session('preferences.timezone'))->subDays(2)->format(Config::get('dateformat.byminute')));
$to = $request->get('dtpickerto', Carbon::now(session('preferences.timezone'))->format(Config::get('dateformat.byminute')));
$smokeping = new Smokeping($device);
$smokeping_tabs = [];

View File

@@ -27,6 +27,7 @@ namespace App\Http\Controllers\Device\Tabs;
use App\Facades\DeviceCache;
use App\Models\Device;
use Illuminate\Http\Request;
use LibreNMS\Interfaces\UI\DeviceTab;
class LoadBalancerController implements DeviceTab
@@ -93,7 +94,7 @@ class LoadBalancerController implements DeviceTab
return __('Load Balancer');
}
public function data(Device $device): array
public function data(Device $device, Request $request): array
{
return [
'loadbalancer_tabs' => $this->tabs,

View File

@@ -26,6 +26,7 @@
namespace App\Http\Controllers\Device\Tabs;
use App\Models\Device;
use Illuminate\Http\Request;
use LibreNMS\Interfaces\UI\DeviceTab;
class LogsController implements DeviceTab
@@ -50,7 +51,7 @@ class LogsController implements DeviceTab
return __('Logs');
}
public function data(Device $device): array
public function data(Device $device, Request $request): array
{
return [];
}

View File

@@ -26,6 +26,7 @@
namespace App\Http\Controllers\Device\Tabs;
use App\Models\Device;
use Illuminate\Http\Request;
use LibreNMS\Interfaces\UI\DeviceTab;
class MefController implements DeviceTab
@@ -50,7 +51,7 @@ class MefController implements DeviceTab
return __('Metro Ethernet');
}
public function data(Device $device): array
public function data(Device $device, Request $request): array
{
return [];
}

View File

@@ -26,6 +26,7 @@
namespace App\Http\Controllers\Device\Tabs;
use App\Models\Device;
use Illuminate\Http\Request;
use LibreNMS\Interfaces\UI\DeviceTab;
class MuninController implements DeviceTab
@@ -50,7 +51,7 @@ class MuninController implements DeviceTab
return __('Munin');
}
public function data(Device $device): array
public function data(Device $device, Request $request): array
{
return [];
}

View File

@@ -26,6 +26,7 @@
namespace App\Http\Controllers\Device\Tabs;
use App\Models\Device;
use Illuminate\Http\Request;
use LibreNMS\Interfaces\UI\DeviceTab;
class NacController implements DeviceTab
@@ -50,7 +51,7 @@ class NacController implements DeviceTab
return __('NAC');
}
public function data(Device $device): array
public function data(Device $device, Request $request): array
{
return [];
}

View File

@@ -27,6 +27,7 @@ namespace App\Http\Controllers\Device\Tabs;
use App\Models\Device;
use App\Models\Link;
use Illuminate\Http\Request;
use LibreNMS\Interfaces\UI\DeviceTab;
class NeighboursController implements DeviceTab
@@ -53,7 +54,7 @@ class NeighboursController implements DeviceTab
return __('Neighbours');
}
public function data(Device $device): array
public function data(Device $device, Request $request): array
{
return [];
}

View File

@@ -26,6 +26,7 @@
namespace App\Http\Controllers\Device\Tabs;
use App\Models\Device;
use Illuminate\Http\Request;
use LibreNMS\Config;
use LibreNMS\Interfaces\UI\DeviceTab;
@@ -72,7 +73,7 @@ class NetflowController implements DeviceTab
return __('Netflow');
}
public function data(Device $device): array
public function data(Device $device, Request $request): array
{
return [
'tab' => 'nfsen',

View File

@@ -54,7 +54,7 @@ class NotesController implements DeviceTab
return __('Notes');
}
public function data(Device $device): array
public function data(Device $device, Request $request): array
{
return [];
}

View File

@@ -26,6 +26,7 @@
namespace App\Http\Controllers\Device\Tabs;
use App\Models\Device;
use Illuminate\Http\Request;
use LibreNMS\Interfaces\UI\DeviceTab;
use Session;
@@ -51,7 +52,7 @@ class OverviewController implements DeviceTab
return __('Overview');
}
public function data(Device $device): array
public function data(Device $device, Request $request): array
{
return [];
}

View File

@@ -26,6 +26,7 @@
namespace App\Http\Controllers\Device\Tabs;
use App\Models\Device;
use Illuminate\Http\Request;
use LibreNMS\Interfaces\UI\DeviceTab;
class PackagesController implements DeviceTab
@@ -50,7 +51,7 @@ class PackagesController implements DeviceTab
return __('Pkgs');
}
public function data(Device $device): array
public function data(Device $device, Request $request): array
{
return [];
}

View File

@@ -26,6 +26,7 @@
namespace App\Http\Controllers\Device\Tabs;
use App\Models\Device;
use Illuminate\Http\Request;
use LibreNMS\Interfaces\UI\DeviceTab;
class PortController implements DeviceTab
@@ -50,7 +51,7 @@ class PortController implements DeviceTab
return __('Port');
}
public function data(Device $device): array
public function data(Device $device, Request $request): array
{
return [];
}

View File

@@ -26,10 +26,25 @@
namespace App\Http\Controllers\Device\Tabs;
use App\Models\Device;
use App\Models\Link;
use App\Models\Port;
use App\Models\Pseudowire;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Http\Request;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Validator;
use LibreNMS\Config;
use LibreNMS\Interfaces\UI\DeviceTab;
class PortsController implements DeviceTab
{
private bool $detail = false;
private int $perPage = 15;
private string $sortOrder = 'asc';
private string $sortColumn = 'default';
public function visible(Device $device): bool
{
return $device->ports()->exists();
@@ -50,8 +65,340 @@ class PortsController implements DeviceTab
return __('Ports');
}
public function data(Device $device): array
public function data(Device $device, Request $request): array
{
return [];
Validator::validate($request->all(), [
'perPage' => 'int',
'sort' => 'in:media,mac,port,traffic,speed',
'order' => 'in:asc,desc',
'disabled' => 'in:0,1',
'ignore' => 'in:0,1',
'admin' => 'in:up,down,testing,any',
'status' => 'in:up,down,testing,unknown,dormant,notPresent,lowerLayerDown,any',
'type' => 'in:bits,upkts,nupkts,errors,etherlike',
'from' => ['regex:/^(int|[+-]\d+[hdmy])$/'],
'to' => ['regex:/^(int|[+-]\d+[hdmy])$/'],
]);
$tab = $this->parseTab($request);
$this->detail = $tab == 'detail';
$data = match ($tab) {
'links' => $this->linksData($device),
'xdsl' => $this->xdslData($device),
'graphs', 'mini_graphs' => $this->graphData($device, $request),
default => $this->portData($device, $request),
};
return array_merge([
'tab' => $tab,
'details' => $this->detail,
'submenu' => [
$this->getTabs($device),
__('Graphs') => $this->getGraphLinks(),
],
'page_links' => $this->pageLinks($request),
'perPage' => $this->perPage,
'sort' => $this->sortColumn,
'next_order' => $this->sortOrder == 'asc' ? 'desc' : 'asc',
], $data);
}
private function portData(Device $device, Request $request): array
{
$relationships = ['groups', 'ipv4', 'ipv6', 'vlans', 'adsl', 'vdsl'];
if ($this->detail) {
$relationships[] = 'links';
$relationships[] = 'pseudowires.endpoints';
$relationships[] = 'ipv4Networks.ipv4';
$relationships[] = 'ipv6Networks.ipv6';
}
/** @var Collection|LengthAwarePaginator<Port> $ports */
$ports = $this->getFilteredPortsQuery($device, $request, $relationships)->paginate($this->perPage);
$data = [
'ports' => $ports,
'neighbors' => $ports->keyBy('port_id')->map(fn (Port $port) => $this->findPortNeighbors($port)),
'graphs' => [
'bits' => [['type' => 'port_bits', 'title' => trans('Traffic'), 'vars' => [['from' => '-1d'], ['from' => '-7d'], ['from' => '-30d'], ['from' => '-1y']]]],
'upkts' => [['type' => 'port_upkts', 'title' => trans('Packets (Unicast)'), 'vars' => [['from' => '-1d'], ['from' => '-7d'], ['from' => '-30d'], ['from' => '-1y']]]],
'errors' => [['type' => 'port_errors', 'title' => trans('Errors'), 'vars' => [['from' => '-1d'], ['from' => '-7d'], ['from' => '-30d'], ['from' => '-1y']]]],
],
];
if ($this->detail) {
$data['neighbor_ports'] = Port::with('device')
->hasAccess(Auth::user())
->whereIn('port_id', $data['neighbors']->map(fn ($a) => array_keys($a))->flatten())
->get()->keyBy('port_id');
}
return $data;
}
public function findPortNeighbors(Port $port): array
{
// only do for detail
if (! $this->detail) {
return [];
}
// skip ports that cannot have neighbors
if (in_array($port->ifType, ['softwareLoopback', 'rs232'])) {
return [];
}
$neighbors = [];
// Links always included
// fa-plus black portlink on devicelink
foreach ($port->links as $link) {
/** @var Link $link */
if ($link->remote_port_id) {
$this->addPortNeighbor($neighbors, 'link', $link->remote_port_id);
}
}
if ($this->detail) {
// IPv4 + IPv6 subnet if detailed
// fa-arrow-right green portlink on devicelink
if ($port->ipv4Networks->isNotEmpty()) {
$ids = $port->ipv4Networks->map(fn ($net) => $net->ipv4->pluck('port_id'))->flatten();
foreach ($ids as $port_id) {
if ($port_id !== $port->port_id) {
$this->addPortNeighbor($neighbors, 'ipv4_network', $port_id);
}
}
}
if ($port->ipv6Networks->isNotEmpty()) {
$ids = $port->ipv6Networks->map(fn ($net) => $net->ipv6->pluck('port_id'))->flatten();
foreach ($ids as $port_id) {
if ($port_id !== $port->port_id) {
$this->addPortNeighbor($neighbors, 'ipv6_network', $port_id);
}
}
}
}
// pseudowires
// fa-cube green portlink on devicelink: cpwVcID
/** @var Pseudowire $pseudowire */
foreach ($port->pseudowires as $pseudowire) {
foreach ($pseudowire->endpoints as $endpoint) {
if ($endpoint->port_id != $port->port_id) {
$this->addPortNeighbor($neighbors, 'pseudowire', $endpoint->port_id);
}
}
}
// port stack
// fa-expand portlink: local is low port
// fa-compress portlink: local is high portPort
$stacks = \DB::table('ports_stack')->where('device_id', $port->device_id)
->where(fn ($q) => $q->where('port_id_high', $port->port_id)->orWhere('port_id_low', $port->port_id))->get();
foreach ($stacks as $stack) {
if ($stack->port_id_low) {
$this->addPortNeighbor($neighbors, 'stack_low', $stack->port_id_low);
}
if ($stack->port_id_high) {
$this->addPortNeighbor($neighbors, 'stack_high', $stack->port_id_high);
}
}
// PAGP members/parent
// fa-cube portlink: pagpGroupIfIndex = ifIndex parent
// fa-cube portlink: if (not parent, pagpGroupIfIndex != ifIndex) ifIndex = pagpGroupIfIndex member
if ($port->pagpGroupIfIndex) {
if ($port->pagpGroupIfIndex == $port->ifIndex) {
$this->addPortNeighbor($neighbors, 'pagp', $port->port_id);
} else {
$this->addPortNeighbor($neighbors, 'pagp', $port->pagpParent->port_id);
}
}
return $neighbors;
}
private function addPortNeighbor(array &$neighbors, string $type, int $port_id): void
{
if (empty($neighbors[$port_id])) {
$neighbors[$port_id] = [
'port_id' => $port_id,
];
}
$neighbors[$port_id][$type] = 1;
}
private function graphData(Device $device, Request $request): array
{
return [
'graph_type' => 'port_' . $request->get('type'),
'ports' => $this->getFilteredPortsQuery($device, $request)->get(),
];
}
private function xdslData(Device $device): array
{
$device->portsAdsl->load('port');
$device->portsVdsl->load('port');
return [
'adsl' => $device->portsAdsl->sortBy('port.ifIndex'),
'vdsl' => $device->portsVdsl->sortBy('port.ifIndex'),
];
}
private function linksData(Device $device): array
{
$device->links->load(['port', 'remotePort', 'remoteDevice']);
return ['links' => $device->links];
}
private function getTabs(Device $device): array
{
$tabs = [
['name' => __('Basic'), 'url' => 'basic'],
['name' => __('Detail'), 'url' => ''],
];
if ($device->macs()->exists()) {
$tabs[] = ['name' => __('port.tabs.arp'), 'url' => 'arp'];
}
if ($device->portsFdb()->exists()) {
$tabs[] = ['name' => __('port.tabs.fdb'), 'url' => 'fdb'];
}
if ($device->links()->exists()) {
$tabs[] = ['name' => __('port.tabs.links'), 'url' => 'links'];
}
if ($device->portsAdsl()->exists() || $device->portsVdsl()->exists()) {
$tabs[] = ['name' => __('port.tabs.xdsl'), 'url' => 'xdsl'];
}
return $tabs;
}
/**
* @return array[]
*/
private function getGraphLinks(): array
{
$graph_links = [
[
'name' => __('port.graphs.bits'),
'url' => 'graphs?type=bits',
'sub_name' => __('Mini'),
'sub_url' => 'mini_graphs?type=bits',
],
[
'name' => __('port.graphs.upkts'),
'url' => 'graphs?type=upkts',
'sub_name' => __('Mini'),
'sub_url' => 'mini_graphs?type=upkts',
],
[
'name' => __('port.graphs.nupkts'),
'url' => 'graphs?type=nupkts',
'sub_name' => __('Mini'),
'sub_url' => 'mini_graphs?type=nupkts',
],
[
'name' => __('port.graphs.errors'),
'url' => 'graphs?type=errors',
'sub_name' => __('Mini'),
'sub_url' => 'mini_graphs?type=errors',
],
];
if (Config::get('enable_ports_etherlike')) {
$graph_links[] = [
'name' => __('port.graphs.etherlike'),
'url' => 'graphs?type=etherlike',
'sub_name' => __('Mini'),
'sub_url' => 'mini_graphs?type=etherlike',
];
}
return $graph_links;
}
private function getFilteredPortsQuery(Device $device, Request $request, array $relationships = []): Builder
{
$this->perPage = $request->input('perPage', 15);
$this->sortOrder = $request->input('order', 'asc');
$this->sortColumn = $request->input('sort', 'default');
$orderBy = match ($this->sortColumn) {
'traffic' => \DB::raw('ports.ifInOctets_rate + ports.ifOutOctets_rate'),
'speed' => 'ifSpeed',
'media' => 'ifType',
'mac' => 'ifPhysAddress',
'port' => 'ifName',
default => 'ifIndex',
};
return Port::where('device_id', $device->device_id)
->isNotDeleted()
->hasAccess(Auth::user())->with($relationships)
->when(! $request->input('disabled'), fn (Builder $q, $disabled) => $q->where('disabled', 0))
->when(! $request->input('ignore'), fn (Builder $q, $disabled) => $q->where('ignore', 0))
->when($request->input('admin') != 'any', fn (Builder $q, $admin) => $q->where('ifAdminStatus', $request->input('admin', 'up')))
->when($request->input('status', 'any') != 'any', fn (Builder $q, $admin) => $q->where('ifOperStatus', $request->input('status')))
->orderBy($orderBy, $this->sortOrder);
}
/**
* get the ports sub tab name including handling legacy urls
*/
private function parseTab(Request $request): string
{
if (preg_match('#view=([^/]+)#', $request->fullUrl(), $matches)) {
return match ($matches[1]) {
'neighbours' => 'links',
default => $matches[1],
};
}
return $request->route('vars', 'detail'); // fourth segment is called vars to handle legacy urls
}
private function pageLinks(Request $request): array
{
$disabled = $request->input('disabled');
$ignore = $request->input('ignore');
$admin = $request->input('admin') == 'any';
$status = $request->input('status') == 'up';
return [
[
'icon' => $status ? 'fa-regular fa-square-check' : 'fa-regular fa-square',
'url' => $status ? $request->fullUrlWithoutQuery('status') : $request->fullUrlWithQuery(['status' => 'up']),
'title' => __('port.filters.status_up'),
'external' => false,
],
[
'icon' => $admin ? 'fa-regular fa-square-check' : 'fa-regular fa-square',
'url' => $admin ? $request->fullUrlWithoutQuery('admin') : $request->fullUrlWithQuery(['admin' => 'any']),
'title' => __('port.filters.admin_down'),
'external' => false,
],
[
'icon' => $disabled ? 'fa-regular fa-square-check' : 'fa-regular fa-square',
'url' => $disabled ? $request->fullUrlWithoutQuery('disabled') : $request->fullUrlWithQuery(['disabled' => 1]),
'title' => __('port.filters.disabled'),
'external' => false,
],
[
'icon' => $ignore ? 'fa-regular fa-square-check' : 'fa-regular fa-square',
'url' => $ignore ? $request->fullUrlWithoutQuery('ignore') : $request->fullUrlWithQuery(['ignore' => 1]),
'title' => __('port.filters.ignored'),
'external' => false,
],
];
}
}

View File

@@ -26,6 +26,7 @@
namespace App\Http\Controllers\Device\Tabs;
use App\Models\Device;
use Illuminate\Http\Request;
use LibreNMS\Interfaces\UI\DeviceTab;
class PrinterController implements DeviceTab
@@ -50,7 +51,7 @@ class PrinterController implements DeviceTab
return __('Printer');
}
public function data(Device $device): array
public function data(Device $device, Request $request): array
{
return [
'tab' => 'toner',

View File

@@ -26,6 +26,7 @@
namespace App\Http\Controllers\Device\Tabs;
use App\Models\Device;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use LibreNMS\Interfaces\UI\DeviceTab;
@@ -51,7 +52,7 @@ class ProcessesController implements DeviceTab
return __('Processes');
}
public function data(Device $device): array
public function data(Device $device, Request $request): array
{
return [];
}

View File

@@ -26,6 +26,7 @@
namespace App\Http\Controllers\Device\Tabs;
use App\Models\Device;
use Illuminate\Http\Request;
use LibreNMS\Interfaces\UI\DeviceTab;
class PseudowiresController implements DeviceTab
@@ -50,7 +51,7 @@ class PseudowiresController implements DeviceTab
return __('Pseudowires');
}
public function data(Device $device): array
public function data(Device $device, Request $request): array
{
return [];
}

View File

@@ -28,6 +28,7 @@ namespace App\Http\Controllers\Device\Tabs;
use App\Facades\DeviceCache;
use App\Models\Component;
use App\Models\Device;
use Illuminate\Http\Request;
use LibreNMS\Interfaces\UI\DeviceTab;
class RoutingController implements DeviceTab
@@ -72,7 +73,7 @@ class RoutingController implements DeviceTab
return __('Routing');
}
public function data(Device $device): array
public function data(Device $device, Request $request): array
{
return [
'routing_tabs' => array_filter($this->tabs),

View File

@@ -26,6 +26,7 @@
namespace App\Http\Controllers\Device\Tabs;
use App\Models\Device;
use Illuminate\Http\Request;
use LibreNMS\Interfaces\UI\DeviceTab;
class ServicesController implements DeviceTab
@@ -50,7 +51,7 @@ class ServicesController implements DeviceTab
return __('Services');
}
public function data(Device $device): array
public function data(Device $device, Request $request): array
{
return [];
}

View File

@@ -28,6 +28,7 @@ namespace App\Http\Controllers\Device\Tabs;
use App\Facades\DeviceCache;
use App\Http\Controllers\Controller;
use App\Models\Device;
use Illuminate\Http\Request;
use LibreNMS\Config;
use LibreNMS\Interfaces\UI\DeviceTab;
@@ -60,7 +61,7 @@ class ShowConfigController extends Controller implements DeviceTab
return __('Config');
}
public function data(Device $device): array
public function data(Device $device, Request $request): array
{
return [
'rancid_path' => $this->getRancidPath(),

View File

@@ -26,6 +26,7 @@
namespace App\Http\Controllers\Device\Tabs;
use App\Models\Device;
use Illuminate\Http\Request;
use LibreNMS\Interfaces\UI\DeviceTab;
class SlasController implements DeviceTab
@@ -50,7 +51,7 @@ class SlasController implements DeviceTab
return __('SLAs');
}
public function data(Device $device): array
public function data(Device $device, Request $request): array
{
return [];
}

View File

@@ -26,6 +26,7 @@
namespace App\Http\Controllers\Device\Tabs;
use App\Models\Device;
use Illuminate\Http\Request;
use LibreNMS\Interfaces\UI\DeviceTab;
use LibreNMS\Util\Url;
@@ -51,7 +52,7 @@ class StpController implements DeviceTab
return __('STP');
}
public function data(Device $device): array
public function data(Device $device, Request $request): array
{
$active_vlan = Url::parseOptions('vlan', 1);
$stpInstances = $device->stpInstances;

View File

@@ -27,6 +27,7 @@ namespace App\Http\Controllers\Device\Tabs;
use App\Models\Device;
use App\Models\TnmsneInfo;
use Illuminate\Http\Request;
use LibreNMS\Interfaces\UI\DeviceTab;
class TnmsneController implements DeviceTab
@@ -51,7 +52,7 @@ class TnmsneController implements DeviceTab
return __('Hardware');
}
public function data(Device $device): array
public function data(Device $device, Request $request): array
{
return [];
}

View File

@@ -27,6 +27,7 @@ namespace App\Http\Controllers\Device\Tabs;
use App\Models\Device;
use App\Models\PortVlan;
use Illuminate\Http\Request;
use LibreNMS\Interfaces\UI\DeviceTab;
class VlansController implements DeviceTab
@@ -51,7 +52,7 @@ class VlansController implements DeviceTab
return __('VLANs');
}
public function data(Device $device): array
public function data(Device $device, Request $request): array
{
return [
'vlans' => self::getVlans($device),

View File

@@ -26,6 +26,7 @@
namespace App\Http\Controllers\Device\Tabs;
use App\Models\Device;
use Illuminate\Http\Request;
use LibreNMS\Interfaces\UI\DeviceTab;
class VmInfoController implements DeviceTab
@@ -50,7 +51,7 @@ class VmInfoController implements DeviceTab
return __('Virtual Machines');
}
public function data(Device $device): array
public function data(Device $device, Request $request): array
{
return [
'vms' => self::getVms($device),

View File

@@ -26,6 +26,7 @@
namespace App\Http\Controllers\Device\Tabs;
use App\Models\Device;
use Illuminate\Http\Request;
use LibreNMS\Interfaces\UI\DeviceTab;
class WirelessController implements DeviceTab
@@ -50,7 +51,7 @@ class WirelessController implements DeviceTab
return __('Wireless');
}
public function data(Device $device): array
public function data(Device $device, Request $request): array
{
return [];
}

View File

@@ -12,6 +12,7 @@ use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Blade;
use LibreNMS\Config;
use LibreNMS\Interfaces\UI\DeviceTab;
use LibreNMS\Util\Debug;
use LibreNMS\Util\Graph;
use LibreNMS\Util\Url;
@@ -83,11 +84,14 @@ class DeviceController extends Controller
$parent_id = Vminfo::guessFromDevice($device)->value('device_id');
$overview_graphs = $this->buildDeviceGraphArrays($device);
/** @var DeviceTab[] $tabs */
$tabs = array_map(function ($class) {
return app()->make($class);
}, array_filter($this->tabs, 'class_exists')); // TODO remove filter
$title = $tabs[$current_tab]->name();
$data = $tabs[$current_tab]->data($device);
$tab_controller = $tabs[$current_tab];
$title = $tab_controller->name();
$data = $tab_controller->data($device, $request);
$page_links = $data['page_links'] ?? [];
// Device Link Menu, select the primary link
$device_links = $this->deviceLinkMenu($device, $current_tab);

View File

@@ -26,6 +26,7 @@
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class Ipv4Address extends PortRelatedModel
{
@@ -40,4 +41,9 @@ class Ipv4Address extends PortRelatedModel
'port_id',
'context_name',
];
public function network(): BelongsTo
{
return $this->belongsTo(Ipv4Network::class, 'ipv4_network_id', 'ipv4_network_id');
}
}

View File

@@ -28,6 +28,7 @@ namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\HasManyThrough;
class Ipv4Network extends Model
{
@@ -45,4 +46,9 @@ class Ipv4Network extends Model
{
return $this->hasMany(Ipv4Address::class, 'ipv4_network_id');
}
public function connectedPorts(): HasManyThrough
{
return $this->hasManyThrough(Port::class, Ipv4Address::class, 'ipv4_network_id', 'port_id', 'ipv4_network_id', 'port_id');
}
}

View File

@@ -26,6 +26,8 @@
namespace App\Models;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class Ipv6Address extends PortRelatedModel
{
public $timestamps = false;
@@ -39,4 +41,9 @@ class Ipv6Address extends PortRelatedModel
'port_id',
'context_name',
];
public function network(): BelongsTo
{
return $this->belongsTo(Ipv6Network::class, 'ipv6_network_id', 'ipv6_network_id');
}
}

View File

@@ -28,6 +28,7 @@ namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\HasManyThrough;
class Ipv6Network extends Model
{
@@ -44,4 +45,9 @@ class Ipv6Network extends Model
{
return $this->hasMany(\App\Models\Ipv6Address::class, 'ipv6_network_id');
}
public function connectedPorts(): HasManyThrough
{
return $this->hasManyThrough(Port::class, Ipv6Address::class, 'ipv6_network_id', 'port_id', 'ipv6_network_id', 'port_id');
}
}

View File

@@ -4,8 +4,10 @@ namespace App\Models;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Factories\HasFactory;
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\MorphMany;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
@@ -276,14 +278,14 @@ class Port extends DeviceRelatedModel
// ---- Define Relationships ----
public function adsl(): HasMany
public function adsl(): \Illuminate\Database\Eloquent\Relations\HasOne
{
return $this->hasMany(PortAdsl::class, 'port_id');
return $this->hasOne(PortAdsl::class, 'port_id');
}
public function vdsl(): HasMany
public function vdsl(): \Illuminate\Database\Eloquent\Relations\HasOne
{
return $this->hasMany(PortVdsl::class, 'port_id');
return $this->hasOne(PortVdsl::class, 'port_id');
}
public function events(): MorphMany
@@ -303,12 +305,22 @@ class Port extends DeviceRelatedModel
public function ipv4(): HasMany
{
return $this->hasMany(\App\Models\Ipv4Address::class, 'port_id');
return $this->hasMany(Ipv4Address::class, 'port_id');
}
public function ipv4Networks(): HasManyThrough
{
return $this->hasManyThrough(Ipv4Network::class, Ipv4Address::class, 'port_id', 'ipv4_network_id', 'port_id', 'ipv4_network_id');
}
public function ipv6(): HasMany
{
return $this->hasMany(\App\Models\Ipv6Address::class, 'port_id');
return $this->hasMany(Ipv6Address::class, 'port_id');
}
public function ipv6Networks(): HasManyThrough
{
return $this->hasManyThrough(Ipv6Network::class, Ipv6Address::class, 'port_id', 'ipv6_network_id', 'port_id', 'ipv6_network_id');
}
public function links(): HasMany
@@ -351,6 +363,11 @@ class Port extends DeviceRelatedModel
return $this->hasMany(OspfPort::class, 'port_id');
}
public function pagpParent(): BelongsTo
{
return $this->belongsTo(Port::class, 'pagpGroupIfIndex', 'ifIndex');
}
public function pseudowires(): HasMany
{
return $this->hasMany(Pseudowire::class, 'port_id');

View File

@@ -25,8 +25,15 @@
namespace App\Models;
use Illuminate\Database\Eloquent\Relations\HasMany;
class Pseudowire extends PortRelatedModel
{
public $timestamps = false;
protected $primaryKey = 'pseudowire_id';
public function endpoints(): HasMany
{
return $this->hasMany(Pseudowire::class, 'cpwVcId', 'cpwVcId');
}
}

View File

@@ -50,8 +50,6 @@ class AppServiceProvider extends ServiceProvider
*/
public function boot(): void
{
\Illuminate\Pagination\Paginator::useBootstrap();
$this->bootCustomBladeDirectives();
$this->bootCustomValidators();
$this->configureMorphAliases();

View File

@@ -34,14 +34,16 @@ class PortLink extends Component
* @var string
*/
public $status;
public bool $basic;
/**
* Create a new component instance.
*
* @return void
*/
public function __construct(Port $port, ?array $graphs = null)
public function __construct(Port $port, ?array $graphs = null, bool $basic = false)
{
$this->basic = $basic;
$this->port = $port;
$this->link = Url::portUrl($port);
$this->label = Rewrite::normalizeIfName($port->getLabel());
@@ -64,7 +66,9 @@ class PortLink extends Component
*/
public function render()
{
return view('components.port-link');
return $this->basic
? view('components.port-link_basic')
: view('components.port-link');
}
private function status(): string

View File

@@ -63,6 +63,20 @@ class Submenu extends Component
*/
public function isSelected($url)
{
// check for get parameters
$parsed_url = parse_url($url);
if (isset($parsed_url['query']) && $parsed_url['path'] === $this->selected) {
parse_str($parsed_url['query'], $vars);
$request = request();
foreach ($vars as $key => $value) {
if ($request->input($key) !== $value) {
return false;
}
}
return true;
}
return $url === $this->selected;
}