Files
librenms-librenms/app/Http/Controllers/DeviceController.php
Tony Murray 075ba4c932 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>
2024-06-16 11:29:06 -05:00

257 lines
11 KiB
PHP

<?php
namespace App\Http\Controllers;
use App\Facades\DeviceCache;
use App\Models\Device;
use App\Models\Vminfo;
use Carbon\Carbon;
use Gate;
use Illuminate\Http\Request;
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;
class DeviceController extends Controller
{
private $tabs = [
'overview' => \App\Http\Controllers\Device\Tabs\OverviewController::class,
'graphs' => \App\Http\Controllers\Device\Tabs\GraphsController::class,
'health' => \App\Http\Controllers\Device\Tabs\HealthController::class,
'apps' => \App\Http\Controllers\Device\Tabs\AppsController::class,
'processes' => \App\Http\Controllers\Device\Tabs\ProcessesController::class,
'collectd' => \App\Http\Controllers\Device\Tabs\CollectdController::class,
'munin' => \App\Http\Controllers\Device\Tabs\MuninController::class,
'ports' => \App\Http\Controllers\Device\Tabs\PortsController::class,
'port' => \App\Http\Controllers\Device\Tabs\PortController::class,
'slas' => \App\Http\Controllers\Device\Tabs\SlasController::class,
'wireless' => \App\Http\Controllers\Device\Tabs\WirelessController::class,
'accesspoints' => \App\Http\Controllers\Device\Tabs\AccessPointsController::class,
'vlans' => \App\Http\Controllers\Device\Tabs\VlansController::class,
'vm' => \App\Http\Controllers\Device\Tabs\VmInfoController::class,
'mef' => \App\Http\Controllers\Device\Tabs\MefController::class,
'tnmsne' => \App\Http\Controllers\Device\Tabs\TnmsneController::class,
'loadbalancer' => \App\Http\Controllers\Device\Tabs\LoadBalancerController::class,
'routing' => \App\Http\Controllers\Device\Tabs\RoutingController::class,
'pseudowires' => \App\Http\Controllers\Device\Tabs\PseudowiresController::class,
'neighbours' => \App\Http\Controllers\Device\Tabs\NeighboursController::class,
'stp' => \App\Http\Controllers\Device\Tabs\StpController::class,
'packages' => \App\Http\Controllers\Device\Tabs\PackagesController::class,
'inventory' => \App\Http\Controllers\Device\Tabs\InventoryController::class,
'services' => \App\Http\Controllers\Device\Tabs\ServicesController::class,
'printer' => \App\Http\Controllers\Device\Tabs\PrinterController::class,
'logs' => \App\Http\Controllers\Device\Tabs\LogsController::class,
'alerts' => \App\Http\Controllers\Device\Tabs\AlertsController::class,
'alert-stats' => \App\Http\Controllers\Device\Tabs\AlertStatsController::class,
'showconfig' => \App\Http\Controllers\Device\Tabs\ShowConfigController::class,
'netflow' => \App\Http\Controllers\Device\Tabs\NetflowController::class,
'latency' => \App\Http\Controllers\Device\Tabs\LatencyController::class,
'nac' => \App\Http\Controllers\Device\Tabs\NacController::class,
'notes' => \App\Http\Controllers\Device\Tabs\NotesController::class,
'edit' => \App\Http\Controllers\Device\Tabs\EditController::class,
'capture' => \App\Http\Controllers\Device\Tabs\CaptureController::class,
];
public function index(Request $request, $device, $current_tab = 'overview', $vars = '')
{
$device = str_replace('device=', '', $device);
$device = is_numeric($device) ? DeviceCache::get((int) $device) : DeviceCache::getByHostname($device);
$device_id = $device->device_id;
if (! $device->exists) {
abort(404);
}
DeviceCache::setPrimary($device_id);
$current_tab = str_replace('tab=', '', $current_tab);
$current_tab = array_key_exists($current_tab, $this->tabs) ? $current_tab : 'overview';
if ($current_tab == 'port') {
$vars = Url::parseLegacyPath($request->path());
$port = $device->ports()->findOrFail($vars->get('port'));
$this->authorize('view', $port);
} else {
$this->authorize('view', $device);
}
$alert_class = $device->disabled ? 'alert-info' : ($device->status ? '' : 'alert-danger');
$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
$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);
$primary_device_link_name = Config::get('html.device.primary_link', 'edit');
if (! isset($device_links[$primary_device_link_name])) {
$primary_device_link_name = array_key_first($device_links);
}
$primary_device_link = $device_links[$primary_device_link_name];
unset($device_links[$primary_device_link_name], $primary_device_link_name);
if (view()->exists('device.tabs.' . $current_tab)) {
return view('device.tabs.' . $current_tab, get_defined_vars());
}
$tab_content = $this->renderLegacyTab($current_tab, $device, $data);
return view('device.tabs.legacy', get_defined_vars());
}
private function renderLegacyTab($tab, Device $device, $data)
{
ob_start();
$device = $device->toArray();
$device['os_group'] = Config::get("os.{$device['os']}.group");
Debug::set(false);
chdir(base_path());
$init_modules = ['web', 'auth'];
require base_path('/includes/init.php');
$vars['device'] = $device['device_id'];
$vars['tab'] = $tab;
extract($data); // set preloaded data into variables
include "includes/html/pages/device/$tab.inc.php";
$output = ob_get_clean();
ob_end_clean();
return $output;
}
private function buildDeviceGraphArrays($device)
{
$graph_array = [
'width' => 150,
'height' => 45,
'device' => $device->device_id,
'type' => 'device_bits',
'from' => Carbon::now()->subDay()->timestamp,
'legend' => 'no',
'bg' => 'FFFFFF00',
];
$graphs = [];
foreach (Graph::getOverviewGraphsForDevice($device) as $graph) {
$graph_array['type'] = $graph['graph'];
$graph_array['popup_title'] = __($graph['text']);
$graphs[] = $graph_array;
}
return $graphs;
}
private function deviceLinkMenu(Device $device, string $current_tab): array
{
$device_links = [];
if (Gate::allows('update', $device)) {
$suffix = 'edit';
$title = __('Edit');
// check if metric has more specific edit page
if (preg_match('#health/metric=(\w+)#', \Request::path(), $matches)) {
if ($this->editTabExists($matches[1])) {
$current_tab = $matches[1];
} elseif ($this->editTabExists($matches[1] . 's')) {
$current_tab = $matches[1] . 's';
}
}
// check if edit page exists
if ($this->editTabExists($current_tab)) {
$suffix .= "/section=$current_tab";
$title .= ' ' . ucfirst($current_tab);
}
$device_links['edit'] = [
'icon' => 'fa-gear',
'url' => route('device', [$device->device_id, $suffix]),
'title' => $title,
'external' => false,
];
}
// User defined device links
foreach (array_values(Arr::wrap(Config::get('html.device.links'))) as $index => $link) {
$device_links['custom' . ($index + 1)] = [
'icon' => $link['icon'] ?? 'fa-external-link',
'url' => Blade::render($link['url'], ['device' => $device]),
'title' => $link['title'],
'external' => $link['external'] ?? true,
];
}
// Web
$http_port = $device->attribs->firstWhere('attrib_type', 'override_device_http_port') ? ':' . $device->attribs->firstWhere('attrib_type', 'override_device_http_port')->attrib_value : '';
$device_links['web'] = [
'icon' => 'fa-globe',
'url' => 'https://' . $device->hostname . $http_port,
'title' => __('Web'),
'external' => true,
'onclick' => 'http_fallback(this); return false;',
];
// IPMI
if ($device->attribs->firstWhere('attrib_type', 'ipmi_hostname')) {
$device_links['ipmi'] = [
'icon' => 'fa-microchip',
'url' => 'https://' . $device->attribs->firstWhere('attrib_type', 'ipmi_hostname')->attrib_value,
'title' => __('IPMI'),
'external' => true,
'onclick' => 'http_fallback(this); return false;',
];
}
// SSH
$ssh_port = $device->attribs->firstWhere('attrib_type', 'override_device_ssh_port') ? ':' . $device->attribs->firstWhere('attrib_type', 'override_device_ssh_port')->attrib_value : '';
$ssh_url = Config::has('gateone.server')
? Config::get('gateone.server') . '?ssh=ssh://' . (Config::get('gateone.use_librenms_user') ? Auth::user()->username . '@' : '') . $device['hostname'] . '&location=' . $device['hostname']
: 'ssh://' . $device->hostname . $ssh_port;
$device_links['ssh'] = [
'icon' => 'fa-lock',
'url' => $ssh_url,
'title' => __('SSH'),
'external' => true,
];
// Telnet
$telnet_port = $device->attribs->firstWhere('attrib_type', 'override_device_telnet_port') ? ':' . $device->attribs->firstWhere('attrib_type', 'override_device_telnet_port')->attrib_value : '';
$device_links['telnet'] = [
'icon' => 'fa-terminal',
'url' => 'telnet://' . $device->hostname . $telnet_port,
'title' => __('Telnet'),
'external' => true,
];
if (Gate::allows('admin')) {
$device_links['capture'] = [
'icon' => 'fa-bug',
'url' => route('device', [$device->device_id, 'capture']),
'title' => __('Capture'),
'external' => false,
];
}
return $device_links;
}
private function editTabExists(string $tab): bool
{
return is_file(base_path("includes/html/pages/device/edit/$tab.inc.php"));
}
}