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) . '">', |
||||
0 ignored issues
–
show
Bug
introduced
by
Loading history...
|
|||||
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) { |
||||
0 ignored issues
–
show
The expression
$port_count of type integer|null is loosely compared to true ; this is ambiguous if the integer can be 0. You might want to explicitly use !== null instead.
In PHP, under loose comparison (like For 0 == false // true
0 == null // true
123 == false // false
123 == null // false
// It is often better to use strict comparison
0 === false // false
0 === null // false
Loading history...
|
|||||
242 | $metrics[] = $this->formatMetric($device, $port_count, 'ports', 'fa-link'); |
||||
243 | } |
||||
244 | |||||
245 | if ($sensor_count) { |
||||
0 ignored issues
–
show
The expression
$sensor_count of type integer|null is loosely compared to true ; this is ambiguous if the integer can be 0. You might want to explicitly use !== null instead.
In PHP, under loose comparison (like For 0 == false // true
0 == null // true
123 == false // false
123 == null // false
// It is often better to use strict comparison
0 === false // false
0 === null // false
Loading history...
|
|||||
246 | $metrics[] = $this->formatMetric($device, $sensor_count, 'health', 'fa-dashboard'); |
||||
247 | } |
||||
248 | |||||
249 | if ($wireless_count) { |
||||
0 ignored issues
–
show
The expression
$wireless_count of type integer|null is loosely compared to true ; this is ambiguous if the integer can be 0. You might want to explicitly use !== null instead.
In PHP, under loose comparison (like For 0 == false // true
0 == null // true
123 == false // false
123 == null // false
// It is often better to use strict comparison
0 === false // false
0 === null // false
Loading history...
|
|||||
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
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 |
||||
0 ignored issues
–
show
|
|||||
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 |
||||
0 ignored issues
–
show
array('template' => $custom['url']) of type array is incompatible with the type null|string expected by parameter $view of view() .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
Loading history...
|
|||||
345 | $actions[$row][] = $custom; |
||||
346 | } |
||||
347 | } |
||||
348 | |||||
349 | return $actions; |
||||
350 | } |
||||
351 | } |
||||
352 |