1 | <?php |
||
2 | /** |
||
3 | * TopDevices.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 2018 Tony Murray |
||
23 | * @author Tony Murray <[email protected]> |
||
24 | */ |
||
25 | |||
26 | namespace App\Http\Controllers\Widgets; |
||
27 | |||
28 | use App\Models\Device; |
||
29 | use App\Models\Mempool; |
||
30 | use App\Models\Port; |
||
31 | use App\Models\Processor; |
||
32 | use App\Models\Storage; |
||
33 | use Carbon\Carbon; |
||
34 | use Illuminate\Database\Eloquent\Builder; |
||
35 | use Illuminate\Http\Request; |
||
36 | use Illuminate\Support\Collection; |
||
37 | use Illuminate\Support\Facades\Auth; |
||
38 | use Illuminate\View\View; |
||
39 | use LibreNMS\Util\Html; |
||
40 | use LibreNMS\Util\StringHelpers; |
||
41 | use LibreNMS\Util\Url; |
||
42 | use LibreNMS\Util\Validate; |
||
43 | |||
44 | class TopDevicesController extends WidgetController |
||
45 | { |
||
46 | protected $title = 'Top Devices'; |
||
47 | protected $defaults = [ |
||
48 | 'title' => null, |
||
49 | 'top_query' => 'traffic', |
||
50 | 'sort_order' => 'desc', |
||
51 | 'device_count' => 5, |
||
52 | 'time_interval' => 15, |
||
53 | 'device_group' => null, |
||
54 | ]; |
||
55 | |||
56 | public function title() |
||
57 | { |
||
58 | $settings = $this->getSettings(); |
||
59 | |||
60 | return isset($settings['title']) ? $settings['title'] : $this->title; |
||
61 | } |
||
62 | |||
63 | /** |
||
64 | * @param Request $request |
||
65 | * @return View |
||
66 | */ |
||
67 | public function getView(Request $request) |
||
68 | { |
||
69 | $settings = $this->getSettings(); |
||
70 | $sort = $settings['sort_order']; |
||
71 | |||
72 | // We use raw() function below, validate input and default to sane value. |
||
73 | $sort = Validate::ascDesc($sort, 'ASC'); |
||
74 | |||
75 | switch ($settings['top_query']) { |
||
76 | case 'traffic': |
||
77 | $data = $this->getTrafficData($sort); |
||
78 | break; |
||
79 | case 'uptime': |
||
80 | $data = $this->getUptimeData($sort); |
||
81 | break; |
||
82 | case 'ping': |
||
83 | $data = $this->getPingData($sort); |
||
84 | break; |
||
85 | case 'cpu': |
||
86 | $data = $this->getProcessorData($sort); |
||
87 | break; |
||
88 | case 'ram': |
||
89 | $data = $this->getMemoryData($sort); |
||
90 | break; |
||
91 | case 'poller': |
||
92 | $data = $this->getPollerData($sort); |
||
93 | break; |
||
94 | case 'storage': |
||
95 | $data = $this->getStorageData($sort); |
||
96 | break; |
||
97 | default: |
||
98 | $data = []; |
||
99 | } |
||
100 | |||
101 | return view('widgets.top-devices', $data); |
||
102 | } |
||
103 | |||
104 | public function getSettingsView(Request $request) |
||
105 | { |
||
106 | return view('widgets.settings.top-devices', $this->getSettings(true)); |
||
107 | } |
||
108 | |||
109 | /** |
||
110 | * @param array|string $headers |
||
111 | * @param Collection $rows |
||
112 | * @return array |
||
113 | */ |
||
114 | private function formatData($headers, $rows) |
||
115 | { |
||
116 | return [ |
||
117 | 'headers' => (array) $headers, |
||
118 | 'rows' => $rows, |
||
119 | ]; |
||
120 | } |
||
121 | |||
122 | /** |
||
123 | * @param Builder $query |
||
124 | * @param string $left_table |
||
125 | * @return Builder |
||
126 | */ |
||
127 | private function withDeviceQuery(Builder $query, $left_table) |
||
128 | { |
||
129 | $settings = $this->getSettings(); |
||
130 | |||
131 | /** @var Builder $query */ |
||
132 | return $query->with(['device' => function ($query) { |
||
133 | return $query->select('device_id', 'hostname', 'sysName', 'status', 'os'); |
||
134 | }]) |
||
135 | ->select("$left_table.device_id") |
||
136 | ->leftJoin('devices', "$left_table.device_id", 'devices.device_id') |
||
137 | ->groupBy("$left_table.device_id") |
||
138 | ->where('devices.last_polled', '>', Carbon::now()->subMinutes($settings['time_interval'])) |
||
139 | ->when($settings['device_group'], function ($query) use ($settings) { |
||
140 | /** @var Builder<\App\Models\DeviceRelatedModel> $query */ |
||
141 | $inDeviceGroup = $query->inDeviceGroup($settings['device_group']); /** @var Builder $inDeviceGroup */ |
||
142 | |||
143 | return $inDeviceGroup; |
||
144 | }); |
||
145 | } |
||
146 | |||
147 | /** |
||
148 | * @return Builder |
||
149 | */ |
||
150 | private function deviceQuery() |
||
151 | { |
||
152 | $settings = $this->getSettings(); |
||
153 | |||
154 | return Device::hasAccess(Auth::user())->select('device_id', 'hostname', 'sysName', 'status', 'os') |
||
0 ignored issues
–
show
Bug
Best Practice
introduced
by
Loading history...
|
|||
155 | ->where('devices.last_polled', '>', Carbon::now()->subMinutes($settings['time_interval'])) |
||
156 | ->when($settings['device_group'], function ($query) use ($settings) { |
||
157 | return $query->inDeviceGroup($settings['device_group']); |
||
158 | }) |
||
159 | ->limit($settings['device_count']); |
||
160 | } |
||
161 | |||
162 | /** |
||
163 | * @param Device $device |
||
164 | * @param string $graph_type |
||
165 | * @param array $graph_params |
||
166 | * @return array |
||
167 | */ |
||
168 | private function standardRow($device, $graph_type, $graph_params = []) |
||
169 | { |
||
170 | return [ |
||
171 | Url::deviceLink($device, $device->shortDisplayName()), |
||
172 | Url::deviceLink($device, Url::minigraphImage( |
||
173 | $device, |
||
174 | Carbon::now()->subDays(1)->timestamp, |
||
175 | Carbon::now()->timestamp, |
||
176 | $graph_type, |
||
177 | 'no', |
||
178 | 150, |
||
179 | 21 |
||
180 | ), $graph_params, 0, 0, 0), |
||
181 | ]; |
||
182 | } |
||
183 | |||
184 | private function getTrafficData($sort) |
||
185 | { |
||
186 | $settings = $this->getSettings(); |
||
187 | |||
188 | $query = Port::hasAccess(Auth::user())->with(['device' => function ($query) { |
||
189 | $query->select('device_id', 'hostname', 'sysName', 'status', 'os'); |
||
190 | }]) |
||
191 | ->select('device_id') |
||
192 | ->groupBy('device_id') |
||
193 | ->where('poll_time', '>', Carbon::now()->subMinutes($settings['time_interval'])->timestamp) |
||
194 | ->when($settings['device_group'], function ($query) use ($settings) { |
||
195 | return $query->inDeviceGroup($settings['device_group']); |
||
196 | }, function ($query) { |
||
197 | return $query->has('device'); |
||
198 | }) |
||
199 | ->orderByRaw('SUM(ifInOctets_rate + ifOutOctets_rate) ' . $sort) |
||
200 | ->limit($settings['device_count']); |
||
201 | |||
202 | $results = $query->get()->map(function ($port) { |
||
203 | return $this->standardRow($port->device, 'device_bits'); |
||
204 | }); |
||
205 | |||
206 | return $this->formatData('Traffic', $results); |
||
207 | } |
||
208 | |||
209 | private function getUptimeData($sort) |
||
210 | { |
||
211 | $settings = $this->getSettings(); |
||
212 | |||
213 | /** @var Builder $query */ |
||
214 | $query = $this->deviceQuery()->orderBy('uptime', $sort)->limit($settings['device_count']); |
||
215 | |||
216 | $results = $query->get()->map(function ($device) { |
||
217 | /** @var Device $device */ |
||
218 | return $this->standardRow($device, 'device_uptime', ['tab' => 'graphs', 'group' => 'system']); |
||
219 | }); |
||
220 | |||
221 | return $this->formatData('Uptime', $results); |
||
222 | } |
||
223 | |||
224 | private function getPingData($sort) |
||
225 | { |
||
226 | $settings = $this->getSettings(); |
||
227 | |||
228 | /** @var Builder $query */ |
||
229 | $query = $this->deviceQuery()->orderBy('last_ping_timetaken', $sort)->limit($settings['device_count']); |
||
230 | |||
231 | $results = $query->get()->map(function ($device) { |
||
232 | /** @var Device $device */ |
||
233 | return $this->standardRow($device, 'device_ping_perf', ['tab' => 'graphs', 'group' => 'poller']); |
||
234 | }); |
||
235 | |||
236 | return $this->formatData('Response time', $results); |
||
237 | } |
||
238 | |||
239 | private function getProcessorData($sort) |
||
240 | { |
||
241 | $settings = $this->getSettings(); |
||
242 | |||
243 | /** @var Processor $query */ |
||
244 | $query = $this->withDeviceQuery(Processor::hasAccess(Auth::user()), (new Processor)->getTable()) |
||
245 | ->orderByRaw('AVG(`processor_usage`) ' . $sort) |
||
246 | ->limit($settings['device_count']); |
||
247 | |||
248 | $results = $query->get()->map(function ($port) { |
||
249 | return $this->standardRow($port->device, 'device_processor', ['tab' => 'health', 'metric' => 'processor']); |
||
250 | }); |
||
251 | |||
252 | return $this->formatData('CPU Load', $results); |
||
253 | } |
||
254 | |||
255 | private function getMemoryData($sort) |
||
256 | { |
||
257 | $settings = $this->getSettings(); |
||
258 | |||
259 | /** @var Mempool $query */ |
||
260 | $query = $this->withDeviceQuery(Mempool::hasAccess(Auth::user()), (new Mempool)->getTable()) |
||
261 | ->orderBy('mempool_perc', $sort) |
||
262 | ->limit($settings['device_count']); |
||
263 | |||
264 | $results = $query->get()->map(function ($port) { |
||
265 | return $this->standardRow($port->device, 'device_mempool', ['tab' => 'health', 'metric' => 'mempool']); |
||
266 | }); |
||
267 | |||
268 | return $this->formatData('Memory usage', $results); |
||
269 | } |
||
270 | |||
271 | private function getPollerData($sort) |
||
272 | { |
||
273 | $settings = $this->getSettings(); |
||
274 | |||
275 | $query = $this->deviceQuery()->orderBy('last_polled_timetaken', $sort)->limit($settings['device_count']); |
||
276 | |||
277 | $results = $query->get()->map(function ($device) { |
||
278 | /** @var Device $device */ |
||
279 | return $this->standardRow($device, 'device_poller_perf', ['tab' => 'graphs', 'group' => 'poller']); |
||
280 | }); |
||
281 | |||
282 | return $this->formatData('Poller duration', $results); |
||
283 | } |
||
284 | |||
285 | private function getStorageData($sort) |
||
286 | { |
||
287 | $settings = $this->getSettings(); |
||
288 | |||
289 | $query = Storage::hasAccess(Auth::user())->with(['device' => function ($query) { |
||
290 | $query->select('device_id', 'hostname', 'sysName', 'status', 'os'); |
||
291 | }]) |
||
292 | ->leftJoin('devices', 'storage.device_id', 'devices.device_id') |
||
293 | ->select('storage.device_id', 'storage_id', 'storage_descr', 'storage_perc', 'storage_perc_warn') |
||
294 | ->where('devices.last_polled', '>', Carbon::now()->subMinutes($settings['time_interval'])) |
||
295 | ->when($settings['device_group'], function ($query) use ($settings) { |
||
296 | $query->inDeviceGroup($settings['device_group']); |
||
297 | }) |
||
298 | ->orderBy('storage_perc', $sort) |
||
299 | ->limit($settings['device_count']); |
||
300 | |||
301 | $results = $query->get()->map(function ($storage) { |
||
302 | $device = $storage->device; |
||
303 | |||
304 | $graph_array = [ |
||
305 | 'height' => 100, |
||
306 | 'width' => 210, |
||
307 | 'to' => Carbon::now()->timestamp, |
||
308 | 'from' => Carbon::now()->subDay()->timestamp, |
||
309 | 'id' => $storage->storage_id, |
||
310 | 'type' => 'storage_usage', |
||
311 | 'legend' => 'no', |
||
312 | ]; |
||
313 | $overlib_content = Url::overlibContent($graph_array, $device->displayName() . ' - ' . $storage->storage_descr); |
||
314 | |||
315 | $link_array = $graph_array; |
||
316 | $link_array['page'] = 'graphs'; |
||
317 | unset($link_array['height'], $link_array['width'], $link_array['legend']); |
||
318 | $link = Url::generate($link_array); |
||
319 | |||
320 | return [ |
||
321 | Url::deviceLink($device, $device->shortDisplayName()), |
||
322 | StringHelpers::shortenText($storage->storage_descr, 50), |
||
323 | Url::overlibLink( |
||
324 | $link, |
||
325 | Html::percentageBar(150, 20, $storage->storage_perc, '', $storage->storage_perc . '%', $storage->storage_perc_warn), |
||
326 | $overlib_content |
||
327 | ), |
||
328 | ]; |
||
329 | }); |
||
330 | |||
331 | return $this->formatData(['Storage Device', 'Disk usage'], $results); |
||
332 | } |
||
333 | } |
||
334 |