Issues (2963)

app/Http/Controllers/Table/DeviceController.php (1 issue)

1
<?php
2
/**
3
 * DeviceController.php
4
 *
5
 * -Description-
6
 *
7
 * This program is free software: you can redistribute it and/or modify
8
 * it under the terms of the GNU General Public License as published by
9
 * the Free Software Foundation, either version 3 of the License, or
10
 * (at your option) any later version.
11
 *
12
 * This program is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
15
 * GNU General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU General Public License
18
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
19
 *
20
 * @link       https://www.librenms.org
21
 *
22
 * @copyright  2019 Tony Murray
23
 * @author     Tony Murray <[email protected]>
24
 */
25
26
namespace App\Http\Controllers\Table;
27
28
use App\Models\Device;
29
use App\Models\Location;
30
use Illuminate\Database\Eloquent\Builder;
31
use Illuminate\Support\Arr;
32
use Illuminate\Support\Facades\Auth;
33
use LibreNMS\Config;
34
use LibreNMS\Util\Rewrite;
35
use LibreNMS\Util\Time;
36
use LibreNMS\Util\Url;
37
38
class DeviceController extends TableController
39
{
40
    private $detailed; // display format is detailed
41
42
    protected function rules()
43
    {
44
        return [
45
            'format' => 'nullable|in:list_basic,list_detail',
46
            'os' => 'nullable|string',
47
            'version' => 'nullable|string',
48
            'hardware' => 'nullable|string',
49
            'features' => 'nullable|string',
50
            'location' => 'nullable|string',
51
            'type' => 'nullable|string',
52
            'state' => 'nullable|in:0,1,up,down',
53
            'disabled' => 'nullable|in:0,1',
54
            'ignore' => 'nullable|in:0,1',
55
            'disable_notify' => 'nullable|in:0,1',
56
            'group' => 'nullable|int',
57
            'poller_group' => 'nullable|int',
58
            'device_id' => 'nullable|int',
59
        ];
60
    }
61
62
    protected function filterFields($request)
63
    {
64
        return ['os', 'version', 'hardware', 'features', 'type', 'status' => 'state', 'disabled', 'disable_notify', 'ignore', 'location_id' => 'location', 'device_id' => 'device_id'];
65
    }
66
67
    protected function searchFields($request)
68
    {
69
        return ['sysName', 'hostname', 'hardware', 'os', 'locations.location'];
70
    }
71
72
    protected function sortFields($request)
73
    {
74
        return [
75
            'status' => 'status',
76
            'icon' => 'icon',
77
            'hostname' => 'hostname',
78
            'hardware' => 'hardware',
79
            'os' => 'os',
80
            'uptime' => \DB::raw('IF(`status` = 1, `uptime`, `last_polled` - NOW())'),
81
            'location' => 'location',
82
            'device_id' => 'device_id',
83
        ];
84
    }
85
86
    /**
87
     * Defines the base query for this resource
88
     *
89
     * @param  \Illuminate\Http\Request  $request
90
     * @return \Illuminate\Database\Eloquent\Builder|\Illuminate\Database\Query\Builder
91
     */
92
    protected function baseQuery($request)
93
    {
94
        /** @var Builder $query */
95
        $query = Device::hasAccess($request->user())->with('location')->withCount(['ports', 'sensors', 'wirelessSensors']);
96
97
        // if searching or sorting the location field, join the locations table
98
        if ($request->get('searchPhrase') || in_array('location', array_keys($request->get('sort', [])))) {
99
            $query->leftJoin('locations', 'locations.id', 'devices.location_id');
100
        }
101
102
        // filter device group, not sure this is the most efficient query
103
        if ($group = $request->get('group')) {
104
            $query->whereHas('groups', function ($query) use ($group) {
105
                $query->where('id', $group);
106
            });
107
        }
108
109
        if ($request->get('poller_group') !== null) {
110
            $query->where('poller_group', $request->get('poller_group'));
111
        }
112
113
        return $query;
114
    }
115
116
    protected function adjustFilterValue($field, $value)
117
    {
118
        if ($field == 'location' && ! is_numeric($value)) {
119
            return Location::query()->where('location', $value)->value('id');
120
        }
121
122
        if ($field == 'state' && ! is_numeric($value)) {
123
            return str_replace(['up', 'down'], [1, 0], $value);
124
        }
125
126
        return $value;
127
    }
128
129
    private function isDetailed()
130
    {
131
        if (is_null($this->detailed)) {
132
            $this->detailed = \Request::get('format', 'list_detail') == 'list_detail';
133
        }
134
135
        return $this->detailed;
136
    }
137
138
    /**
139
     * @param  Device  $device
140
     * @return array|\Illuminate\Database\Eloquent\Model|\Illuminate\Support\Collection
141
     */
142
    public function formatItem($device)
143
    {
144
        return [
145
            'extra' => $this->getLabel($device),
146
            'status' => $this->getStatus($device),
147
            'maintenance' => $device->isUnderMaintenance(),
148
            'icon' => '<img src="' . asset($device->icon) . '" title="' . pathinfo($device->icon, PATHINFO_FILENAME) . '">',
149
            'hostname' => $this->getHostname($device),
150
            'metrics' => $this->getMetrics($device),
151
            'hardware' => Rewrite::ciscoHardware($device),
152
            'os' => $this->getOsText($device),
153
            'uptime' => (! $device->status && ! $device->last_polled) ? __('Never polled') : Time::formatInterval($device->status ? $device->uptime : $device->last_polled->diffInSeconds(), 'short'),
154
            'location' => $this->getLocation($device),
155
            'actions' => view('device.actions', ['actions' => $this->getActions($device)])->__toString(),
156
            'device_id' => $device->device_id,
157
        ];
158
    }
159
160
    /**
161
     * Get the device up/down status
162
     *
163
     * @param  Device  $device
164
     * @return string
165
     */
166
    private function getStatus($device)
167
    {
168
        if ($device->disabled == 1) {
169
            return 'disabled';
170
        } elseif ($device->status == 0) {
171
            return 'down';
172
        }
173
174
        return 'up';
175
    }
176
177
    /**
178
     * Get the status label class
179
     *
180
     * @param  Device  $device
181
     * @return string
182
     */
183
    private function getLabel($device)
184
    {
185
        if ($device->disabled == 1) {
186
            return 'blackbg';
187
        } elseif ($device->disable_notify == 1) {
188
            return 'blackbg';
189
        } elseif ($device->ignore == 1) {
190
            return 'label-default';
191
        } elseif ($device->status == 0) {
192
            return 'label-danger';
193
        } else {
194
            $warning_time = \LibreNMS\Config::get('uptime_warning', 84600);
195
            if ($device->uptime < $warning_time && $device->uptime != 0) {
196
                return 'label-warning';
197
            }
198
199
            return 'label-success';
200
        }
201
    }
202
203
    /**
204
     * @param  Device  $device
205
     * @return string
206
     */
207
    private function getHostname($device)
208
    {
209
        return (string) view('device.list.hostname', [
210
            'device' => $device,
211
            'detailed' => $this->isDetailed(),
212
        ]);
213
    }
214
215
    /**
216
     * @param  Device  $device
217
     * @return string
218
     */
219
    private function getOsText($device)
220
    {
221
        $os_text = Config::getOsSetting($device->os, 'text');
222
223
        if ($this->isDetailed()) {
224
            $os_text .= '<br />' . $device->version . ($device->features ? " ($device->features)" : '');
225
        }
226
227
        return $os_text;
228
    }
229
230
    /**
231
     * @param  Device  $device
232
     * @return string
233
     */
234
    private function getMetrics($device)
235
    {
236
        $port_count = $device->ports_count;
237
        $sensor_count = $device->sensors_count;
238
        $wireless_count = $device->wirelessSensors_count;
239
240
        $metrics = [];
241
        if ($port_count) {
242
            $metrics[] = $this->formatMetric($device, $port_count, 'ports', 'fa-link');
243
        }
244
245
        if ($sensor_count) {
246
            $metrics[] = $this->formatMetric($device, $sensor_count, 'health', 'fa-dashboard');
247
        }
248
249
        if ($wireless_count) {
250
            $metrics[] = $this->formatMetric($device, $wireless_count, 'wireless', 'fa-wifi');
251
        }
252
253
        $glue = $this->isDetailed() ? '<br />' : ' ';
254
        $metrics_content = implode(count($metrics) == 2 ? $glue : '', $metrics);
255
256
        return '<div class="device-table-metrics">' . $metrics_content . '</div>';
257
    }
258
259
    /**
260
     * @param  int|Device  $device
261
     * @param  mixed  $count
262
     * @param  mixed  $tab
263
     * @param  mixed  $icon
264
     * @return string
265
     */
266
    private function formatMetric($device, $count, $tab, $icon)
267
    {
268
        $html = '<a href="' . Url::deviceUrl($device, ['tab' => $tab]) . '">';
269
        $html .= '<span><i title="' . $tab . '" class="fa ' . $icon . ' fa-lg icon-theme"></i> ' . $count;
270
        $html .= '</span></a> ';
271
272
        return $html;
273
    }
274
275
    /**
276
     * @param  Device  $device
277
     * @return string
278
     */
279
    private function getLocation($device)
280
    {
281
        return extension_loaded('mbstring')
282
            ? mb_substr($device->location, 0, 32, 'utf8')
283
            : substr($device->location, 0, 32);
284
    }
285
286
    private function getActions(Device $device): array
287
    {
288
        $actions = [
289
            [
290
                [
291
                    'title' => 'View Device',
292
                    'href' => Url::deviceUrl($device),
293
                    'icon' => 'fa-id-card',
294
                    'external' => false,
295
                ],
296
                [
297
                    'title' => 'View alerts',
298
                    'href' => Url::deviceUrl($device, ['tab' => 'alerts']),
299
                    'icon' => 'fa-exclamation-circle',
300
                    'external' => false,
301
                ],
302
            ],
303
        ];
304
305
        if (\Auth::user()->hasGlobalAdmin()) {
0 ignored issues
show
The method hasGlobalAdmin() does not exist on Illuminate\Contracts\Auth\Authenticatable. It seems like you code against a sub-type of Illuminate\Contracts\Auth\Authenticatable such as Illuminate\Foundation\Auth\User. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

305
        if (\Auth::user()->/** @scrutinizer ignore-call */ hasGlobalAdmin()) {
Loading history...
306
            $actions[0][] = [
307
                'title' => 'Edit device',
308
                'href' => Url::deviceUrl($device, ['tab' => 'edit']),
309
                'icon' => 'fa-gear',
310
                'external' => false,
311
            ];
312
        }
313
        $row = $this->isDetailed() ? 1 : 0;
314
315
        $actions[$row][] = [
316
            'title' => 'Telnet to ' . $device->hostname,
317
            'href' => 'telnet://' . $device->hostname,
318
            'icon' => 'fa-terminal',
319
        ];
320
321
        $ssh_href = 'ssh://' . $device->hostname;
322
        if ($server = Config::get('gateone.server')) {
323
            $ssh_href = Config::get('gateone.use_librenms_user')
324
                ? $server . '?ssh=ssh://' . Auth::user()->username . '@' . $device->hostname . '&location=' . $device->hostname
325
                : $server . '?ssh=ssh://' . $device->hostname . '&location=' . $device->hostname;
326
        }
327
328
        $actions[$row][] = [
329
            'title' => 'SSH to ' . $device->hostname,
330
            'href' => $ssh_href,
331
            'icon' => 'fa-lock',
332
        ];
333
334
        $actions[$row][] = [
335
            'title' => 'Launch browser to ' . $device->hostname,
336
            'href' => 'https://' . $device->hostname,
337
            'onclick' => 'http_fallback(this); return false;',
338
            'icon' => 'fa-globe',
339
        ];
340
341
        foreach (array_values(Arr::wrap(Config::get('html.device.links'))) as $index => $custom) {
342
            if ($custom['action'] ?? false) {
343
                $row = $this->isDetailed() ? $index % 2 : 0;
344
                $custom['href'] = view(['template' => $custom['url']], ['device' => $device])->__toString(); // @phpstan-ignore-line
345
                $actions[$row][] = $custom;
346
            }
347
        }
348
349
        return $actions;
350
    }
351
}
352