Issues (2963)

includes/html/api_functions.inc.php (3 issues)

Code
1
<?php
2
3
/*
4
 * LibreNMS
5
 *
6
 * Copyright (c) 2014 Neil Lathwood <https://github.com/laf/ http://www.lathwood.co.uk/fa>
7
 *
8
 * This program is free software: you can redistribute it and/or modify it
9
 * under the terms of the GNU General Public License as published by the
10
 * Free Software Foundation, either version 3 of the License, or (at your
11
 * option) any later version.  Please see LICENSE.txt at the top level of
12
 * the source code distribution for details.
13
 */
14
15
use App\Models\Availability;
16
use App\Models\Device;
17
use App\Models\DeviceGroup;
18
use App\Models\DeviceOutage;
19
use App\Models\OspfPort;
20
use App\Models\Port;
21
use App\Models\PortGroup;
22
use App\Models\PortsFdb;
23
use App\Models\Sensor;
24
use App\Models\ServiceTemplate;
25
use Illuminate\Database\Eloquent\Builder;
26
use Illuminate\Routing\Router;
27
use Illuminate\Support\Arr;
28
use Illuminate\Support\Facades\Validator;
29
use Illuminate\Support\Str;
30
use LibreNMS\Alerting\QueryBuilderParser;
31
use LibreNMS\Config;
32
use LibreNMS\Data\Store\Datastore;
33
use LibreNMS\Exceptions\InvalidIpException;
34
use LibreNMS\Util\IP;
35
use LibreNMS\Util\IPv4;
36
use LibreNMS\Util\Rewrite;
37
38
function api_success($result, $result_name, $message = null, $code = 200, $count = null, $extra = null)
39
{
40
    if (isset($result) && ! isset($result_name)) {
41
        return api_error(500, 'Result name not specified');
42
    }
43
44
    $output = ['status' => 'ok'];
45
46
    if (isset($result)) {
47
        $output[$result_name] = $result;
48
    }
49
    if (isset($message) && $message != '') {
50
        $output['message'] = $message;
51
    }
52
    if (! isset($count) && is_array($result)) {
53
        $count = count($result);
54
    }
55
    if (isset($count)) {
56
        $output['count'] = $count;
57
    }
58
    if (isset($extra)) {
59
        $output = array_merge($output, $extra);
60
    }
61
62
    return response()->json($output, $code, [], JSON_PRETTY_PRINT);
63
} // end api_success()
64
65
function api_success_noresult($code, $message = null)
66
{
67
    return api_success(null, null, $message, $code);
68
} // end api_success_noresult
69
70
function api_error($statusCode, $message)
71
{
72
    return response()->json([
73
        'status'  => 'error',
74
        'message' => $message,
75
    ], $statusCode, [], JSON_PRETTY_PRINT);
76
} // end api_error()
77
78
function api_not_found()
79
{
80
    return api_error(404, "This API route doesn't exist.");
81
}
82
83
function api_get_graph(array $vars)
84
{
85
    global $dur;        // Needed for callback within graph code
86
87
    $auth = true;
88
89
    // prevent ugly error for undefined graphs from being passed to the user
90
    [$type, $subtype] = extract_graph_type($vars['type']);
91
    if (! is_file(base_path("includes/html/graphs/$type/auth.inc.php"))) {
92
        return api_error(400, 'Invalid graph type');
93
    }
94
95
    ob_start();
96
97
    include 'includes/html/graphs/graph.inc.php';
98
    Datastore::terminate();
99
100
    $image = ob_get_contents();
101
    ob_end_clean();
102
103
    if ($vars['output'] === 'base64') {
104
        return api_success(['image' => $image, 'content-type' => get_image_type(Config::get('webui.graph_type'))], 'image');
105
    }
106
107
    return response($image, 200, ['Content-Type' => get_image_type(Config::get('webui.graph_type'))]);
108
}
109
110
function check_bill_permission($bill_id, $callback)
111
{
112
    if (! bill_permitted($bill_id)) {
113
        return api_error(403, 'Insufficient permissions to access this bill');
114
    }
115
116
    return $callback($bill_id);
117
}
118
119
function check_device_permission($device_id, $callback = null)
120
{
121
    if (! device_permitted($device_id)) {
122
        return api_error(403, 'Insufficient permissions to access this device');
123
    }
124
125
    return is_callable($callback) ? $callback($device_id) : true;
126
}
127
128
function check_port_permission($port_id, $device_id, $callback)
129
{
130
    if (! device_permitted($device_id) && ! port_permitted($port_id, $device_id)) {
131
        return api_error(403, 'Insufficient permissions to access this port');
132
    }
133
134
    return $callback($port_id);
135
}
136
137
function get_graph_by_port_hostname(Illuminate\Http\Request $request, $ifname = null, $type = 'port_bits')
138
{
139
    // This will return a graph for a given port by the ifName
140
    $hostname = $request->route('hostname');
141
    $device_id = ctype_digit($hostname) ? $hostname : getidbyname($hostname);
142
    $vars = [
143
        'port'   => $ifname ?: $request->route('ifname'),
144
        'type'   => $request->route('type', $type),
145
        'output' => $request->get('output', 'display'),
146
        'width'  => $request->get('width', 1075),
147
        'height' => $request->get('height', 300),
148
    ];
149
150
    if ($request->has('from')) {
151
        $vars['from'] = $request->get('from');
152
    }
153
154
    if ($request->has('to')) {
155
        $vars['to'] = $request->get('to');
156
    }
157
158
    $port = $request->get('ifDescr') ? 'ifDescr' : 'ifName';
159
    $vars['id'] = dbFetchCell("SELECT `P`.`port_id` FROM `ports` AS `P` JOIN `devices` AS `D` ON `P`.`device_id` = `D`.`device_id` WHERE `D`.`device_id`=? AND `P`.`$port`=? AND `deleted` = 0 LIMIT 1", [$device_id, $vars['port']]);
160
161
    return check_port_permission($vars['id'], $device_id, function () use ($vars) {
162
        return api_get_graph($vars);
163
    });
164
}
165
166
function get_port_stats_by_port_hostname(Illuminate\Http\Request $request)
167
{
168
    $ifName = $request->route('ifname');
169
170
    // handle %2f in paths and pass to get_graph_by_port_hostname if needed
171
    if (Str::contains($ifName, '/')) {
172
        $parts = explode('/', $request->path());
173
174
        if (isset($parts[5])) {
175
            $ifName = urldecode($parts[5]);
176
            if (isset($parts[6])) {
177
                return get_graph_by_port_hostname($request, $ifName, $parts[6]);
178
            }
179
        }
180
    }
181
182
    // This will return port stats based on a devices hostname and ifName
183
    $hostname = $request->route('hostname');
184
    $device_id = ctype_digit($hostname) ? $hostname : getidbyname($hostname);
185
    $port = dbFetchRow('SELECT * FROM `ports` WHERE `device_id`=? AND `ifName`=? AND `deleted` = 0', [$device_id, $ifName]);
186
187
    return check_port_permission($port['port_id'], $device_id, function () use ($request, $port) {
188
        $in_rate = $port['ifInOctets_rate'] * 8;
189
        $out_rate = $port['ifOutOctets_rate'] * 8;
190
        $port['in_rate'] = \LibreNMS\Util\Number::formatSi($in_rate, 2, 3, 'bps');
191
        $port['out_rate'] = \LibreNMS\Util\Number::formatSi($out_rate, 2, 3, 'bps');
192
        $port['in_perc'] = number_format($in_rate / $port['ifSpeed'] * 100, 2, '.', '');
193
        $port['out_perc'] = number_format($out_rate / $port['ifSpeed'] * 100, 2, '.', '');
194
        $port['in_pps'] = \LibreNMS\Util\Number::formatBi($port['ifInUcastPkts_rate'], 2, 3, '');
195
        $port['out_pps'] = \LibreNMS\Util\Number::formatBi($port['ifOutUcastPkts_rate'], 2, 3, '');
196
197
        //only return requested columns
198
        if ($request->has('columns')) {
199
            $cols = explode(',', $request->get('columns'));
200
            foreach (array_keys($port) as $c) {
201
                if (! in_array($c, $cols)) {
202
                    unset($port[$c]);
203
                }
204
            }
205
        }
206
207
        return api_success($port, 'port');
208
    });
209
}
210
211
function get_graph_generic_by_hostname(Illuminate\Http\Request $request)
212
{
213
    // This will return a graph type given a device id.
214
    $hostname = $request->route('hostname');
215
    $sensor_id = $request->route('sensor_id');
216
    $vars = [];
217
    $vars['type'] = $request->route('type', 'device_uptime');
218
    $vars['output'] = $request->get('output', 'display');
219
    if (isset($sensor_id)) {
220
        $vars['id'] = $sensor_id;
221
        if (Str::contains($vars['type'], '_wireless')) {
222
            $vars['type'] = str_replace('device_', '', $vars['type']);
223
        } else {
224
            // If this isn't a wireless graph we need to fix the name.
225
            $vars['type'] = str_replace('device_', 'sensor_', $vars['type']);
226
        }
227
    }
228
229
    // use hostname as device_id if it's all digits
230
    $device_id = ctype_digit($hostname) ? $hostname : getidbyname($hostname);
231
    $device = device_by_id_cache($device_id);
232
    $vars['device'] = $device['device_id'];
233
234
    return check_device_permission($device_id, function () use ($request, $vars) {
235
        if ($request->has('from')) {
236
            $vars['from'] = $request->get('from');
237
        }
238
239
        if ($request->has('to')) {
240
            $vars['to'] = $request->get('to');
241
        }
242
243
        $vars['width'] = $request->get('width', 1075);
244
        $vars['height'] = $request->get('height', 300);
245
246
        return api_get_graph($vars);
247
    });
248
}
249
250
function list_locations()
251
{
252
    $locations = dbFetchRows('SELECT `locations`.* FROM `locations` WHERE `locations`.`location` IS NOT NULL');
253
    $total_locations = count($locations);
254
    if ($total_locations == 0) {
255
        return api_error(404, 'Locations do not exist');
256
    }
257
258
    return api_success($locations, 'locations');
259
}
260
261
function get_device(Illuminate\Http\Request $request)
262
{
263
    // return details of a single device
264
    $hostname = $request->route('hostname');
265
266
    // use hostname as device_id if it's all digits
267
    $device_id = ctype_digit($hostname) ? $hostname : getidbyname($hostname);
268
269
    // find device matching the id
270
    $device = device_by_id_cache($device_id);
271
    if (! $device || ! $device['device_id']) {
272
        return api_error(404, "Device $hostname does not exist");
273
    }
274
275
    return check_device_permission($device_id, function () use ($device) {
276
        $host_id = get_vm_parent_id($device);
277
        if (is_numeric($host_id)) {
278
            $device = array_merge($device, ['parent_id' => $host_id]);
279
        }
280
281
        return api_success([$device], 'devices');
282
    });
283
}
284
285
function list_devices(Illuminate\Http\Request $request)
286
{
287
    // This will return a list of devices
288
289
    $order = $request->get('order');
290
    $type = $request->get('type');
291
    $query = $request->get('query');
292
    $param = [];
293
294
    if (empty($order)) {
295
        $order = 'hostname';
296
    }
297
298
    if (stristr($order, ' desc') === false && stristr($order, ' asc') === false) {
299
        $order = 'd.`' . $order . '` ASC';
300
    }
301
302
    $select = ' d.*, GROUP_CONCAT(dd.device_id) AS dependency_parent_id, GROUP_CONCAT(dd.hostname) AS dependency_parent_hostname, `location`, `lat`, `lng` ';
303
    $join = ' LEFT JOIN `device_relationships` AS dr ON dr.`child_device_id` = d.`device_id` LEFT JOIN `devices` AS dd ON dr.`parent_device_id` = dd.`device_id` LEFT JOIN `locations` ON `locations`.`id` = `d`.`location_id`';
304
305
    if ($type == 'all' || empty($type)) {
306
        $sql = '1';
307
    } elseif ($type == 'active') {
308
        $sql = "`d`.`ignore`='0' AND `d`.`disabled`='0'";
309
    } elseif ($type == 'location') {
310
        $sql = "`locations`.`location` LIKE '%" . $query . "%'";
311
    } elseif ($type == 'hostname') {
312
        $sql = "`d`.`hostname` LIKE '%" . $query . "%'";
313
    } elseif ($type == 'ignored') {
314
        $sql = "`d`.`ignore`='1' AND `d`.`disabled`='0'";
315
    } elseif ($type == 'up') {
316
        $sql = "`d`.`status`='1' AND `d`.`ignore`='0' AND `d`.`disabled`='0'";
317
    } elseif ($type == 'down') {
318
        $sql = "`d`.`status`='0' AND `d`.`ignore`='0' AND `d`.`disabled`='0'";
319
    } elseif ($type == 'disabled') {
320
        $sql = "`d`.`disabled`='1'";
321
    } elseif ($type == 'os') {
322
        $sql = '`d`.`os`=?';
323
        $param[] = $query;
324
    } elseif ($type == 'mac') {
325
        $join .= ' LEFT JOIN `ports` AS p ON d.`device_id` = p.`device_id` LEFT JOIN `ipv4_mac` AS m ON p.`port_id` = m.`port_id` ';
326
        $sql = 'm.`mac_address`=?';
327
        $select .= ',p.* ';
328
        $param[] = $query;
329
    } elseif ($type == 'ipv4') {
330
        $join .= ' LEFT JOIN `ports` AS p ON d.`device_id` = p.`device_id` LEFT JOIN `ipv4_addresses` AS a ON p.`port_id` = a.`port_id` ';
331
        $sql = 'a.`ipv4_address`=?';
332
        $select .= ',p.* ';
333
        $param[] = $query;
334
    } elseif ($type == 'ipv6') {
335
        $join .= ' LEFT JOIN `ports` AS p ON d.`device_id` = p.`device_id` LEFT JOIN `ipv6_addresses` AS a ON p.`port_id` = a.`port_id` ';
336
        $sql = 'a.`ipv6_address`=? OR a.`ipv6_compressed`=?';
337
        $select .= ',p.* ';
338
        $param = [$query, $query];
339
    } else {
340
        $sql = '1';
341
    }
342
343
    if (! Auth::user()->hasGlobalRead()) {
344
        $sql .= ' AND `d`.`device_id` IN (SELECT device_id FROM devices_perms WHERE user_id = ?)';
345
        $param[] = Auth::id();
346
    }
347
    $devices = [];
348
    $dev_query = "SELECT $select FROM `devices` AS d $join WHERE $sql GROUP BY d.`hostname` ORDER BY $order";
349
    foreach (dbFetchRows($dev_query, $param) as $device) {
350
        $host_id = get_vm_parent_id($device);
351
        $device['ip'] = inet6_ntop($device['ip']);
352
        if (is_numeric($host_id)) {
353
            $device['parent_id'] = $host_id;
354
        }
355
        $devices[] = $device;
356
    }
357
358
    return api_success($devices, 'devices');
359
}
360
361
function add_device(Illuminate\Http\Request $request)
362
{
363
    // This will add a device using the data passed encoded with json
364
    // FIXME: Execution flow through this function could be improved
365
    $data = json_decode($request->getContent(), true);
366
367
    $additional = [];
368
    // keep scrutinizer from complaining about snmpver not being set for all execution paths
369
    $snmpver = 'v2c';
370
    if (empty($data)) {
371
        return api_error(400, 'No information has been provided to add this new device');
372
    }
373
    if (empty($data['hostname'])) {
374
        return api_error(400, 'Missing the device hostname');
375
    }
376
377
    $hostname = $data['hostname'];
378
    $port = $data['port'] ?: Config::get('snmp.port');
379
    $transport = $data['transport'] ?: 'udp';
380
    $poller_group = $data['poller_group'] ?: 0;
381
    $force_add = $data['force_add'] ? true : false;
382
    $snmp_disable = ($data['snmp_disable']);
383
    if ($snmp_disable) {
384
        $additional = [
385
            'sysName'      => $data['sysName'] ?: '',
386
            'os'           => $data['os'] ?: 'ping',
387
            'hardware'     => $data['hardware'] ?: '',
388
            'snmp_disable' => 1,
389
        ];
390
    } elseif ($data['version'] == 'v1' || $data['version'] == 'v2c') {
391
        if ($data['community']) {
392
            Config::set('snmp.community', [$data['community']]);
393
        }
394
395
        $snmpver = $data['version'];
396
    } elseif ($data['version'] == 'v3') {
397
        $v3 = [
398
            'authlevel'  => $data['authlevel'],
399
            'authname'   => $data['authname'],
400
            'authpass'   => $data['authpass'],
401
            'authalgo'   => $data['authalgo'],
402
            'cryptopass' => $data['cryptopass'],
403
            'cryptoalgo' => $data['cryptoalgo'],
404
        ];
405
406
        $v3_config = Config::get('snmp.v3');
407
        array_unshift($v3_config, $v3);
408
        Config::set('snmp.v3', $v3_config);
409
        $snmpver = 'v3';
410
    } else {
411
        return api_error(400, 'You haven\'t specified an SNMP version to use');
412
    }
413
414
    $additional['overwrite_ip'] = $data['overwrite_ip'] ?: null;
415
416
    try {
417
        $device_id = addHost($hostname, $snmpver, $port, $transport, $poller_group, $force_add, 'ifIndex', $additional);
418
    } catch (Exception $e) {
419
        return api_error(500, $e->getMessage());
420
    }
421
422
    return api_success_noresult(201, "Device $hostname ($device_id) has been added successfully");
423
}
424
425
function del_device(Illuminate\Http\Request $request)
426
{
427
    // This will add a device using the data passed encoded with json
428
    $hostname = $request->route('hostname');
429
430
    if (empty($hostname)) {
431
        return api_error(400, 'No hostname has been provided to delete');
432
    }
433
434
    // allow deleting by device_id or hostname
435
    $device_id = ctype_digit($hostname) ? $hostname : getidbyname($hostname);
436
    $device = null;
437
    if ($device_id) {
438
        // save the current details for returning to the client on successful delete
439
        $device = device_by_id_cache($device_id);
440
    }
441
442
    if (! $device) {
443
        return api_error(404, "Device $hostname not found");
444
    }
445
446
    $response = delete_device($device_id);
447
    if (empty($response)) {
448
        // FIXME: Need to provide better diagnostics out of delete_device
449
        return api_error(500, 'Device deletion failed');
450
    }
451
452
    // deletion succeeded - include old device details in response
453
    return api_success([$device], 'devices', $response);
454
}
455
456
function device_availability(Illuminate\Http\Request $request)
457
{
458
    // return availability per device
459
460
    $hostname = $request->route('hostname');
461
462
    if (empty($hostname)) {
463
        return api_error(400, 'No hostname has been provided to get availability');
464
    }
465
466
    $device_id = ctype_digit($hostname) ? $hostname : getidbyname($hostname);
467
468
    return check_device_permission($device_id, function ($device_id) {
469
        $availabilities = Availability::select('duration', 'availability_perc')
470
                      ->where('device_id', '=', $device_id)
471
                      ->orderBy('duration', 'ASC');
472
473
        return api_success($availabilities->get(), 'availability');
474
    });
475
}
476
477
function device_outages(Illuminate\Http\Request $request)
478
{
479
    // return outages per device
480
481
    $hostname = $request->route('hostname');
482
483
    if (empty($hostname)) {
484
        return api_error(400, 'No hostname has been provided to get availability');
485
    }
486
487
    $device_id = ctype_digit($hostname) ? $hostname : getidbyname($hostname);
488
489
    return check_device_permission($device_id, function ($device_id) {
490
        $outages = DeviceOutage::select('going_down', 'up_again')
491
                   ->where('device_id', '=', $device_id)
492
                   ->orderBy('going_down', 'DESC');
493
494
        return api_success($outages->get(), 'outages');
495
    });
496
}
497
498
function get_vlans(Illuminate\Http\Request $request)
499
{
500
    $hostname = $request->route('hostname');
501
502
    if (empty($hostname)) {
503
        return api_error(500, 'No hostname has been provided');
504
    }
505
506
    $device_id = ctype_digit($hostname) ? $hostname : getidbyname($hostname);
507
    $device = null;
508
    if ($device_id) {
509
        // save the current details for returning to the client on successful delete
510
        $device = device_by_id_cache($device_id);
511
    }
512
513
    if (! $device) {
514
        return api_error(404, "Device $hostname not found");
515
    }
516
517
    return check_device_permission($device_id, function ($device_id) {
518
        $vlans = dbFetchRows('SELECT vlan_vlan,vlan_domain,vlan_name,vlan_type,vlan_mtu FROM vlans WHERE `device_id` = ?', [$device_id]);
519
520
        return api_success($vlans, 'vlans');
521
    });
522
}
523
524
function show_endpoints(Illuminate\Http\Request $request, Router $router)
525
{
526
    $output = [];
527
    $base = str_replace('api/v0', '', $request->url());
528
    foreach ($router->getRoutes() as $route) {
529
        /** @var \Illuminate\Routing\Route $route */
530
        if (Str::startsWith($route->getPrefix(), 'api/v0') && $route->getName()) {
531
            $output[$route->getName()] = $base . $route->uri();
532
        }
533
    }
534
535
    ksort($output);
536
537
    return response()->json($output, 200, [], JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
538
}
539
540
function list_bgp(Illuminate\Http\Request $request)
541
{
542
    $sql = '';
543
    $sql_params = [];
544
    $hostname = $request->get('hostname');
545
    $asn = $request->get('asn');
546
    $remote_asn = $request->get('remote_asn');
547
    $local_address = $request->get('local_address');
548
    $remote_address = $request->get('remote_address');
549
    $device_id = ctype_digit($hostname) ? $hostname : getidbyname($hostname);
550
    if (is_numeric($device_id)) {
551
        $sql .= ' AND `devices`.`device_id` = ?';
552
        $sql_params[] = $device_id;
553
    }
554
    if (! empty($asn)) {
555
        $sql .= ' AND `devices`.`bgpLocalAs` = ?';
556
        $sql_params[] = $asn;
557
    }
558
    if (! empty($remote_asn)) {
559
        $sql .= ' AND `bgpPeers`.`bgpPeerRemoteAs` = ?';
560
        $sql_params[] = $remote_asn;
561
    }
562
    if (! empty($local_address)) {
563
        $sql .= ' AND `bgpPeers`.`bgpLocalAddr` = ?';
564
        try {
565
            $sql_params[] = IP::parse($local_address)->uncompressed();
566
        } catch (InvalidIpException $e) {
567
            return api_error(400, 'Invalid local address');
568
        }
569
    }
570
    if (! empty($remote_address)) {
571
        $sql .= ' AND `bgpPeers`.`bgpPeerIdentifier` = ?';
572
        try {
573
            $sql_params[] = IP::parse($remote_address)->uncompressed();
574
        } catch (InvalidIpException $e) {
575
            return api_error(400, 'Invalid remote address');
576
        }
577
    }
578
579
    $bgp_sessions = dbFetchRows("SELECT `bgpPeers`.* FROM `bgpPeers` LEFT JOIN `devices` ON `bgpPeers`.`device_id` = `devices`.`device_id` WHERE `bgpPeerState` IS NOT NULL AND `bgpPeerState` != '' $sql", $sql_params);
580
    $total_bgp_sessions = count($bgp_sessions);
581
    if (! is_numeric($total_bgp_sessions)) {
0 ignored issues
show
The condition is_numeric($total_bgp_sessions) is always true.
Loading history...
582
        return api_error(500, 'Error retrieving bgpPeers');
583
    }
584
585
    return api_success($bgp_sessions, 'bgp_sessions');
586
}
587
588
function get_bgp(Illuminate\Http\Request $request)
589
{
590
    $bgpPeerId = $request->route('id');
591
    if (! is_numeric($bgpPeerId)) {
592
        return api_error(400, 'Invalid id has been provided');
593
    }
594
595
    $bgp_session = dbFetchRows("SELECT * FROM `bgpPeers` WHERE `bgpPeerState` IS NOT NULL AND `bgpPeerState` != '' AND bgpPeer_id = ?", [$bgpPeerId]);
596
    $bgp_session_count = count($bgp_session);
597
    if (! is_numeric($bgp_session_count)) {
598
        return api_error(500, 'Error retrieving BGP peer');
599
    }
600
    if ($bgp_session_count == 0) {
601
        return api_error(404, "BGP peer $bgpPeerId does not exist");
602
    }
603
604
    return api_success($bgp_session, 'bgp_session');
605
}
606
607
function edit_bgp_descr(Illuminate\Http\Request $request)
608
{
609
    $bgp_descr = $request->json('bgp_descr');
610
    if (! $bgp_descr) {
611
        return api_error(500, 'Invalid JSON data');
612
    }
613
614
    //find existing bgp for update
615
    $bgpPeerId = $request->route('id');
616
    if (! is_numeric($bgpPeerId)) {
617
        return api_error(400, 'Invalid id has been provided');
618
    }
619
620
    $peer = \App\Models\BgpPeer::firstWhere('bgpPeer_id', $bgpPeerId);
621
622
    // update existing bgp
623
    if ($peer === null) {
624
        return api_error(404, 'BGP peer ' . $bgpPeerId . ' does not exist');
625
    }
626
627
    $peer->bgpPeerDescr = $bgp_descr;
628
629
    if ($peer->save()) {
630
        return api_success_noresult(200, 'BGP description for peer ' . $peer->bgpPeerIdentifier . ' on device ' . $peer->device_id . ' updated to ' . $peer->bgpPeerDescr . '.');
631
    }
632
633
    return api_error(500, 'Failed to update existing bgp');
634
}
635
636
function list_cbgp(Illuminate\Http\Request $request)
637
{
638
    $sql = '';
639
    $sql_params = [];
640
    $hostname = $request->get('hostname');
641
    $device_id = ctype_digit($hostname) ? $hostname : getidbyname($hostname);
642
    if (is_numeric($device_id)) {
643
        $permission = check_device_permission($device_id);
644
        if ($permission !== true) {
645
            return $permission; // permission error
646
        }
647
        $sql = ' AND `devices`.`device_id` = ?';
648
        $sql_params[] = $device_id;
649
    }
650
    if (! Auth::user()->hasGlobalRead()) {
651
        $sql .= ' AND `bgpPeers_cbgp`.`device_id` IN (SELECT device_id FROM devices_perms WHERE user_id = ?)';
652
        $sql_params[] = Auth::id();
653
    }
654
655
    $bgp_counters = dbFetchRows("SELECT `bgpPeers_cbgp`.* FROM `bgpPeers_cbgp` LEFT JOIN `devices` ON `bgpPeers_cbgp`.`device_id` = `devices`.`device_id` WHERE `bgpPeers_cbgp`.`device_id` IS NOT NULL $sql", $sql_params);
656
    $total_bgp_counters = count($bgp_counters);
657
    if ($total_bgp_counters == 0) {
658
        return api_error(404, 'BGP counters does not exist');
659
    }
660
661
    return api_success($bgp_counters, 'bgp_counters');
662
}
663
664
function list_ospf(Illuminate\Http\Request $request)
665
{
666
    $sql = '';
667
    $sql_params = [];
668
    $hostname = $request->get('hostname');
669
    $device_id = ctype_digit($hostname) ? $hostname : getidbyname($hostname);
670
    if (is_numeric($device_id)) {
671
        $sql = ' AND `device_id`=?';
672
        $sql_params = [$device_id];
673
    }
674
675
    $ospf_neighbours = dbFetchRows("SELECT * FROM ospf_nbrs WHERE `ospfNbrState` IS NOT NULL AND `ospfNbrState` != '' $sql", $sql_params);
676
    $total_ospf_neighbours = count($ospf_neighbours);
677
    if (! is_numeric($total_ospf_neighbours)) {
0 ignored issues
show
The condition is_numeric($total_ospf_neighbours) is always true.
Loading history...
678
        return api_error(500, 'Error retrieving ospf_nbrs');
679
    }
680
681
    return api_success($ospf_neighbours, 'ospf_neighbours');
682
}
683
684
function list_ospf_ports(Illuminate\Http\Request $request)
685
{
686
    $ospf_ports = OspfPort::hasAccess(Auth::user())
687
              ->get();
688
    if ($ospf_ports->isEmpty()) {
689
        return api_error(404, 'Ospf ports do not exist');
690
    }
691
692
    return api_success($ospf_ports, 'ospf_ports', null, 200, $ospf_ports->count());
693
}
694
695
function get_graph_by_portgroup(Illuminate\Http\Request $request)
696
{
697
    $group = $request->route('group');
698
    $id = $request->route('id');
699
    $vars = [
700
        'output' => $request->get('output', 'display'),
701
        'width'  => $request->get('width', 1075),
702
        'height' => $request->get('height', 300),
703
        'type'   => 'multiport_bits_separate',
704
    ];
705
    if ($request->has('from')) {
706
        $vars['from'] = $request->get('from');
707
    }
708
709
    if ($request->has('to')) {
710
        $vars['to'] = $request->get('to');
711
    }
712
713
    if (empty($id)) {
714
        $ports = get_ports_from_type(explode(',', $group));
715
        $if_list = implode(',', Arr::pluck($ports, 'port_id'));
716
    } else {
717
        $if_list = $id;
718
    }
719
    $vars['id'] = $if_list;
720
721
    return api_get_graph($vars);
722
}
723
724
function get_components(Illuminate\Http\Request $request)
725
{
726
    $hostname = $request->route('hostname');
727
728
    // Do some filtering if the user requests.
729
    $options = [];
730
    // Add the rest of the options with an equals query
731
    foreach ($request->all() as $k => $v) {
732
        $options['filter'][$k] = ['=', $v];
733
    }
734
735
    // We need to specify the label as this is a LIKE query
736
    if ($request->has('label')) {
737
        // set a label like filter
738
        $options['filter']['label'] = ['LIKE', $request->get('label')];
739
    }
740
741
    // use hostname as device_id if it's all digits
742
    $device_id = ctype_digit($hostname) ? $hostname : getidbyname($hostname);
743
744
    return check_device_permission($device_id, function ($device_id) use ($options) {
745
        $COMPONENT = new LibreNMS\Component();
746
        $components = $COMPONENT->getComponents($device_id, $options);
747
748
        return api_success($components[$device_id], 'components');
749
    });
750
}
751
752
function add_components(Illuminate\Http\Request $request)
753
{
754
    $hostname = $request->route('hostname');
755
    $ctype = $request->route('type');
756
757
    // use hostname as device_id if it's all digits
758
    $device_id = ctype_digit($hostname) ? $hostname : getidbyname($hostname);
759
    $COMPONENT = new LibreNMS\Component();
760
    $component = $COMPONENT->createComponent($device_id, $ctype);
761
762
    return api_success($component, 'components');
763
}
764
765
function edit_components(Illuminate\Http\Request $request)
766
{
767
    $hostname = $request->route('hostname');
768
    $data = json_decode($request->getContent(), true);
769
770
    // use hostname as device_id if it's all digits
771
    $device_id = ctype_digit($hostname) ? $hostname : getidbyname($hostname);
772
    $COMPONENT = new LibreNMS\Component();
773
774
    if (! $COMPONENT->setComponentPrefs($device_id, $data)) {
775
        return api_error(500, 'Components could not be edited.');
776
    }
777
778
    return api_success_noresult(200);
779
}
780
781
function delete_components(Illuminate\Http\Request $request)
782
{
783
    $cid = $request->route('component');
784
785
    $COMPONENT = new LibreNMS\Component();
786
    if ($COMPONENT->deleteComponent($cid)) {
787
        return api_success_noresult(200);
788
    } else {
789
        return api_error(500, 'Components could not be deleted.');
790
    }
791
}
792
793
function get_graphs(Illuminate\Http\Request $request)
794
{
795
    $hostname = $request->route('hostname');
796
797
    // use hostname as device_id if it's all digits
798
    $device_id = ctype_digit($hostname) ? $hostname : getidbyname($hostname);
799
800
    return check_device_permission($device_id, function ($device_id) {
801
        $graphs = [];
802
        $graphs[] = [
803
            'desc' => 'Poller Time',
804
            'name' => 'device_poller_perf',
805
        ];
806
        $graphs[] = [
807
            'desc' => 'Ping Response',
808
            'name' => 'device_ping_perf',
809
        ];
810
        foreach (dbFetchRows('SELECT * FROM device_graphs WHERE device_id = ? ORDER BY graph', [$device_id]) as $graph) {
811
            $desc = Config::get("graph_types.device.{$graph['graph']}.descr");
812
            $graphs[] = [
813
                'desc' => $desc,
814
                'name' => 'device_' . $graph['graph'],
815
            ];
816
        }
817
818
        return api_success($graphs, 'graphs');
819
    });
820
}
821
822
function trigger_device_discovery(Illuminate\Http\Request $request)
823
{
824
    // return details of a single device
825
    $hostname = $request->route('hostname');
826
827
    // use hostname as device_id if it's all digits
828
    $device_id = ctype_digit($hostname) ? $hostname : getidbyname($hostname);
829
    // find device matching the id
830
    $device = device_by_id_cache($device_id);
831
    if (! $device) {
832
        return api_error(404, "Device $hostname does not exist");
833
    }
834
835
    $ret = device_discovery_trigger($device_id);
836
837
    return api_success($ret, 'result');
838
}
839
840
function list_available_health_graphs(Illuminate\Http\Request $request)
841
{
842
    $hostname = $request->route('hostname');
843
    $device_id = ctype_digit($hostname) ? $hostname : getidbyname($hostname);
844
845
    return check_device_permission($device_id, function ($device_id) use ($request) {
846
        $input_type = $request->route('type');
847
        if ($input_type) {
848
            $type = preg_replace('/^device_/', '', $input_type);
849
        }
850
        $sensor_id = $request->route('sensor_id');
851
        $graphs = [];
852
853
        if (isset($type)) {
854
            if (isset($sensor_id)) {
855
                $graphs = dbFetchRows('SELECT * FROM `sensors` WHERE `sensor_id` = ?', [$sensor_id]);
856
            } else {
857
                foreach (dbFetchRows('SELECT `sensor_id`, `sensor_descr` FROM `sensors` WHERE `device_id` = ? AND `sensor_class` = ? AND `sensor_deleted` = 0', [$device_id, $type]) as $graph) {
858
                    $graphs[] = [
859
                        'sensor_id' => $graph['sensor_id'],
860
                        'desc' => $graph['sensor_descr'],
861
                    ];
862
                }
863
            }
864
        } else {
865
            foreach (dbFetchRows('SELECT `sensor_class` FROM `sensors` WHERE `device_id` = ? AND `sensor_deleted` = 0 GROUP BY `sensor_class`', [$device_id]) as $graph) {
866
                $graphs[] = [
867
                    'desc' => ucfirst($graph['sensor_class']),
868
                    'name' => 'device_' . $graph['sensor_class'],
869
                ];
870
            }
871
            $device = Device::find($device_id);
872
873
            if ($device) {
874
                if ($device->processors()->count() > 0) {
875
                    array_push($graphs, [
876
                        'desc' => 'Processors',
877
                        'name' => 'device_processor',
878
                    ]);
879
                }
880
881
                if ($device->storage()->count() > 0) {
882
                    array_push($graphs, [
883
                        'desc' => 'Storage',
884
                        'name' => 'device_storage',
885
                    ]);
886
                }
887
888
                if ($device->mempools()->count() > 0) {
889
                    array_push($graphs, [
890
                        'desc' => 'Memory Pools',
891
                        'name' => 'device_mempool',
892
                    ]);
893
                }
894
            }
895
        }
896
897
        return api_success($graphs, 'graphs');
898
    });
899
}
900
901
function list_available_wireless_graphs(Illuminate\Http\Request $request)
902
{
903
    $hostname = $request->route('hostname');
904
    $device_id = ctype_digit($hostname) ? $hostname : getidbyname($hostname);
905
906
    return check_device_permission($device_id, function ($device_id) use ($request) {
907
        $input_type = $request->route('type');
908
        if ($input_type) {
909
            [, , $type] = explode('_', $input_type);
910
        }
911
        $sensor_id = $request->route('sensor_id');
912
        $graphs = [];
913
914
        if (isset($type)) {
915
            if (isset($sensor_id)) {
916
                $graphs = dbFetchRows('SELECT * FROM `wireless_sensors` WHERE `sensor_id` = ?', [$sensor_id]);
917
            } else {
918
                foreach (dbFetchRows('SELECT `sensor_id`, `sensor_descr` FROM `wireless_sensors` WHERE `device_id` = ? AND `sensor_class` = ? AND `sensor_deleted` = 0', [$device_id, $type]) as $graph) {
919
                    $graphs[] = [
920
                        'sensor_id' => $graph['sensor_id'],
921
                        'desc'      => $graph['sensor_descr'],
922
                    ];
923
                }
924
            }
925
        } else {
926
            foreach (dbFetchRows('SELECT `sensor_class` FROM `wireless_sensors` WHERE `device_id` = ? AND `sensor_deleted` = 0 GROUP BY `sensor_class`', [$device_id]) as $graph) {
927
                $graphs[] = [
928
                    'desc' => ucfirst($graph['sensor_class']),
929
                    'name' => 'device_wireless_' . $graph['sensor_class'],
930
                ];
931
            }
932
        }
933
934
        return api_success($graphs, 'graphs');
935
    });
936
}
937
938
function get_port_graphs(Illuminate\Http\Request $request)
939
{
940
    $hostname = $request->route('hostname');
941
    $columns = $request->get('columns', 'ifName');
942
943
    if ($validate = validate_column_list($columns, 'ports') !== true) {
944
        return $validate;
945
    }
946
947
    // use hostname as device_id if it's all digits
948
    $device_id = ctype_digit($hostname) ? $hostname : getidbyname($hostname);
949
    $sql = '';
950
    $params = [$device_id];
951
    if (! device_permitted($device_id)) {
952
        $sql = 'AND `port_id` IN (select `port_id` from `ports_perms` where `user_id` = ?)';
953
        array_push($params, Auth::id());
954
    }
955
956
    $ports = dbFetchRows("SELECT $columns FROM `ports` WHERE `device_id` = ? AND `deleted` = '0' $sql ORDER BY `ifIndex`", $params);
957
958
    return api_success($ports, 'ports');
959
}
960
961
function get_device_ip_addresses(Illuminate\Http\Request $request)
962
{
963
    $hostname = $request->route('hostname');
964
    // use hostname as device_id if it's all digits
965
    $device_id = ctype_digit($hostname) ? $hostname : getidbyname($hostname);
966
967
    return check_device_permission($device_id, function ($device_id) {
968
        $ipv4 = dbFetchRows('SELECT `ipv4_addresses`.* FROM `ipv4_addresses` JOIN `ports` ON `ports`.`port_id`=`ipv4_addresses`.`port_id` WHERE `ports`.`device_id` = ? AND `deleted` = 0', [$device_id]);
969
        $ipv6 = dbFetchRows('SELECT `ipv6_addresses`.* FROM `ipv6_addresses` JOIN `ports` ON `ports`.`port_id`=`ipv6_addresses`.`port_id` WHERE `ports`.`device_id` = ? AND `deleted` = 0', [$device_id]);
970
        $ip_addresses_count = count(array_merge($ipv4, $ipv6));
971
        if ($ip_addresses_count == 0) {
972
            return api_error(404, "Device $device_id does not have any IP addresses");
973
        }
974
975
        return api_success(array_merge($ipv4, $ipv6), 'addresses');
976
    });
977
}
978
979
function get_port_ip_addresses(Illuminate\Http\Request $request)
980
{
981
    $port_id = $request->route('portid');
982
983
    return check_port_permission($port_id, null, function ($port_id) {
984
        $ipv4 = dbFetchRows('SELECT * FROM `ipv4_addresses` WHERE `port_id` = ?', [$port_id]);
985
        $ipv6 = dbFetchRows('SELECT * FROM `ipv6_addresses` WHERE `port_id` = ?', [$port_id]);
986
        $ip_addresses_count = count(array_merge($ipv4, $ipv6));
987
        if ($ip_addresses_count == 0) {
988
            return api_error(404, "Port $port_id does not have any IP addresses");
989
        }
990
991
        return api_success(array_merge($ipv4, $ipv6), 'addresses');
992
    });
993
}
994
995
function get_network_ip_addresses(Illuminate\Http\Request $request)
996
{
997
    $network_id = $request->route('id');
998
    $ipv4 = dbFetchRows('SELECT * FROM `ipv4_addresses` WHERE `ipv4_network_id` = ?', [$network_id]);
999
    $ipv6 = dbFetchRows('SELECT * FROM `ipv6_addresses` WHERE `ipv6_network_id` = ?', [$network_id]);
1000
    $ip_addresses_count = count(array_merge($ipv4, $ipv6));
1001
    if ($ip_addresses_count == 0) {
1002
        return api_error(404, "IP network $network_id does not exist or is empty");
1003
    }
1004
1005
    return api_success(array_merge($ipv4, $ipv6), 'addresses');
1006
}
1007
1008
function get_port_info(Illuminate\Http\Request $request)
1009
{
1010
    $port_id = $request->route('portid');
1011
1012
    return check_port_permission($port_id, null, function ($port_id) {
1013
        // use hostname as device_id if it's all digits
1014
        $port = dbFetchRows('SELECT * FROM `ports` WHERE `port_id` = ?', [$port_id]);
1015
1016
        return api_success($port, 'port');
1017
    });
1018
}
1019
1020
function search_ports(Illuminate\Http\Request $request)
1021
{
1022
    $search = $request->route('search');
1023
    $value = "%$search%";
1024
    $ports = Port::hasAccess(Auth::user())
1025
         ->select(['device_id', 'port_id', 'ifIndex', 'ifName'])
1026
         ->where('ifAlias', 'like', $value)
1027
         ->orWhere('ifDescr', 'like', $value)
1028
         ->orWhere('ifName', 'like', $value)
1029
         ->orderBy('ifName')
1030
         ->get();
1031
1032
    if ($ports->isEmpty()) {
1033
        return api_error(404, 'No ports found');
1034
    }
1035
1036
    return api_success($ports, 'ports');
1037
}
1038
1039
function get_all_ports(Illuminate\Http\Request $request)
1040
{
1041
    $columns = $request->get('columns', 'port_id, ifName');
1042
    if ($validate = validate_column_list($columns, 'ports') !== true) {
1043
        return $validate;
1044
    }
1045
1046
    $params = [];
1047
    $sql = '';
1048
    if (! Auth::user()->hasGlobalRead()) {
1049
        $sql = ' AND (device_id IN (SELECT device_id FROM devices_perms WHERE user_id = ?) OR port_id IN (SELECT port_id FROM ports_perms WHERE user_id = ?))';
1050
        array_push($params, Auth::id());
1051
        array_push($params, Auth::id());
1052
    }
1053
    $ports = dbFetchRows("SELECT $columns FROM `ports` WHERE `deleted` = 0 $sql", $params);
1054
1055
    return api_success($ports, 'ports');
1056
}
1057
1058
function get_port_stack(Illuminate\Http\Request $request)
1059
{
1060
    $hostname = $request->route('hostname');
1061
    // use hostname as device_id if it's all digits
1062
    $device_id = ctype_digit($hostname) ? $hostname : getidbyname($hostname);
1063
1064
    return check_device_permission($device_id, function ($device_id) use ($request) {
1065
        if ($request->get('valid_mappings')) {
1066
            $mappings = dbFetchRows("SELECT * FROM `ports_stack` WHERE (`device_id` = ? AND `ifStackStatus` = 'active' AND (`port_id_high` != '0' AND `port_id_low` != '0')) ORDER BY `port_id_high`", [$device_id]);
1067
        } else {
1068
            $mappings = dbFetchRows("SELECT * FROM `ports_stack` WHERE `device_id` = ? AND `ifStackStatus` = 'active' ORDER BY `port_id_high`", [$device_id]);
1069
        }
1070
1071
        return api_success($mappings, 'mappings');
1072
    });
1073
}
1074
1075
function list_alert_rules(Illuminate\Http\Request $request)
1076
{
1077
    $id = $request->route('id');
1078
    $sql = '';
1079
    $param = [];
1080
    if ($id > 0) {
1081
        $sql = 'WHERE id=?';
1082
        $param = [$id];
1083
    }
1084
1085
    $rules = dbFetchRows("SELECT * FROM `alert_rules` $sql", $param);
1086
1087
    return api_success($rules, 'rules');
1088
}
1089
1090
function list_alerts(Illuminate\Http\Request $request)
1091
{
1092
    $id = $request->route('id');
1093
1094
    $sql = 'SELECT `D`.`hostname`, `A`.*, `R`.`severity` FROM `alerts` AS `A`, `devices` AS `D`, `alert_rules` AS `R` WHERE `D`.`device_id` = `A`.`device_id` AND `A`.`rule_id` = `R`.`id` ';
1095
    $sql .= 'AND `A`.`state` IN ';
1096
    if ($request->has('state')) {
1097
        $param = explode(',', $request->get('state'));
1098
    } else {
1099
        $param = [1];
1100
    }
1101
    $sql .= dbGenPlaceholders(count($param));
1102
1103
    if ($id > 0) {
1104
        $param[] = $id;
1105
        $sql .= 'AND `A`.id=?';
1106
    }
1107
1108
    $severity = $request->get('severity');
1109
    if ($severity) {
1110
        if (in_array($severity, ['ok', 'warning', 'critical'])) {
1111
            $param[] = $severity;
1112
            $sql .= ' AND `R`.severity=?';
1113
        }
1114
    }
1115
1116
    $order = 'timestamp desc';
1117
1118
    $alert_rule = $request->get('alert_rule');
1119
    if (isset($alert_rule)) {
1120
        if (is_numeric($alert_rule)) {
1121
            $param[] = $alert_rule;
1122
            $sql .= ' AND `R`.id=?';
1123
        }
1124
    }
1125
1126
    if ($request->has('order')) {
1127
        [$sort_column, $sort_order] = explode(' ', $request->get('order'), 2);
1128
        if (($res = validate_column_list($sort_column, 'alerts')) !== true) {
1129
            return $res;
1130
        }
1131
        if (in_array($sort_order, ['asc', 'desc'])) {
1132
            $order = $request->get('order');
1133
        }
1134
    }
1135
    $sql .= ' ORDER BY A.' . $order;
1136
1137
    $alerts = dbFetchRows($sql, $param);
1138
1139
    return api_success($alerts, 'alerts');
1140
}
1141
1142
function add_edit_rule(Illuminate\Http\Request $request)
1143
{
1144
    $data = json_decode($request->getContent(), true);
1145
    if (json_last_error() || ! is_array($data)) {
1146
        return api_error(500, "We couldn't parse the provided json");
1147
    }
1148
1149
    $rule_id = $data['rule_id'];
1150
    $tmp_devices = (array) $data['devices'];
1151
    $groups = (array) $data['groups'];
1152
    $locations = (array) $data['locations'];
1153
    if (empty($tmp_devices) && ! isset($rule_id)) {
1154
        return api_error(400, 'Missing the devices or global device (-1)');
1155
    }
1156
1157
    $devices = [];
1158
    foreach ($tmp_devices as $device) {
1159
        if ($device == '-1') {
1160
            continue;
1161
        }
1162
        $devices[] = (ctype_digit($device) || is_int($device)) ? $device : getidbyname($device);
1163
    }
1164
1165
    if (isset($data['builder'])) {
1166
        // accept inline json or json as a string
1167
        $builder = is_array($data['builder']) ? json_encode($data['builder']) : $data['builder'];
1168
    } else {
1169
        $builder = $data['rule'];
1170
    }
1171
    if (empty($builder)) {
1172
        return api_error(400, 'Missing the alert builder rule');
1173
    }
1174
1175
    $name = $data['name'];
1176
    if (empty($name)) {
1177
        return api_error(400, 'Missing the alert rule name');
1178
    }
1179
1180
    $severity = $data['severity'];
1181
    $sevs = [
1182
        'ok',
1183
        'warning',
1184
        'critical',
1185
    ];
1186
    if (! in_array($severity, $sevs)) {
1187
        return api_error(400, 'Missing the severity');
1188
    }
1189
1190
    $disabled = $data['disabled'];
1191
    if ($disabled != '0' && $disabled != '1') {
1192
        $disabled = 0;
1193
    }
1194
1195
    $count = $data['count'];
1196
    $mute = $data['mute'];
1197
    $delay = $data['delay'];
1198
    $interval = $data['interval'];
1199
    $override_query = $data['override_query'];
1200
    $adv_query = $data['adv_query'];
1201
    $delay_sec = convert_delay($delay);
1202
    $interval_sec = convert_delay($interval);
1203
    if ($mute == 1) {
1204
        $mute = true;
1205
    } else {
1206
        $mute = false;
1207
    }
1208
1209
    $extra = [
1210
        'mute'  => $mute,
1211
        'count' => $count,
1212
        'delay' => $delay_sec,
1213
        'interval' => $interval_sec,
1214
        'options' => [
1215
            'override_query' => $override_query,
1216
        ],
1217
    ];
1218
    $extra_json = json_encode($extra);
1219
1220
    if ($override_query === 'on') {
1221
        $query = $adv_query;
1222
    } else {
1223
        $query = QueryBuilderParser::fromJson($builder)->toSql();
1224
        if (empty($query)) {
1225
            return api_error(500, "We couldn't parse your rule");
1226
        }
1227
    }
1228
1229
    if (! isset($rule_id)) {
1230
        if (dbFetchCell('SELECT `name` FROM `alert_rules` WHERE `name`=?', [$name]) == $name) {
1231
            return api_error(500, 'Addition failed : Name has already been used');
1232
        }
1233
    } elseif (dbFetchCell('SELECT name FROM alert_rules WHERE name=? AND id !=? ', [$name, $rule_id]) == $name) {
1234
        return api_error(500, 'Update failed : Invalid rule id');
1235
    }
1236
1237
    if (is_numeric($rule_id)) {
1238
        if (! (dbUpdate(['name' => $name, 'builder' => $builder, 'query' => $query, 'severity' => $severity, 'disabled' => $disabled, 'extra' => $extra_json], 'alert_rules', 'id=?', [$rule_id]) >= 0)) {
1239
            return api_error(500, 'Failed to update existing alert rule');
1240
        }
1241
    } elseif (! $rule_id = dbInsert(['name' => $name, 'builder' => $builder, 'query' => $query, 'severity' => $severity, 'disabled' => $disabled, 'extra' => $extra_json], 'alert_rules')) {
1242
        return api_error(500, 'Failed to create new alert rule');
1243
    }
1244
1245
    dbSyncRelationship('alert_device_map', 'rule_id', $rule_id, 'device_id', $devices);
1246
    dbSyncRelationship('alert_group_map', 'rule_id', $rule_id, 'group_id', $groups);
1247
    dbSyncRelationship('alert_location_map', 'rule_id', $rule_id, 'location_id', $locations);
1248
1249
    return api_success_noresult(200);
1250
}
1251
1252
function delete_rule(Illuminate\Http\Request $request)
1253
{
1254
    $rule_id = $request->route('id');
1255
    if (is_numeric($rule_id)) {
1256
        if (dbDelete('alert_rules', '`id` =  ? LIMIT 1', [$rule_id])) {
1257
            return api_success_noresult(200, 'Alert rule has been removed');
1258
        } else {
1259
            return api_success_noresult(200, 'No alert rule by that ID');
1260
        }
1261
    }
1262
1263
    return api_error(400, 'Invalid rule id has been provided');
1264
}
1265
1266
function ack_alert(Illuminate\Http\Request $request)
1267
{
1268
    $alert_id = $request->route('id');
1269
    $data = json_decode($request->getContent(), true);
1270
1271
    if (! is_numeric($alert_id)) {
1272
        return api_error(400, 'Invalid alert has been provided');
1273
    }
1274
1275
    $alert = dbFetchRow('SELECT note, info FROM alerts WHERE id=?', [$alert_id]);
1276
    $note = $alert['note'];
1277
    $info = json_decode($alert['info'], true);
1278
    if (! empty($note)) {
1279
        $note .= PHP_EOL;
1280
    }
1281
    $note .= date(Config::get('dateformat.long')) . ' - Ack (' . Auth::user()->username . ") {$data['note']}";
1282
    $info['until_clear'] = $data['until_clear'];
1283
    $info = json_encode($info);
1284
1285
    if (dbUpdate(['state' => 2, 'note' => $note, 'info' => $info], 'alerts', '`id` = ? LIMIT 1', [$alert_id])) {
1286
        return api_success_noresult(200, 'Alert has been acknowledged');
1287
    } else {
1288
        return api_success_noresult(200, 'No Alert by that ID');
1289
    }
1290
}
1291
1292
function unmute_alert(Illuminate\Http\Request $request)
1293
{
1294
    $alert_id = $request->route('id');
1295
    $data = json_decode($request->getContent(), true);
1296
1297
    if (! is_numeric($alert_id)) {
1298
        return api_error(400, 'Invalid alert has been provided');
1299
    }
1300
1301
    $alert = dbFetchRow('SELECT note, info FROM alerts WHERE id=?', [$alert_id]);
1302
    $note = $alert['note'];
1303
1304
    if (! empty($note)) {
1305
        $note .= PHP_EOL;
1306
    }
1307
    $note .= date(Config::get('dateformat.long')) . ' - Ack (' . Auth::user()->username . ") {$data['note']}";
1308
1309
    if (dbUpdate(['state' => 1, 'note' => $note], 'alerts', '`id` = ? LIMIT 1', [$alert_id])) {
1310
        return api_success_noresult(200, 'Alert has been unmuted');
1311
    } else {
1312
        return api_success_noresult(200, 'No alert by that ID');
1313
    }
1314
}
1315
1316
function get_inventory(Illuminate\Http\Request $request)
1317
{
1318
    $hostname = $request->route('hostname');
1319
    // use hostname as device_id if it's all digits
1320
    $device_id = ctype_digit($hostname) ? $hostname : getidbyname($hostname);
1321
1322
    return check_device_permission($device_id, function ($device_id) use ($request) {
1323
        $sql = '';
1324
        $params = [];
1325
        if ($request->get('entPhysicalClass')) {
1326
            $sql .= ' AND entPhysicalClass=?';
1327
            $params[] = $request->get('entPhysicalClass');
1328
        }
1329
1330
        if ($request->get('entPhysicalContainedIn')) {
1331
            $sql .= ' AND entPhysicalContainedIn=?';
1332
            $params[] = $request->get('entPhysicalContainedIn');
1333
        } else {
1334
            $sql .= ' AND entPhysicalContainedIn="0"';
1335
        }
1336
1337
        if (! is_numeric($device_id)) {
1338
            return api_error(400, 'Invalid device provided');
1339
        }
1340
        $sql .= ' AND `device_id`=?';
1341
        $params[] = $device_id;
1342
        $inventory = dbFetchRows("SELECT * FROM `entPhysical` WHERE 1 $sql", $params);
1343
1344
        return api_success($inventory, 'inventory');
1345
    });
1346
}
1347
1348
function get_inventory_for_device(Illuminate\Http\Request $request)
1349
{
1350
    $hostname = $request->route('hostname');
1351
    // use hostname as device_id if it's all digits
1352
    $device_id = ctype_digit($hostname) ? $hostname : getidbyname($hostname);
1353
1354
    return check_device_permission($device_id, function ($device_id) {
1355
        $params = [];
1356
        $sql = 'SELECT * FROM `entPhysical` WHERE device_id = ?';
1357
        $params[] = $device_id;
1358
        $inventory = dbFetchRows($sql, $params);
1359
1360
        return api_success($inventory, 'inventory');
1361
    });
1362
}
1363
1364
function search_oxidized(Illuminate\Http\Request $request)
1365
{
1366
    $search_in_conf_textbox = $request->route('searchstring');
1367
    $result = search_oxidized_config($search_in_conf_textbox);
1368
1369
    if (! $result) {
1370
        return api_error(404, 'Received no data from Oxidized');
1371
    } else {
1372
        return api_success($result, 'nodes');
1373
    }
1374
}
1375
1376
function get_oxidized_config(Illuminate\Http\Request $request)
1377
{
1378
    $hostname = $request->route('device_name');
1379
    $result = json_decode(file_get_contents(Config::get('oxidized.url') . '/node/fetch/' . $hostname . '?format=json'), true);
1380
    if (! $result) {
1381
        return api_error(404, 'Received no data from Oxidized');
1382
    } else {
1383
        return api_success($result, 'config');
1384
    }
1385
}
1386
1387
function list_oxidized(Illuminate\Http\Request $request)
1388
{
1389
    $return = [];
1390
    $devices = Device::query()
1391
             ->where('disabled', 0)
1392
             ->when($request->route('hostname'), function ($query, $hostname) {
1393
                 return $query->where('hostname', $hostname);
1394
             })
1395
             ->whereNotIn('type', Config::get('oxidized.ignore_types', []))
1396
             ->whereNotIn('os', Config::get('oxidized.ignore_os', []))
1397
             ->whereAttributeDisabled('override_Oxidized_disable')
1398
             ->select(['hostname', 'sysName', 'sysDescr', 'hardware', 'os', 'ip', 'location_id'])
1399
             ->get();
1400
1401
    /** @var Device $device */
1402
    foreach ($devices as $device) {
1403
        $output = [
1404
            'hostname' => $device->hostname,
1405
            'os' => $device->os,
1406
            'ip' => $device->ip,
1407
        ];
1408
1409
        // Pre-populate the group with the default
1410
        if (Config::get('oxidized.group_support') === true && ! empty(Config::get('oxidized.default_group'))) {
1411
            $output['group'] = Config::get('oxidized.default_group');
1412
        }
1413
1414
        foreach (Config::get('oxidized.maps') as $maps_column => $maps) {
1415
            // Based on Oxidized group support we can apply groups by setting group_support to true
1416
            if ($maps_column == 'group' && Config::get('oxidized.group_support', true) !== true) {
1417
                continue;
1418
            }
1419
1420
            foreach ($maps as $field_type => $fields) {
1421
                if ($field_type == 'sysname') {
1422
                    $value = $device->sysName; // fix typo in previous code forcing users to use sysname instead of sysName
1423
                } elseif ($field_type == 'location') {
1424
                    $value = $device->location->location;
1425
                } else {
1426
                    $value = $device->$field_type;
1427
                }
1428
1429
                foreach ($fields as $field) {
1430
                    if (isset($field['regex']) && preg_match($field['regex'] . 'i', $value)) {
1431
                        $output[$maps_column] = $field['value'] ?? $field[$maps_column];  // compatibility with old format
1432
                        break;
1433
                    } elseif (isset($field['match']) && $field['match'] == $value) {
1434
                        $output[$maps_column] = $field['value'] ?? $field[$maps_column]; // compatibility with old format
1435
                        break;
1436
                    }
1437
                }
1438
            }
1439
        }
1440
        //Exclude groups from being sent to Oxidized
1441
        if (in_array($output['group'], Config::get('oxidized.ignore_groups'))) {
1442
            continue;
1443
        }
1444
1445
        $return[] = $output;
1446
    }
1447
1448
    return response()->json($return, 200, [], JSON_PRETTY_PRINT);
1449
}
1450
1451
function list_bills(Illuminate\Http\Request $request)
1452
{
1453
    $bills = [];
1454
    $bill_id = $request->route('bill_id');
1455
    $bill_ref = $request->get('ref');
1456
    $bill_custid = $request->get('custid');
1457
    $period = $request->get('period');
1458
    $param = [];
1459
    $sql = '';
1460
1461
    if (! empty($bill_custid)) {
1462
        $sql .= '`bill_custid` = ?';
1463
        $param[] = $bill_custid;
1464
    } elseif (! empty($bill_ref)) {
1465
        $sql .= '`bill_ref` = ?';
1466
        $param[] = $bill_ref;
1467
    } elseif (is_numeric($bill_id)) {
1468
        $sql .= '`bill_id` = ?';
1469
        $param[] = $bill_id;
1470
    } else {
1471
        $sql = '1';
1472
    }
1473
    if (! Auth::user()->hasGlobalRead()) {
1474
        $sql .= ' AND `bill_id` IN (SELECT `bill_id` FROM `bill_perms` WHERE `user_id` = ?)';
1475
        $param[] = Auth::id();
1476
    }
1477
1478
    if ($period === 'previous') {
1479
        $select = 'SELECT bills.bill_autoadded, bills.bill_cdr, bills.bill_custid, bills.bill_day, bills.bill_name,
1480
            bills.bill_notes, bills.bill_quota, bills.bill_ref, bill_history.*, bill_history.traf_total as total_data,
1481
            bill_history.traf_in as total_data_in, bill_history.traf_out as total_data_out, bill_history.updated as bill_last_calc
1482
        ';
1483
        $query = 'FROM `bills`
1484
            INNER JOIN (SELECT bill_id, MAX(bill_hist_id) AS bill_hist_id FROM bill_history
1485
                        WHERE bill_dateto < NOW() AND bill_dateto > subdate(NOW(), 40)
1486
                        GROUP BY bill_id) qLastBills ON bills.bill_id = qLastBills.bill_id
1487
            INNER JOIN bill_history ON qLastBills.bill_hist_id = bill_history.bill_hist_id
1488
        ';
1489
    } else {
1490
        $select = "SELECT bills.*,
1491
            IF(bills.bill_type = 'cdr', bill_cdr, bill_quota) AS bill_allowed
1492
        ";
1493
        $query = "FROM `bills`\n";
1494
    }
1495
1496
    foreach (dbFetchRows("$select $query WHERE $sql ORDER BY `bill_name`", $param) as $bill) {
1497
        $rate_data = $bill;
1498
        $allowed = '';
1499
        $used = '';
1500
        $percent = '';
1501
        $overuse = '';
1502
1503
        if (strtolower($bill['bill_type']) == 'cdr') {
1504
            $allowed = \LibreNMS\Util\Number::formatSi($bill['bill_cdr'], 2, 3, '') . 'bps';
1505
            $used = \LibreNMS\Util\Number::formatSi($rate_data['rate_95th'], 2, 3, '') . 'bps';
1506
            if ($bill['bill_cdr'] > 0) {
1507
                $percent = round(($rate_data['rate_95th'] / $bill['bill_cdr']) * 100, 2);
1508
            } else {
1509
                $percent = '-';
1510
            }
1511
            $overuse = $rate_data['rate_95th'] - $bill['bill_cdr'];
1512
            $overuse = (($overuse <= 0) ? '-' : \LibreNMS\Util\Number::formatSi($overuse, 2, 3, ''));
1513
        } elseif (strtolower($bill['bill_type']) == 'quota') {
1514
            $allowed = format_bytes_billing($bill['bill_quota']);
1515
            $used = format_bytes_billing($rate_data['total_data']);
1516
            if ($bill['bill_quota'] > 0) {
1517
                $percent = round(($rate_data['total_data'] / ($bill['bill_quota'])) * 100, 2);
1518
            } else {
1519
                $percent = '-';
1520
            }
1521
            $overuse = $rate_data['total_data'] - $bill['bill_quota'];
1522
            $overuse = (($overuse <= 0) ? '-' : format_bytes_billing($overuse));
1523
        }
1524
        $bill['allowed'] = $allowed;
1525
        $bill['used'] = $used;
1526
        $bill['percent'] = $percent;
1527
        $bill['overuse'] = $overuse;
1528
1529
        $bill['ports'] = dbFetchRows('SELECT `D`.`device_id`,`P`.`port_id`,`P`.`ifName` FROM `bill_ports` AS `B`, `ports` AS `P`, `devices` AS `D` WHERE `B`.`bill_id` = ? AND `P`.`port_id` = `B`.`port_id` AND `D`.`device_id` = `P`.`device_id`', [$bill['bill_id']]);
1530
1531
        $bills[] = $bill;
1532
    }
1533
1534
    return api_success($bills, 'bills');
1535
}
1536
1537
function get_bill_graph(Illuminate\Http\Request $request)
1538
{
1539
    $bill_id = $request->route('bill_id');
1540
    $graph_type = $request->route('graph_type');
1541
    if ($graph_type == 'monthly') {
1542
        $graph_type = 'historicmonthly';
1543
    }
1544
1545
    $vars = [
1546
        'type' => "bill_$graph_type",
1547
        'id' => $bill_id,
1548
        'width' => $request->get('width', 1075),
1549
        'height' => $request->get('height', 300),
1550
    ];
1551
1552
    return check_bill_permission($bill_id, function () use ($vars) {
1553
        return api_get_graph($vars);
1554
    });
1555
}
1556
1557
function get_bill_graphdata(Illuminate\Http\Request $request)
1558
{
1559
    $bill_id = $request->route('bill_id');
1560
1561
    return check_bill_permission($bill_id, function ($bill_id) use ($request) {
1562
        $graph_type = $request->route('graph_type');
1563
        if ($graph_type == 'bits') {
1564
            $from = $request->get('from', time() - 60 * 60 * 24);
1565
            $to = $request->get('to', time());
1566
            $reducefactor = $request->get('reducefactor');
1567
1568
            $graph_data = getBillingBitsGraphData($bill_id, $from, $to, $reducefactor);
1569
        } elseif ($graph_type == 'monthly') {
1570
            $graph_data = getHistoricTransferGraphData($bill_id);
1571
        }
1572
1573
        if (! isset($graph_data)) {
1574
            return api_error(400, "Unsupported graph type $graph_type");
1575
        } else {
1576
            return api_success($graph_data, 'graph_data');
1577
        }
1578
    });
1579
}
1580
1581
function get_bill_history(Illuminate\Http\Request $request)
1582
{
1583
    $bill_id = $request->route('bill_id');
1584
1585
    return check_bill_permission($bill_id, function ($bill_id) {
1586
        $result = dbFetchRows('SELECT * FROM `bill_history` WHERE `bill_id` = ? ORDER BY `bill_datefrom` DESC LIMIT 24', [$bill_id]);
1587
1588
        return api_success($result, 'bill_history');
1589
    });
1590
}
1591
1592
function get_bill_history_graph(Illuminate\Http\Request $request)
1593
{
1594
    $bill_id = $request->route('bill_id');
1595
    $bill_hist_id = $request->route('bill_hist_id');
1596
    $graph_type = $request->route('graph_type');
1597
1598
    $vars = [
1599
        'type' => "bill_$graph_type",
1600
        'id' => $bill_id,
1601
        'bill_hist_id' => $bill_hist_id,
1602
        'width' => $request->get('width', 1075),
1603
        'height' => $request->get('height', 300),
1604
    ];
1605
1606
    switch ($graph_type) {
1607
        case 'bits':
1608
            $vars['type'] = 'bill_historicbits';
1609
            $vars['reducefactor'] = $request->get('reducefactor');
1610
            break;
1611
1612
        case 'day':
1613
        case 'hour':
1614
            $vars['imgtype'] = $graph_type;
1615
            $vars['type'] = 'bill_historictransfer';
1616
            break;
1617
1618
        default:
1619
            return api_error(400, "Unknown Graph Type $graph_type");
1620
    }
1621
1622
    return check_bill_permission($bill_id, function () use ($vars) {
1623
        return api_get_graph($vars);
1624
    });
1625
}
1626
1627
function get_bill_history_graphdata(Illuminate\Http\Request $request)
1628
{
1629
    $bill_id = $request->route('bill_id');
1630
1631
    return check_bill_permission($bill_id, function ($bill_id) use ($request) {
1632
        $bill_hist_id = $request->route('bill_hist_id');
1633
        $graph_type = $request->route('graph_type');
1634
1635
        switch ($graph_type) {
1636
            case 'bits':
1637
                $reducefactor = $request->get('reducefactor');
1638
1639
                $graph_data = getBillingHistoryBitsGraphData($bill_id, $bill_hist_id, $reducefactor);
1640
                break;
1641
            case 'day':
1642
            case 'hour':
1643
                $graph_data = getBillingBandwidthGraphData($bill_id, $bill_hist_id, null, null, $graph_type);
1644
                break;
1645
        }
1646
1647
        return ! isset($graph_data) ?
1648
               api_error(400, "Unsupported graph type $graph_type") :
1649
               api_success($graph_data, 'graph_data');
1650
    });
1651
}
1652
1653
function delete_bill(Illuminate\Http\Request $request)
1654
{
1655
    $bill_id = $request->route('bill_id');
1656
1657
    if ($bill_id < 1) {
1658
        return api_error(400, 'Could not remove bill with id ' . $bill_id . '. Invalid id');
1659
    }
1660
1661
    $res = dbDelete('bills', '`bill_id` =  ? LIMIT 1', [$bill_id]);
1662
    if ($res == 1) {
1663
        dbDelete('bill_ports', '`bill_id` =  ? ', [$bill_id]);
1664
        dbDelete('bill_data', '`bill_id` =  ? ', [$bill_id]);
1665
        dbDelete('bill_history', '`bill_id` =  ? ', [$bill_id]);
1666
        dbDelete('bill_history', '`bill_id` =  ? ', [$bill_id]);
1667
        dbDelete('bill_perms', '`bill_id` =  ? ', [$bill_id]);
1668
1669
        return api_success_noresult(200, 'Bill has been removed');
1670
    }
1671
1672
    return api_error(400, 'Could not remove bill with id ' . $bill_id);
1673
}
1674
1675
function check_bill_key_value($bill_key, $bill_value)
1676
{
1677
    $bill_types = ['quota', 'cdr'];
1678
1679
    switch ($bill_key) {
1680
        case 'bill_type':
1681
            if (! in_array($bill_value, $bill_types)) {
1682
                return api_error(400, "Invalid value for $bill_key: $bill_value. Allowed: quota,cdr");
1683
            }
1684
            break;
1685
        case 'bill_cdr':
1686
            if (! is_numeric($bill_value)) {
1687
                return api_error(400, "Invalid value for $bill_key. Must be numeric.");
1688
            }
1689
            break;
1690
        case 'bill_day':
1691
            if ($bill_value < 1 || $bill_value > 31) {
1692
                return api_error(400, "Invalid value for $bill_key. range: 1-31");
1693
            }
1694
            break;
1695
        case 'bill_quota':
1696
            if (! is_numeric($bill_value)) {
1697
                return api_error(400, "Invalid value for $bill_key. Must be numeric");
1698
            }
1699
            break;
1700
        default:
1701
    }
1702
1703
    return true;
1704
}
1705
1706
function create_edit_bill(Illuminate\Http\Request $request)
1707
{
1708
    $data = json_decode($request->getContent(), true);
1709
    if (! $data) {
1710
        return api_error(500, 'Invalid JSON data');
1711
    }
1712
    //check ports
1713
    $ports_add = null;
1714
    if (array_key_exists('ports', $data)) {
1715
        $ports_add = [];
1716
        $ports = $data['ports'];
1717
        foreach ($ports as $port_id) {
1718
            $result = dbFetchRows('SELECT port_id FROM `ports` WHERE `port_id` = ?  LIMIT 1', [$port_id]);
1719
            $result = $result[0];
1720
            if (! is_array($result) || ! array_key_exists('port_id', $result)) {
1721
                return api_error(500, 'Port ' . $port_id . ' does not exists');
1722
            }
1723
            $ports_add[] = $port_id;
1724
        }
1725
    }
1726
1727
    $bill = [];
1728
    //find existing bill for update
1729
    $bill_id = (int) $data['bill_id'];
1730
    $bills = dbFetchRows("SELECT * FROM `bills` WHERE `bill_id` = $bill_id LIMIT 1");
1731
1732
    // update existing bill
1733
    if (is_array($bills) && count($bills) == 1) {
1734
        $bill = $bills[0];
1735
1736
        foreach ($data as $bill_key => $bill_value) {
1737
            $res = check_bill_key_value($bill_key, $bill_value);
1738
            if ($res === true) {
1739
                $bill[$bill_key] = $bill_value;
1740
            } else {
1741
                return $res;
1742
            }
1743
        }
1744
        $update_data = [
1745
            'bill_name' => $bill['bill_name'],
1746
            'bill_type' => $bill['bill_type'],
1747
            'bill_cdr' => $bill['bill_cdr'],
1748
            'bill_day' => $bill['bill_day'],
1749
            'bill_quota' => $bill['bill_quota'],
1750
            'bill_custid' => $bill['bill_custid'],
1751
            'bill_ref' => $bill['bill_ref'],
1752
            'bill_notes' => $bill['bill_notes'],
1753
        ];
1754
        $update = dbUpdate($update_data, 'bills', 'bill_id=?', [$bill_id]);
1755
        if ($update === false || $update < 0) {
1756
            return api_error(500, 'Failed to update existing bill');
1757
        }
1758
    } else {
1759
        // create new bill
1760
        if (array_key_exists('bill_id', $data)) {
1761
            return api_error(500, 'Argument bill_id is not allowed on bill create (auto assigned)');
1762
        }
1763
1764
        $bill_keys = [
1765
            'bill_name',
1766
            'bill_type',
1767
            'bill_cdr',
1768
            'bill_day',
1769
            'bill_quota',
1770
            'bill_custid',
1771
            'bill_ref',
1772
            'bill_notes',
1773
        ];
1774
1775
        if ($data['bill_type'] == 'quota') {
1776
            $data['bill_cdr'] = 0;
1777
        }
1778
        if ($data['bill_type'] == 'cdr') {
1779
            $data['bill_quota'] = 0;
1780
        }
1781
1782
        $missing_keys = '';
1783
        $missing = array_diff_key(array_flip($bill_keys), $data);
1784
        if (count($missing) > 0) {
1785
            foreach ($missing as $missing_key => $dummy) {
1786
                $missing_keys .= " $missing_key";
1787
            }
1788
1789
            return api_error(500, 'Missing parameters: ' . $missing_keys);
1790
        }
1791
1792
        foreach ($bill_keys as $bill_key) {
1793
            $res = check_bill_key_value($bill_key, $data[$bill_key]);
1794
            if ($res === true) {
1795
                $bill[$bill_key] = $data[$bill_key];
1796
            } else {
1797
                return $res;
1798
            }
1799
        }
1800
1801
        $bill_id = dbInsert(
1802
            [
1803
                'bill_name' => $bill['bill_name'],
1804
                'bill_type' => $bill['bill_type'],
1805
                'bill_cdr' => $bill['bill_cdr'],
1806
                'bill_day' => $bill['bill_day'],
1807
                'bill_quota' => $bill['bill_quota'],
1808
                'bill_custid' => $bill['bill_custid'],
1809
                'bill_ref' => $bill['bill_ref'],
1810
                'bill_notes' => $bill['bill_notes'],
1811
            ],
1812
            'bills'
1813
        );
1814
1815
        if ($bill_id === null) {
1816
            return api_error(500, 'Failed to create new bill');
1817
        }
1818
    }
1819
1820
    // set previously checked ports
1821
    if (is_array($ports_add)) {
1822
        dbDelete('bill_ports', "`bill_id` =  $bill_id");
1823
        if (count($ports_add) > 0) {
1824
            foreach ($ports_add as $port_id) {
1825
                dbInsert(['bill_id' => $bill_id, 'port_id' => $port_id, 'bill_port_autoadded' => 0], 'bill_ports');
1826
            }
1827
        }
1828
    }
1829
1830
    return api_success($bill_id, 'bill_id');
1831
}
1832
1833
function update_device(Illuminate\Http\Request $request)
1834
{
1835
    $hostname = $request->route('hostname');
1836
    // use hostname as device_id if it's all digits
1837
    $device_id = ctype_digit($hostname) ? $hostname : getidbyname($hostname);
1838
    $data = json_decode($request->getContent(), true);
1839
    $bad_fields = ['device_id', 'hostname'];
1840
    if (empty($data['field'])) {
1841
        return api_error(400, 'Device field to patch has not been supplied');
1842
    } elseif (in_array($data['field'], $bad_fields)) {
1843
        return api_error(500, 'Device field is not allowed to be updated');
1844
    }
1845
1846
    if (is_array($data['field']) && is_array($data['data'])) {
1847
        foreach ($data['field'] as $tmp_field) {
1848
            if (in_array($tmp_field, $bad_fields)) {
1849
                return api_error(500, 'Device field is not allowed to be updated');
1850
            }
1851
        }
1852
        if (count($data['field']) == count($data['data'])) {
1853
            $update = [];
1854
            for ($x = 0; $x < count($data['field']); $x++) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
1855
                $update[$data['field'][$x]] = $data['data'][$x];
1856
            }
1857
            if (dbUpdate($update, 'devices', '`device_id`=?', [$device_id]) >= 0) {
1858
                return api_success_noresult(200, 'Device fields have been updated');
1859
            } else {
1860
                return api_error(500, 'Device fields failed to be updated');
1861
            }
1862
        } else {
1863
            return api_error(500, 'Device fields failed to be updated as the number of fields (' . count($data['field']) . ') does not match the supplied data (' . count($data['data']) . ')');
1864
        }
1865
    } elseif (dbUpdate([$data['field'] => $data['data']], 'devices', '`device_id`=?', [$device_id]) >= 0) {
1866
        return api_success_noresult(200, 'Device ' . $data['field'] . ' field has been updated');
1867
    } else {
1868
        return api_error(500, 'Device ' . $data['field'] . ' field failed to be updated');
1869
    }
1870
}
1871
1872
function rename_device(Illuminate\Http\Request $request)
1873
{
1874
    $hostname = $request->route('hostname');
1875
    $device_id = ctype_digit($hostname) ? $hostname : getidbyname($hostname);
1876
    $new_hostname = $request->route('new_hostname');
1877
    $new_device = getidbyname($new_hostname);
1878
1879
    if (empty($new_hostname)) {
1880
        return api_error(500, 'Missing new hostname');
1881
    } elseif ($new_device) {
1882
        return api_error(500, 'Device failed to rename, new hostname already exists');
1883
    } else {
1884
        if (renamehost($device_id, $new_hostname, 'api') == '') {
1885
            return api_success_noresult(200, 'Device has been renamed');
1886
        } else {
1887
            return api_error(500, 'Device failed to be renamed');
1888
        }
1889
    }
1890
}
1891
1892
function add_port_group(Illuminate\Http\Request $request)
1893
{
1894
    $data = json_decode($request->getContent(), true);
1895
    if (json_last_error() || ! is_array($data)) {
1896
        return api_error(400, "We couldn't parse the provided json. " . json_last_error_msg());
1897
    }
1898
1899
    $rules = [
1900
        'name' => 'required|string|unique:port_groups',
1901
    ];
1902
1903
    $v = Validator::make($data, $rules);
1904
    if ($v->fails()) {
1905
        return api_error(422, $v->messages());
1906
    }
1907
1908
    $portGroup = PortGroup::make(['name' => $data['name'], 'desc' => $data['desc']]);
1909
    $portGroup->save();
1910
1911
    return api_success($portGroup->id, 'id', 'Port group ' . $portGroup->name . ' created', 201);
1912
}
1913
1914
function get_port_groups(Illuminate\Http\Request $request)
1915
{
1916
    $query = PortGroup::query();
1917
1918
    $groups = $query->orderBy('name')->get();
1919
1920
    if ($groups->isEmpty()) {
1921
        return api_error(404, 'No port groups found');
1922
    }
1923
1924
    return api_success($groups->makeHidden('pivot')->toArray(), 'groups', 'Found ' . $groups->count() . ' port groups');
1925
}
1926
1927
function add_device_group(Illuminate\Http\Request $request)
1928
{
1929
    $data = json_decode($request->getContent(), true);
1930
    if (json_last_error() || ! is_array($data)) {
1931
        return api_error(400, "We couldn't parse the provided json. " . json_last_error_msg());
1932
    }
1933
1934
    $rules = [
1935
        'name' => 'required|string|unique:device_groups',
1936
        'type' => 'required|in:dynamic,static',
1937
        'devices' => 'array|required_if:type,static',
1938
        'devices.*' => 'integer',
1939
        'rules' => 'json|required_if:type,dynamic',
1940
    ];
1941
1942
    $v = Validator::make($data, $rules);
1943
    if ($v->fails()) {
1944
        return api_error(422, $v->messages());
1945
    }
1946
1947
    // Only use the rules if they are able to be parsed by the QueryBuilder
1948
    $query = QueryBuilderParser::fromJson($data['rules'])->toSql();
1949
    if (empty($query)) {
1950
        return api_error(500, "We couldn't parse your rule");
1951
    }
1952
1953
    $deviceGroup = DeviceGroup::make(['name' => $data['name'], 'type' => $data['type'], 'desc' => $data['desc']]);
1954
    $deviceGroup->rules = json_decode($data['rules']);
1955
    $deviceGroup->save();
1956
1957
    if ($data['type'] == 'static') {
1958
        $deviceGroup->devices()->sync($data['devices']);
1959
    }
1960
1961
    return api_success($deviceGroup->id, 'id', 'Device group ' . $deviceGroup->name . ' created', 201);
1962
}
1963
1964
function get_device_groups(Illuminate\Http\Request $request)
1965
{
1966
    $hostname = $request->route('hostname');
1967
1968
    if ($hostname) {
1969
        $device = ctype_digit($hostname) ? Device::find($hostname) : Device::findByHostname($hostname);
1970
        if (is_null($device)) {
1971
            return api_error(404, 'Device not found');
1972
        }
1973
        $query = $device->groups();
1974
    } else {
1975
        $query = DeviceGroup::query();
1976
    }
1977
1978
    $groups = $query->hasAccess(Auth::user())->orderBy('name')->get();
1979
1980
    if ($groups->isEmpty()) {
1981
        return api_error(404, 'No device groups found');
1982
    }
1983
1984
    return api_success($groups->makeHidden('pivot')->toArray(), 'groups', 'Found ' . $groups->count() . ' device groups');
1985
}
1986
1987
function get_devices_by_group(Illuminate\Http\Request $request)
1988
{
1989
    $name = $request->route('name');
1990
    if (! $name) {
1991
        return api_error(400, 'No device group name provided');
1992
    }
1993
1994
    $device_group = ctype_digit($name) ? DeviceGroup::find($name) : DeviceGroup::where('name', $name)->first();
1995
1996
    if (empty($device_group)) {
1997
        return api_error(404, 'Device group not found');
1998
    }
1999
2000
    $devices = $device_group->devices()->get($request->get('full') ? ['*'] : ['devices.device_id']);
2001
2002
    if ($devices->isEmpty()) {
2003
        return api_error(404, 'No devices found in group ' . $name);
2004
    }
2005
2006
    return api_success($devices->makeHidden('pivot')->toArray(), 'devices');
2007
}
2008
2009
function list_vrf(Illuminate\Http\Request $request)
2010
{
2011
    $sql = '';
2012
    $sql_params = [];
2013
    $hostname = $request->get('hostname');
2014
    $vrfname = $request->get('vrfname');
2015
    $device_id = ctype_digit($hostname) ? $hostname : getidbyname($hostname);
2016
    if (is_numeric($device_id)) {
2017
        $permission = check_device_permission($device_id);
2018
        if ($permission !== true) {
2019
            return $permission;
2020
        }
2021
        $sql = ' AND `devices`.`device_id`=?';
2022
        $sql_params = [$device_id];
2023
    }
2024
    if (! empty($vrfname)) {
2025
        $sql = '  AND `vrfs`.`vrf_name`=?';
2026
        $sql_params = [$vrfname];
2027
    }
2028
    if (! Auth::user()->hasGlobalRead()) {
2029
        $sql .= ' AND `vrfs`.`device_id` IN (SELECT device_id FROM devices_perms WHERE user_id = ?)';
2030
        $sql_params[] = Auth::id();
2031
    }
2032
2033
    $vrfs = dbFetchRows("SELECT `vrfs`.* FROM `vrfs` LEFT JOIN `devices` ON `vrfs`.`device_id` = `devices`.`device_id` WHERE `vrfs`.`vrf_name` IS NOT NULL $sql", $sql_params);
2034
    $total_vrfs = count($vrfs);
2035
    if ($total_vrfs == 0) {
2036
        return api_error(404, 'VRFs do not exist');
2037
    }
2038
2039
    return api_success($vrfs, 'vrfs');
2040
}
2041
2042
function get_vrf(Illuminate\Http\Request $request)
2043
{
2044
    $vrfId = $request->route('id');
2045
    if (! is_numeric($vrfId)) {
2046
        return api_error(400, 'Invalid id has been provided');
2047
    }
2048
2049
    $vrf = dbFetchRows('SELECT * FROM `vrfs` WHERE `vrf_id` IS NOT NULL AND `vrf_id` = ?', [$vrfId]);
2050
    $vrf_count = count($vrf);
2051
    if ($vrf_count == 0) {
2052
        return api_error(404, "VRF $vrfId does not exist");
2053
    }
2054
2055
    return api_success($vrf, 'vrf');
2056
}
2057
2058
function list_ipsec(Illuminate\Http\Request $request)
2059
{
2060
    $hostname = $request->route('hostname');
2061
    // use hostname as device_id if it's all digits
2062
    $device_id = ctype_digit($hostname) ? $hostname : getidbyname($hostname);
2063
    if (! is_numeric($device_id)) {
2064
        return api_error(400, 'No valid hostname or device ID provided');
2065
    }
2066
2067
    $ipsec = dbFetchRows('SELECT `D`.`hostname`, `I`.* FROM `ipsec_tunnels` AS `I`, `devices` AS `D` WHERE `I`.`device_id`=? AND `D`.`device_id` = `I`.`device_id`', [$device_id]);
2068
2069
    return api_success($ipsec, 'ipsec');
2070
}
2071
2072
function list_vlans(Illuminate\Http\Request $request)
2073
{
2074
    $sql = '';
2075
    $sql_params = [];
2076
    $hostname = $request->get('hostname');
2077
    $device_id = ctype_digit($hostname) ? $hostname : getidbyname($hostname);
2078
    if (is_numeric($device_id)) {
2079
        $permission = check_device_permission($device_id);
2080
        if ($permission !== true) {
2081
            return $permission;
2082
        }
2083
        $sql = ' AND `devices`.`device_id` = ?';
2084
        $sql_params[] = $device_id;
2085
    }
2086
    if (! Auth::user()->hasGlobalRead()) {
2087
        $sql .= ' AND `vlans`.`device_id` IN (SELECT device_id FROM devices_perms WHERE user_id = ?)';
2088
        $sql_params[] = Auth::id();
2089
    }
2090
2091
    $vlans = dbFetchRows("SELECT `vlans`.* FROM `vlans` LEFT JOIN `devices` ON `vlans`.`device_id` = `devices`.`device_id` WHERE `vlans`.`vlan_vlan` IS NOT NULL $sql", $sql_params);
2092
    $vlans_count = count($vlans);
2093
    if ($vlans_count == 0) {
2094
        return api_error(404, 'VLANs do not exist');
2095
    }
2096
2097
    return api_success($vlans, 'vlans');
2098
}
2099
2100
function list_links(Illuminate\Http\Request $request)
2101
{
2102
    $hostname = $request->route('hostname');
2103
    $sql = '';
2104
    $sql_params = [];
2105
2106
    $device_id = ctype_digit($hostname) ? $hostname : getidbyname($hostname);
2107
    if (is_numeric($device_id)) {
2108
        $permission = check_device_permission($device_id);
2109
        if ($permission !== true) {
2110
            return $permission;
2111
        }
2112
        $sql = ' AND `links`.`local_device_id`=?';
2113
        $sql_params = [$device_id];
2114
    }
2115
    if (! Auth::user()->hasGlobalRead()) {
2116
        $sql .= ' AND `links`.`local_device_id` IN (SELECT device_id FROM devices_perms WHERE user_id = ?)';
2117
        $sql_params[] = Auth::id();
2118
    }
2119
    $links = dbFetchRows("SELECT `links`.* FROM `links` LEFT JOIN `devices` ON `links`.`local_device_id` = `devices`.`device_id` WHERE `links`.`id` IS NOT NULL $sql", $sql_params);
2120
    $total_links = count($links);
2121
    if ($total_links == 0) {
2122
        return api_error(404, 'Links do not exist');
2123
    }
2124
2125
    return api_success($links, 'links');
2126
}
2127
2128
function get_link(Illuminate\Http\Request $request)
2129
{
2130
    $linkId = $request->route('id');
2131
    if (! is_numeric($linkId)) {
2132
        return api_error(400, 'Invalid id has been provided');
2133
    }
2134
2135
    $link = dbFetchRows('SELECT * FROM `links` WHERE `id` IS NOT NULL AND `id` = ?', [$linkId]);
2136
    $link_count = count($link);
2137
    if ($link_count == 0) {
2138
        return api_error(404, "Link $linkId does not exist");
2139
    }
2140
2141
    return api_success($link, 'link');
2142
}
2143
2144
function get_fdb(Illuminate\Http\Request $request)
2145
{
2146
    $hostname = $request->route('hostname');
2147
2148
    if (empty($hostname)) {
2149
        return api_error(500, 'No hostname has been provided');
2150
    }
2151
2152
    $device_id = ctype_digit($hostname) ? $hostname : getidbyname($hostname);
2153
    $device = null;
2154
    if ($device_id) {
2155
        // save the current details for returning to the client on successful delete
2156
        $device = Device::find($device_id);
2157
    }
2158
2159
    if (! $device) {
2160
        return api_error(404, "Device $hostname not found");
2161
    }
2162
2163
    return check_device_permission($device_id, function () use ($device) {
2164
        if ($device) {
2165
            $fdb = $device->portsFdb;
2166
2167
            return api_success($fdb, 'ports_fdb');
2168
        }
2169
2170
        return api_error(404, 'Device does not exist');
2171
    });
2172
}
2173
2174
function list_fdb(Illuminate\Http\Request $request)
2175
{
2176
    $mac = $request->route('mac');
2177
2178
    $fdb = PortsFdb::hasAccess(Auth::user())
2179
           ->when(! empty($mac), function (Builder $query) use ($mac) {
2180
               return $query->where('mac_address', $mac);
2181
           })
2182
           ->get();
2183
2184
    if ($fdb->isEmpty()) {
2185
        return api_error(404, 'Fdb do not exist');
2186
    }
2187
2188
    return api_success($fdb, 'ports_fdb');
2189
}
2190
2191
function list_sensors()
2192
{
2193
    $sensors = Sensor::hasAccess(Auth::user())->get();
2194
    $total_sensors = $sensors->count();
2195
    if ($total_sensors == 0) {
2196
        return api_error(404, 'Sensors do not exist');
2197
    }
2198
2199
    return api_success($sensors, 'sensors');
2200
}
2201
2202
function list_ip_addresses()
2203
{
2204
    $ipv4_addresses = dbFetchRows('SELECT * FROM `ipv4_addresses`');
2205
    $ipv6_addresses = dbFetchRows('SELECT * FROM `ipv6_addresses`');
2206
    $ip_addresses_count = count(array_merge($ipv4_addresses, $ipv6_addresses));
2207
    if ($ip_addresses_count == 0) {
2208
        return api_error(404, 'IP addresses do not exist');
2209
    }
2210
2211
    return api_success(array_merge($ipv4_addresses, $ipv6_addresses), 'ip_addresses');
2212
}
2213
2214
function list_ip_networks()
2215
{
2216
    $ipv4_networks = dbFetchRows('SELECT * FROM `ipv4_networks`');
2217
    $ipv6_networks = dbFetchRows('SELECT * FROM `ipv6_networks`');
2218
    $ip_networks_count = count(array_merge($ipv4_networks, $ipv6_networks));
2219
    if ($ip_networks_count == 0) {
2220
        return api_error(404, 'IP networks do not exist');
2221
    }
2222
2223
    return api_success(array_merge($ipv4_networks, $ipv6_networks), 'ip_networks');
2224
}
2225
2226
function list_arp(Illuminate\Http\Request $request)
2227
{
2228
    $query = $request->route('query');
2229
    $cidr = $request->route('cidr');
2230
    $hostname = $request->get('device');
2231
2232
    if (empty($query)) {
2233
        return api_error(400, 'No valid IP/MAC provided');
2234
    } elseif ($query === 'all' && empty($hostname)) {
2235
        return api_error(400, 'Device argument is required when requesting all entries');
2236
    }
2237
2238
    if ($query === 'all') {
2239
        $device_id = ctype_digit($hostname) ? $hostname : getidbyname($hostname);
2240
        $arp = dbFetchRows('SELECT `ipv4_mac`.* FROM `ipv4_mac` LEFT JOIN `ports` ON `ipv4_mac`.`port_id` = `ports`.`port_id` WHERE `ports`.`device_id` = ?', [$device_id]);
2241
    } elseif ($cidr) {
2242
        try {
2243
            $ip = new IPv4("$query/$cidr");
2244
            $arp = dbFetchRows(
2245
                'SELECT * FROM `ipv4_mac` WHERE (inet_aton(`ipv4_address`) & ?) = ?',
2246
                [ip2long($ip->getNetmask()), ip2long($ip->getNetworkAddress())]
2247
            );
2248
        } catch (InvalidIpException $e) {
2249
            return api_error(400, 'Invalid Network Address');
2250
        }
2251
    } elseif (filter_var($query, FILTER_VALIDATE_MAC)) {
2252
        $mac = \LibreNMS\Util\Rewrite::macToHex($query);
2253
        $arp = dbFetchRows('SELECT * FROM `ipv4_mac` WHERE `mac_address`=?', [$mac]);
2254
    } else {
2255
        $arp = dbFetchRows('SELECT * FROM `ipv4_mac` WHERE `ipv4_address`=?', [$query]);
2256
    }
2257
2258
    return api_success($arp, 'arp');
2259
}
2260
2261
function list_services(Illuminate\Http\Request $request)
2262
{
2263
    $where = [];
2264
    $params = [];
2265
2266
    // Filter by State
2267
    if ($request->has('state')) {
2268
        $where[] = '`service_status`=?';
2269
        $params[] = $request->get('state');
2270
        $where[] = "`service_disabled`='0'";
2271
        $where[] = "`service_ignore`='0'";
2272
2273
        if (! is_numeric($request->get('state'))) {
2274
            return api_error(400, 'No valid service state provided, valid option is 0=Ok, 1=Warning, 2=Critical');
2275
        }
2276
    }
2277
2278
    //Filter by Type
2279
    if ($request->has('type')) {
2280
        $where[] = '`service_type` LIKE ?';
2281
        $params[] = $request->get('type');
2282
    }
2283
2284
    //GET by Host
2285
    $hostname = $request->route('hostname');
2286
    if ($hostname) {
2287
        $device_id = ctype_digit($hostname) ? $hostname : getidbyname($hostname);
2288
        $where[] = '`device_id` = ?';
2289
        $params[] = $device_id;
2290
2291
        if (! is_numeric($device_id)) {
2292
            return api_error(500, 'No valid hostname or device id provided');
2293
        }
2294
    }
2295
2296
    $query = 'SELECT * FROM `services`';
2297
2298
    if (! empty($where)) {
2299
        $query .= ' WHERE ' . implode(' AND ', $where);
2300
    }
2301
    $query .= ' ORDER BY `service_ip`';
2302
    $services = [dbFetchRows($query, $params)]; // double array for backwards compat :(
2303
2304
    return api_success($services, 'services');
2305
}
2306
2307
function list_logs(Illuminate\Http\Request $request, Router $router)
2308
{
2309
    $type = $router->current()->getName();
2310
    $hostname = $request->route('hostname');
2311
    $device_id = ctype_digit($hostname) ? $hostname : getidbyname($hostname);
2312
2313
    $count_query = 'SELECT COUNT(*)';
2314
    $param = [];
2315
    if ($type === 'list_eventlog') {
2316
        $query = ' FROM eventlog LEFT JOIN `devices` ON `eventlog`.`device_id`=`devices`.`device_id` WHERE 1';
2317
        $full_query = 'SELECT `devices`.`hostname`, `devices`.`sysName`, `eventlog`.`device_id` as `host`, `eventlog`.*'; // inject host for backward compat
2318
        $timestamp = 'datetime';
2319
        $id_field = 'event_id';
2320
    } elseif ($type === 'list_syslog') {
2321
        $query = ' FROM syslog LEFT JOIN `devices` ON `syslog`.`device_id`=`devices`.`device_id` WHERE 1';
2322
        $full_query = 'SELECT `devices`.`hostname`, `devices`.`sysName`, `syslog`.*';
2323
        $timestamp = 'timestamp';
2324
        $id_field = 'seq';
2325
    } elseif ($type === 'list_alertlog') {
2326
        $query = ' FROM alert_log LEFT JOIN `devices` ON `alert_log`.`device_id`=`devices`.`device_id` WHERE 1';
2327
        $full_query = 'SELECT `devices`.`hostname`, `devices`.`sysName`, `alert_log`.*';
2328
        $timestamp = 'time_logged';
2329
        $id_field = 'id';
2330
    } elseif ($type === 'list_authlog') {
2331
        $query = ' FROM authlog WHERE 1';
2332
        $full_query = 'SELECT `authlog`.*';
2333
        $timestamp = 'datetime';
2334
        $id_field = 'id';
2335
    } else {
2336
        $query = ' FROM eventlog LEFT JOIN `devices` ON `eventlog`.`device_id`=`devices`.`device_id` WHERE 1';
2337
        $full_query = 'SELECT `devices`.`hostname`, `devices`.`sysName`, `eventlog`.*';
2338
        $timestamp = 'datetime';
2339
    }
2340
2341
    $start = (int) $request->get('start', 0);
2342
    $limit = (int) $request->get('limit', 50);
2343
    $from = $request->get('from');
2344
    $to = $request->get('to');
2345
2346
    if (is_numeric($device_id)) {
2347
        $query .= ' AND `devices`.`device_id` = ?';
2348
        $param[] = $device_id;
2349
    }
2350
2351
    if ($from) {
2352
        if (is_numeric($from)) {
2353
            $query .= " AND $id_field >= ?";
2354
        } else {
2355
            $query .= " AND $timestamp >= ?";
2356
        }
2357
        $param[] = $from;
2358
    }
2359
2360
    if ($to) {
2361
        if (is_numeric($to)) {
2362
            $query .= " AND $id_field <= ?";
2363
        } else {
2364
            $query .= " AND $timestamp <= ?";
2365
        }
2366
        $param[] = $to;
2367
    }
2368
2369
    $count_query = $count_query . $query;
2370
    $count = dbFetchCell($count_query, $param);
2371
    $full_query = $full_query . $query . " ORDER BY $timestamp ASC LIMIT $start,$limit";
2372
    $logs = dbFetchRows($full_query, $param);
2373
2374
    if ($type === 'list_alertlog') {
2375
        foreach ($logs as $index => $log) {
2376
            $logs[$index]['details'] = json_decode(gzuncompress($log['details']), true);
2377
        }
2378
    }
2379
2380
    return api_success($logs, 'logs', null, 200, null, ['total' => $count]);
2381
}
2382
2383
function validate_column_list($columns, $tableName)
2384
{
2385
    static $schema;
2386
    if (is_null($schema)) {
2387
        $schema = new \LibreNMS\DB\Schema();
2388
    }
2389
2390
    $column_names = is_array($columns) ? $columns : explode(',', $columns);
2391
    $valid_columns = $schema->getColumns($tableName);
2392
    $invalid_columns = array_diff(array_map('trim', $column_names), $valid_columns);
2393
2394
    if (count($invalid_columns) > 0) {
2395
        return api_error(400, 'Invalid columns: ' . join(',', $invalid_columns));
2396
    }
2397
2398
    return true;
2399
}
2400
2401
function missing_fields($required_fields, $data)
2402
{
2403
    foreach ($required_fields as $required) {
2404
        if (empty($data[$required])) {
2405
            return true;
2406
        }
2407
    }
2408
2409
    return false;
2410
}
2411
2412
function add_service_template_for_device_group(Illuminate\Http\Request $request)
2413
{
2414
    $data = json_decode($request->getContent(), true);
2415
    if (json_last_error() || ! is_array($data)) {
2416
        return api_error(400, "We couldn't parse the provided json. " . json_last_error_msg());
2417
    }
2418
2419
    $rules = [
2420
        'name' => 'required|string|unique:service_templates',
2421
        'device_group_id' => 'integer',
2422
        'type' => 'string',
2423
        'param' => 'nullable|string',
2424
        'ip' => 'nullable|string',
2425
        'desc' => 'nullable|string',
2426
        'changed' => 'integer',
2427
        'disabled' => 'integer',
2428
        'ignore' => 'integer',
2429
    ];
2430
2431
    $v = Validator::make($data, $rules);
2432
    if ($v->fails()) {
2433
        return api_error(422, $v->messages());
2434
    }
2435
2436
    // Only use the rules if they are able to be parsed by the QueryBuilder
2437
    $query = QueryBuilderParser::fromJson($data['rules'])->toSql();
2438
    if (empty($query)) {
2439
        return api_error(500, "We couldn't parse your rule");
2440
    }
2441
2442
    $serviceTemplate = ServiceTemplate::make(['name' => $data['name'], 'device_group_id' => $data['device_group_id'], 'type' => $data['type'], 'param' => $data['param'], 'ip' => $data['ip'], 'desc' => $data['desc'], 'changed' => $data['changed'], 'disabled' => $data['disabled'], 'ignore' => $data['ignore']]);
2443
    $serviceTemplate->save();
2444
2445
    return api_success($serviceTemplate->id, 'id', 'Service Template ' . $serviceTemplate->name . ' created', 201);
2446
}
2447
2448
function get_service_templates(Illuminate\Http\Request $request)
2449
{
2450
    if ($request->user()->cannot('viewAny', ServiceTemplate::class)) {
2451
        return api_error(403, 'Insufficient permissions to access service templates');
2452
    }
2453
2454
    $templates = ServiceTemplate::query()->orderBy('name')->get();
2455
2456
    if ($templates->isEmpty()) {
2457
        return api_error(404, 'No service templates found');
2458
    }
2459
2460
    return api_success($templates->makeHidden('pivot')->toArray(), 'templates', 'Found ' . $templates->count() . ' service templates');
2461
}
2462
2463
function add_service_for_host(Illuminate\Http\Request $request)
2464
{
2465
    $hostname = $request->route('hostname');
2466
    $device_id = ctype_digit($hostname) ? $hostname : getidbyname($hostname);
2467
    $data = json_decode($request->getContent(), true);
2468
    if (missing_fields(['type'], $data)) {
2469
        return api_error(400, 'Required fields missing (hostname and type needed)');
2470
    }
2471
    if (! in_array($data['type'], list_available_services())) {
2472
        return api_error(400, 'The service ' . $data['type'] . " does not exist.\n Available service types: " . implode(', ', list_available_services()));
2473
    }
2474
    $service_type = $data['type'];
2475
    $service_ip = $data['ip'];
2476
    $service_desc = $data['desc'] ? $data['desc'] : '';
2477
    $service_param = $data['param'] ? $data['param'] : '';
2478
    $service_ignore = $data['ignore'] ? true : false; // Default false
2479
    $service_disable = $data['disable'] ? true : false; // Default false
2480
    $service_name = $data['name'];
2481
    $service_id = add_service($device_id, $service_type, $service_desc, $service_ip, $service_param, (int) $service_ignore, (int) $service_disable, 0, $service_name);
2482
    if ($service_id != false) {
2483
        return api_success_noresult(201, "Service $service_type has been added to device $hostname (#$service_id)");
2484
    }
2485
2486
    return api_error(500, 'Failed to add the service');
2487
}
2488
2489
function add_parents_to_host(Illuminate\Http\Request $request)
2490
{
2491
    $data = json_decode($request->getContent(), true);
2492
    $device_id = $request->route('id');
2493
    $device_id = ctype_digit($device_id) ? $device_id : getidbyname($device_id);
2494
2495
    $parent_ids = [];
2496
    foreach (explode(',', $data['parent_ids']) as $hostname) {
2497
        $hostname = trim($hostname);
2498
        $parent_id = ctype_digit($hostname) ? $hostname : getidbyname($hostname);
2499
        if (empty($parent_id)) {
2500
            return api_error(400, 'Parent device IDs/Hostname does not exist: ' . $hostname);
2501
        }
2502
        $parent_ids[] = $parent_id;
2503
    }
2504
2505
    if (validateDeviceIds($parent_ids) && validateDeviceIds([$device_id]) && (! in_array($device_id, $parent_ids))) {
2506
        Device::find($device_id)->parents()->sync($parent_ids);
2507
2508
        return api_success_noresult(201, 'Device dependencies have been saved');
2509
    }
2510
2511
    return api_error(400, 'Check your parent and device IDs');
2512
}
2513
2514
function del_parents_from_host(Illuminate\Http\Request $request)
2515
{
2516
    $device_id = $request->route('id');
2517
    $device_id = ctype_digit($device_id) ? $device_id : getidbyname($device_id);
2518
    $data = json_decode($request->getContent(), true);
2519
    if (! validateDeviceIds([$device_id])) {
2520
        return api_error(400, 'Check your device ID!');
2521
    }
2522
    $device = Device::find($device_id);
2523
    if (! empty($data['parent_ids'])) {
2524
        foreach (explode(',', $data['parent_ids']) as $hostname) {
2525
            $hostname = trim($hostname);
2526
            $parent_id = ctype_digit($hostname) ? $hostname : getidbyname($hostname);
2527
            if (empty($parent_id)) {
2528
                return api_error(400, 'Parent device IDs/Hostname does not exist: ' . $hostname);
2529
            }
2530
            $parent_ids[] = $parent_id;
2531
        }
2532
2533
        //remove parents included in the request if they are valid device ids
2534
        $result = validateDeviceIds($parent_ids) ? $device->parents()->detach($parent_ids) : false;
2535
    }
2536
    if (is_null($result)) {
2537
        //$result doesn't exist so $data['parent_ids'] is empty
2538
        $result = $device->parents()->detach(); //remove all parents
2539
    }
2540
    if ($result) {
2541
        return api_success_noresult(201, 'All device dependencies have been removed');
2542
    }
2543
2544
    return api_error(400, 'Device dependency cannot be deleted check device and parents ids');
2545
}
2546
2547
function validateDeviceIds($ids)
2548
{
2549
    foreach ($ids as $id) {
2550
        $invalidId = ! is_numeric($id) || $id < 1 || is_null(Device::find($id));
2551
        if ($invalidId) {
2552
            return false;
2553
        }
2554
    }
2555
2556
    return true;
2557
}
2558
2559
function add_location(Illuminate\Http\Request $request)
2560
{
2561
    $data = json_decode($request->getContent(), true);
2562
    if (missing_fields(['location', 'lat', 'lng'], $data)) {
2563
        return api_error(400, 'Required fields missing (location, lat and lng needed)');
2564
    }
2565
    // Set the location
2566
    $timestamp = date('Y-m-d H:m:s');
2567
    $insert = ['location' => $data['location'], 'lat' => $data['lat'], 'lng' => $data['lng'], 'timestamp' => $timestamp];
2568
    $location_id = dbInsert($insert, 'locations');
2569
    if ($location_id != false) {
2570
        return api_success_noresult(201, "Location added with id #$location_id");
2571
    }
2572
2573
    return api_error(500, 'Failed to add the location');
2574
}
2575
2576
function edit_location(Illuminate\Http\Request $request)
2577
{
2578
    $location = $request->route('location_id_or_name');
2579
    if (empty($location)) {
2580
        return api_error(400, 'No location has been provided to edit');
2581
    }
2582
    $location_id = ctype_digit($location) ? $location : get_location_id_by_name($location);
2583
    $data = json_decode($request->getContent(), true);
2584
    if (empty($location_id)) {
2585
        return api_error(400, 'Failed to delete location');
2586
    }
2587
    $result = dbUpdate($data, 'locations', '`id` = ?', [$location_id]);
2588
    if ($result == 1) {
2589
        return api_success_noresult(201, 'Location updated successfully');
2590
    }
2591
2592
    return api_error(500, 'Failed to update location');
2593
}
2594
2595
function get_location_id_by_name($location)
2596
{
2597
    return dbFetchCell('SELECT id FROM locations WHERE location = ?', $location);
2598
}
2599
2600
function del_location(Illuminate\Http\Request $request)
2601
{
2602
    $location = $request->route('location');
2603
    if (empty($location)) {
2604
        return api_error(400, 'No location has been provided to delete');
2605
    }
2606
    $location_id = get_location_id_by_name($location);
2607
    if (empty($location_id)) {
2608
        return api_error(400, "Failed to delete $location (Does not exists)");
2609
    }
2610
    $data = [
2611
        'location_id' => 0,
2612
    ];
2613
    dbUpdate($data, 'devices', '`location_id` = ?', [$location_id]);
2614
    $result = dbDelete('locations', '`location` = ? ', [$location]);
2615
    if ($result == 1) {
2616
        return api_success_noresult(201, "Location $location has been deleted successfully");
2617
    }
2618
2619
    return api_error(500, "Failed to delete the location $location");
2620
}
2621
2622
function del_service_from_host(Illuminate\Http\Request $request)
2623
{
2624
    $service_id = $request->route('id');
2625
    if (empty($service_id)) {
2626
        return api_error(400, 'No service_id has been provided to delete');
2627
    }
2628
    $result = delete_service($service_id);
2629
    if ($result == 1) {
2630
        return api_success_noresult(201, 'Service has been deleted successfully');
2631
    }
2632
2633
    return api_error(500, 'Failed to delete the service');
2634
}
2635
2636
function search_by_mac(Illuminate\Http\Request $request)
2637
{
2638
    $macAddress = Rewrite::macToHex((string) $request->route('search'));
2639
2640
    $rules = [
2641
        'macAddress' => 'required|string|regex:/^[0-9a-fA-F]{12}$/',
2642
    ];
2643
2644
    $validate = Validator::make(['macAddress' => $macAddress], $rules);
2645
    if ($validate->fails()) {
2646
        return api_error(422, $validate->messages());
2647
    }
2648
2649
    $ports = Port::whereHas('fdbEntries', function ($fdbDownlink) use ($macAddress) {
2650
        $fdbDownlink->where('mac_address', $macAddress);
2651
    })
2652
         ->withCount('fdbEntries')
2653
         ->orderBy('fdb_entries_count')
2654
         ->get();
2655
2656
    if ($ports->count() == 0) {
2657
        return api_error(404, 'mac not found');
2658
    }
2659
2660
    if ($request->has('filter') && $request->get('filter') === 'first') {
2661
        return  api_success($ports->first(), 'ports');
2662
    }
2663
2664
    return api_success($ports, 'ports');
2665
}
2666
function edit_service_for_host(Illuminate\Http\Request $request)
2667
{
2668
    $service_id = $request->route('id');
2669
    $data = json_decode($request->getContent(), true);
2670
    if (edit_service($data, $service_id) == 1) {
2671
        return api_success_noresult(201, 'Service updated successfully');
2672
    }
2673
2674
    return api_error(500, "Failed to update the service with id $service_id");
2675
}
2676
2677
/**
2678
 * Display Librenms Instance Info
2679
 */
2680
function server_info()
2681
{
2682
    $versions = version_info();
2683
2684
    return api_success([
2685
        $versions,
2686
    ], 'system');
2687
}
2688