librenms/includes/functions.php

1728 lines
55 KiB
PHP
Raw Normal View History

<?php
/**
* LibreNMS
*
* This file is part of LibreNMS.
*
* @copyright (C) 2006 - 2012 Adam Armstrong
*/
use App\Models\Device;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Str;
use LibreNMS\Config;
use LibreNMS\Exceptions\HostExistsException;
use LibreNMS\Exceptions\HostIpExistsException;
use LibreNMS\Exceptions\HostUnreachableException;
use LibreNMS\Exceptions\HostUnreachablePingException;
use LibreNMS\Exceptions\InvalidPortAssocModeException;
use LibreNMS\Exceptions\SnmpVersionUnsupportedException;
use LibreNMS\Modules\Core;
use LibreNMS\Util\Debug;
use LibreNMS\Util\IPv4;
use LibreNMS\Util\IPv6;
use LibreNMS\Util\Proxy;
function array_sort_by_column($array, $on, $order = SORT_ASC)
{
2020-09-21 13:40:17 +00:00
$new_array = [];
$sortable_array = [];
if (count($array) > 0) {
foreach ($array as $k => $v) {
if (is_array($v)) {
foreach ($v as $k2 => $v2) {
if ($k2 == $on) {
$sortable_array[$k] = $v2;
}
}
} else {
$sortable_array[$k] = $v;
}
}
switch ($order) {
case SORT_ASC:
asort($sortable_array);
break;
case SORT_DESC:
arsort($sortable_array);
break;
}
foreach ($sortable_array as $k => $v) {
$new_array[$k] = $array[$k];
}
}
2020-09-21 13:40:17 +00:00
return $new_array;
}
function only_alphanumeric($string)
{
2015-07-13 18:10:26 +00:00
return preg_replace('/[^a-zA-Z0-9]/', '', $string);
}
/**
* Parse cli discovery or poller modules and set config for this run
*
2021-09-08 21:35:56 +00:00
* @param string $type discovery or poller
* @param array $options get_opts array (only m key is checked)
* @return bool
*/
function parse_modules($type, $options)
{
$override = false;
2021-11-18 01:23:55 +00:00
if (! empty($options['m'])) {
Config::set("{$type}_modules", []);
foreach (explode(',', $options['m']) as $module) {
// parse submodules (only supported by some modules)
if (Str::contains($module, '/')) {
[$module, $submodule] = explode('/', $module, 2);
$existing_submodules = Config::get("{$type}_submodules.$module", []);
$existing_submodules[] = $submodule;
Config::set("{$type}_submodules.$module", $existing_submodules);
}
$dir = $type == 'poller' ? 'polling' : $type;
if (is_file("includes/$dir/$module.inc.php")) {
Config::set("{$type}_modules.$module", 1);
$override = true;
}
}
// display selected modules
$modules = array_map(function ($module) use ($type) {
$submodules = Config::get("{$type}_submodules.$module");
2020-09-21 13:40:17 +00:00
return $module . ($submodules ? '(' . implode(',', $submodules) . ')' : '');
}, array_keys(Config::get("{$type}_modules", [])));
d_echo("Override $type modules: " . implode(', ', $modules) . PHP_EOL);
}
return $override;
}
function logfile($string)
{
$fd = fopen(Config::get('log_file'), 'a');
fputs($fd, $string . "\n");
2015-07-13 18:10:26 +00:00
fclose($fd);
}
2017-01-12 17:01:34 +00:00
/**
* Check an array of regexes against a subject if any match, return true
*
2021-09-08 21:35:56 +00:00
* @param string $subject the string to match against
* @param array|string $regexes an array of regexes or single regex to check
2017-01-12 17:01:34 +00:00
* @return bool if any of the regexes matched, return true
*/
function preg_match_any($subject, $regexes)
{
2020-09-21 13:40:17 +00:00
foreach ((array) $regexes as $regex) {
if (preg_match($regex, $subject)) {
return true;
}
}
2020-09-21 13:40:17 +00:00
return false;
}
/**
* Perform comparison of two items based on give comparison method
* Valid comparisons: =, !=, ==, !==, >=, <=, >, <, contains, starts, ends, regex
* contains, starts, ends: $a haystack, $b needle(s)
* regex: $a subject, $b regex
*
2021-09-08 21:35:56 +00:00
* @param mixed $a
* @param mixed $b
* @param string $comparison =, !=, ==, !== >=, <=, >, <, contains, starts, ends, regex
* @return bool
*/
function compare_var($a, $b, $comparison = '=')
{
// handle PHP8 change to implicit casting
if (is_numeric($a) || is_numeric($b)) {
$a = cast_number($a);
$b = is_array($b) ? $b : cast_number($b);
}
switch ($comparison) {
2020-09-21 13:59:34 +00:00
case '=':
return $a == $b;
2020-09-21 13:59:34 +00:00
case '!=':
return $a != $b;
2020-09-21 13:59:34 +00:00
case '==':
return $a === $b;
2020-09-21 13:59:34 +00:00
case '!==':
return $a !== $b;
2020-09-21 13:59:34 +00:00
case '>=':
return $a >= $b;
2020-09-21 13:59:34 +00:00
case '<=':
return $a <= $b;
2020-09-21 13:59:34 +00:00
case '>':
return $a > $b;
2020-09-21 13:59:34 +00:00
case '<':
return $a < $b;
2020-09-21 13:59:34 +00:00
case 'contains':
return Str::contains($a, $b);
2020-09-21 13:59:34 +00:00
case 'not_contains':
2020-09-21 13:40:17 +00:00
return ! Str::contains($a, $b);
2020-09-21 13:59:34 +00:00
case 'starts':
return Str::startsWith($a, $b);
2020-09-21 13:59:34 +00:00
case 'not_starts':
2020-09-21 13:40:17 +00:00
return ! Str::startsWith($a, $b);
2020-09-21 13:59:34 +00:00
case 'ends':
return Str::endsWith($a, $b);
2020-09-21 13:59:34 +00:00
case 'not_ends':
2020-09-21 13:40:17 +00:00
return ! Str::endsWith($a, $b);
2020-09-21 13:59:34 +00:00
case 'regex':
2020-09-21 13:40:17 +00:00
return (bool) preg_match($b, $a);
case 'not_regex':
2020-09-21 13:40:17 +00:00
return ! ((bool) preg_match($b, $a));
2020-09-21 13:59:34 +00:00
case 'in_array':
return in_array($a, $b);
2020-09-21 13:59:34 +00:00
case 'not_in_array':
2020-09-21 13:40:17 +00:00
return ! in_array($a, $b);
2020-09-21 13:59:34 +00:00
case 'exists':
return isset($a) == $b;
default:
return false;
}
}
function percent_colour($perc)
{
2015-07-13 18:10:26 +00:00
$r = min(255, 5 * ($perc - 25));
$b = max(0, 255 - (5 * ($perc + 25)));
2015-07-13 18:10:26 +00:00
return sprintf('#%02x%02x%02x', $r, $b, $b);
}
/**
* @param $device
* @return string the logo image path for this device. Images are often wide, not square.
*/
function getLogo($device)
{
$img = getImageName($device, true, 'images/logos/');
2020-09-21 13:40:17 +00:00
if (! Str::startsWith($img, 'generic')) {
return 'images/logos/' . $img;
}
return getIcon($device);
}
/**
2021-09-08 21:35:56 +00:00
* @param array $device
* @param string $class to apply to the image tag
* @return string an image tag with the logo for this device. Images are often wide, not square.
*/
function getLogoTag($device, $class = null)
{
$tag = '<img src="' . url(getLogo($device)) . '" title="' . getImageTitle($device) . '"';
if (isset($class)) {
$tag .= " class=\"$class\" ";
}
$tag .= ' />';
2020-09-21 13:40:17 +00:00
return $tag;
}
/**
* @param $device
* @return string the path to the icon image for this device. Close to square.
*/
function getIcon($device)
{
return 'images/os/' . getImageName($device);
}
/**
* @param $device
* @return string an image tag with the icon for this device. Close to square.
*/
function getIconTag($device)
{
return '<img src="' . getIcon($device) . '" title="' . getImageTitle($device) . '"/>';
}
2017-01-24 23:36:33 +00:00
function getImageTitle($device)
{
2020-09-21 13:40:17 +00:00
return $device['icon'] ? str_replace(['.svg', '.png'], '', $device['icon']) : $device['os'];
}
function getImageName($device, $use_database = true, $dir = 'images/os/')
{
return \LibreNMS\Util\Url::findOsImage($device['os'], $device['features'], $use_database ? $device['icon'] : null, $dir);
}
function renamehost($id, $new, $source = 'console')
{
$host = gethostbyid($id);
2015-07-13 18:10:26 +00:00
if (! is_dir(Rrd::dirFromHost($new)) && rename(Rrd::dirFromHost($host), Rrd::dirFromHost($new)) === true) {
dbUpdate(['hostname' => $new, 'ip' => null], 'devices', 'device_id=?', [$id]);
log_event("Hostname changed -> $new ($source)", $id, 'system', 3);
2020-09-21 13:40:17 +00:00
return '';
2015-07-13 18:10:26 +00:00
}
log_event("Renaming of $host failed", $id, 'system', 5);
2020-09-21 13:40:17 +00:00
return "Renaming of $host failed\n";
}
function device_discovery_trigger($id)
{
if (App::runningInConsole() === false) {
ignore_user_abort(true);
set_time_limit(0);
}
2020-09-21 13:40:17 +00:00
$update = dbUpdate(['last_discovered' => ['NULL']], 'devices', '`device_id` = ?', [$id]);
if (! empty($update) || $update == '0') {
$message = 'Device will be rediscovered';
} else {
$message = 'Error rediscovering device';
}
2020-09-21 13:40:17 +00:00
return ['status'=> $update, 'message' => $message];
}
function delete_device($id)
{
if (App::runningInConsole() === false) {
ignore_user_abort(true);
set_time_limit(0);
}
2015-07-13 18:10:26 +00:00
$ret = '';
2020-09-21 13:59:34 +00:00
$host = dbFetchCell('SELECT hostname FROM devices WHERE device_id = ?', [$id]);
if (empty($host)) {
2020-09-21 13:59:34 +00:00
return 'No such host.';
2015-07-13 18:10:26 +00:00
}
// Remove IPv4/IPv6 addresses before removing ports as they depend on port_id
2020-09-21 13:59:34 +00:00
dbQuery('DELETE `ipv4_addresses` FROM `ipv4_addresses` INNER JOIN `ports` ON `ports`.`port_id`=`ipv4_addresses`.`port_id` WHERE `device_id`=?', [$id]);
dbQuery('DELETE `ipv6_addresses` FROM `ipv6_addresses` INNER JOIN `ports` ON `ports`.`port_id`=`ipv6_addresses`.`port_id` WHERE `device_id`=?', [$id]);
2015-07-13 18:10:26 +00:00
//Remove IsisAdjacencies
\App\Models\IsisAdjacency::where('device_id', $id)->delete();
//Remove Outages
\App\Models\Availability::where('device_id', $id)->delete();
\App\Models\DeviceOutage::where('device_id', $id)->delete();
\App\Models\Port::where('device_id', $id)
->with('device')
->select(['port_id', 'device_id', 'ifIndex', 'ifName', 'ifAlias', 'ifDescr'])
->chunk(100, function ($ports) use (&$ret) {
foreach ($ports as $port) {
$port->delete();
$ret .= "Removed interface $port->port_id (" . $port->getLabel() . ")\n";
}
});
2015-07-13 18:10:26 +00:00
// Remove sensors manually due to constraints
2020-09-21 13:59:34 +00:00
foreach (dbFetchRows('SELECT * FROM `sensors` WHERE `device_id` = ?', [$id]) as $sensor) {
$sensor_id = $sensor['sensor_id'];
2020-09-21 13:59:34 +00:00
dbDelete('sensors_to_state_indexes', '`sensor_id` = ?', [$sensor_id]);
}
2020-09-21 13:40:17 +00:00
$fields = ['device_id', 'host'];
$db_name = dbFetchCell('SELECT DATABASE()');
foreach ($fields as $field) {
2020-09-21 13:59:34 +00:00
foreach (dbFetch('SELECT TABLE_NAME FROM information_schema.columns WHERE table_schema = ? AND column_name = ?', [$db_name, $field]) as $table) {
$table = $table['TABLE_NAME'];
2020-09-21 13:40:17 +00:00
$entries = (int) dbDelete($table, "`$field` = ?", [$id]);
if ($entries > 0 && Debug::isEnabled()) {
2015-07-13 18:10:26 +00:00
$ret .= "$field@$table = #$entries\n";
}
}
2015-07-13 18:10:26 +00:00
}
$ex = shell_exec("bash -c '( [ ! -d " . trim(Rrd::dirFromHost($host)) . ' ] || rm -vrf ' . trim(Rrd::dirFromHost($host)) . " 2>&1 ) && echo -n OK'");
$tmp = explode("\n", $ex);
2020-09-21 13:59:34 +00:00
if ($tmp[sizeof($tmp) - 1] != 'OK') {
2015-07-13 18:10:26 +00:00
$ret .= "Could not remove files:\n$ex\n";
}
$ret .= "Removed device $host\n";
log_event("Device $host has been removed", 0, 'system', 3);
oxidized_reload_nodes();
2020-09-21 13:40:17 +00:00
2015-07-13 18:10:26 +00:00
return $ret;
}
/**
* Add a device to LibreNMS
*
2021-09-08 21:35:56 +00:00
* @param string $host dns name or ip address
* @param string $snmp_version If this is empty, try v2c,v3,v1. Otherwise, use this specific version.
* @param int $port the port to connect to for snmp
* @param string $transport udp or tcp
* @param string $poller_group the poller group this device will belong to
* @param bool $force_add add even if the device isn't reachable
* @param string $port_assoc_mode snmp field to use to determine unique ports
* @param array $additional an array with additional parameters to take into consideration when adding devices
* @return int returns the device_id of the added device
*
* @throws HostExistsException This hostname already exists
* @throws HostIpExistsException We already have a host with this IP
* @throws HostUnreachableException We could not reach this device is some way
* @throws HostUnreachablePingException We could not ping the device
* @throws InvalidPortAssocModeException The given port association mode was invalid
* @throws SnmpVersionUnsupportedException The given snmp version was invalid
*/
function addHost($host, $snmp_version = '', $port = 161, $transport = 'udp', $poller_group = '0', $force_add = false, $port_assoc_mode = 'ifIndex', $additional = [])
{
2015-07-13 18:10:26 +00:00
// Test Database Exists
if (host_exists($host)) {
throw new HostExistsException("Already have host $host");
}
// Valid port assoc mode
2020-09-21 13:40:17 +00:00
if (! in_array($port_assoc_mode, get_port_assoc_modes())) {
throw new InvalidPortAssocModeException("Invalid port association_mode '$port_assoc_mode'. Valid modes are: " . join(', ', get_port_assoc_modes()));
}
2015-07-13 18:10:26 +00:00
// check if we have the host by IP
$overwrite_ip = null;
2020-09-21 13:40:17 +00:00
if (! empty($additional['overwrite_ip'])) {
$overwrite_ip = $additional['overwrite_ip'];
$ip = $overwrite_ip;
} elseif (Config::get('addhost_alwayscheckip') === true) {
$ip = gethostbyname($host);
} else {
$ip = $host;
}
if ($force_add !== true && $device = device_has_ip($ip)) {
$message = "Cannot add $host, already have device with this IP $ip";
if ($ip != $device->hostname) {
$message .= " ($device->hostname)";
}
$message .= '. You may force add to ignore this.';
throw new HostIpExistsException($message);
}
// Test reachability
2020-09-21 13:40:17 +00:00
if (! $force_add) {
if (! ((new \LibreNMS\Polling\ConnectivityHelper(new Device(['hostname' => $ip])))->isPingable()->success())) {
throw new HostUnreachablePingException("Could not ping $host");
}
}
2015-07-13 18:10:26 +00:00
// if $snmpver isn't set, try each version of snmp
if (empty($snmp_version)) {
$snmpvers = Config::get('snmp.version');
} else {
2020-09-21 13:40:17 +00:00
$snmpvers = [$snmp_version];
}
if (isset($additional['snmp_disable']) && $additional['snmp_disable'] == 1) {
2020-09-21 13:40:17 +00:00
return createHost($host, '', $snmp_version, $port, $transport, [], $poller_group, 1, true, $overwrite_ip, $additional);
}
$host_unreachable_exception = new HostUnreachableException("Could not connect to $host, please check the snmp details and snmp reachability");
// try different snmp variables to add the device
foreach ($snmpvers as $snmpver) {
2020-09-21 13:59:34 +00:00
if ($snmpver === 'v3') {
// Try each set of parameters from config
foreach (Config::get('snmp.v3') as $v3) {
$device = deviceArray($host, null, $snmpver, $port, $transport, $v3, $port_assoc_mode, $overwrite_ip);
if ($force_add === true || isSNMPable($device)) {
return createHost($host, null, $snmpver, $port, $transport, $v3, $poller_group, $port_assoc_mode, $force_add, $overwrite_ip);
} else {
2020-09-21 13:59:34 +00:00
$host_unreachable_exception->addReason("SNMP $snmpver: No reply with credentials " . $v3['authname'] . '/' . $v3['authlevel']);
2015-07-13 18:10:26 +00:00
}
}
2020-09-21 13:59:34 +00:00
} elseif ($snmpver === 'v2c' || $snmpver === 'v1') {
// try each community from config
foreach (Config::get('snmp.community') as $community) {
$device = deviceArray($host, $community, $snmpver, $port, $transport, null, $port_assoc_mode, $overwrite_ip);
if ($force_add === true || isSNMPable($device)) {
2020-09-21 13:40:17 +00:00
return createHost($host, $community, $snmpver, $port, $transport, [], $poller_group, $port_assoc_mode, $force_add, $overwrite_ip);
} else {
$host_unreachable_exception->addReason("SNMP $snmpver: No reply with community $community");
2015-07-13 18:10:26 +00:00
}
}
} else {
throw new SnmpVersionUnsupportedException("Unsupported SNMP Version \"$snmpver\", must be v1, v2c, or v3");
}
}
if (isset($additional['ping_fallback']) && $additional['ping_fallback'] == 1) {
$additional['snmp_disable'] = 1;
2020-09-21 13:59:34 +00:00
$additional['os'] = 'ping';
2020-09-21 13:40:17 +00:00
return createHost($host, '', $snmp_version, $port, $transport, [], $poller_group, 1, true, $overwrite_ip, $additional);
}
throw $host_unreachable_exception;
}
2020-09-21 13:40:17 +00:00
function deviceArray($host, $community, $snmpver, $port = 161, $transport = 'udp', $v3 = [], $port_assoc_mode = 'ifIndex', $overwrite_ip = null)
{
2020-09-21 13:40:17 +00:00
$device = [];
2015-07-13 18:10:26 +00:00
$device['hostname'] = $host;
$device['overwrite_ip'] = $overwrite_ip;
2015-07-13 18:10:26 +00:00
$device['port'] = $port;
$device['transport'] = $transport;
/* Get port_assoc_mode id if neccessary
* We can work with names of IDs here */
if (! is_int($port_assoc_mode)) {
$port_assoc_mode = get_port_assoc_mode_id($port_assoc_mode);
}
$device['port_association_mode'] = $port_assoc_mode;
2015-07-13 18:10:26 +00:00
$device['snmpver'] = $snmpver;
2020-09-21 13:59:34 +00:00
if ($snmpver === 'v2c' or $snmpver === 'v1') {
2015-07-13 18:10:26 +00:00
$device['community'] = $community;
2020-09-21 13:59:34 +00:00
} elseif ($snmpver === 'v3') {
2020-09-21 13:40:17 +00:00
$device['authlevel'] = $v3['authlevel'];
$device['authname'] = $v3['authname'];
$device['authpass'] = $v3['authpass'];
$device['authalgo'] = $v3['authalgo'];
2015-07-13 18:10:26 +00:00
$device['cryptopass'] = $v3['cryptopass'];
$device['cryptoalgo'] = $v3['cryptoalgo'];
}
return $device;
}//end deviceArray()
function isSNMPable($device)
{
$pos = snmp_check($device);
if ($pos === true) {
2015-07-13 18:10:26 +00:00
return true;
} else {
2020-09-21 13:59:34 +00:00
$pos = snmp_get($device, 'sysObjectID.0', '-Oqv', 'SNMPv2-MIB');
if ($pos === '' || $pos === false) {
return false;
} else {
return true;
}
2015-07-13 18:10:26 +00:00
}
}
function getpollergroup($poller_group = '0')
{
2015-07-13 18:10:26 +00:00
//Is poller group an integer
if (is_int($poller_group) || ctype_digit($poller_group)) {
return $poller_group;
} else {
2015-07-13 18:10:26 +00:00
//Check if it contains a comma
2020-09-21 13:40:17 +00:00
if (strpos($poller_group, ',') !== false) {
2015-07-13 18:10:26 +00:00
//If it has a comma use the first element as the poller group
2020-09-21 13:40:17 +00:00
$poller_group_array = explode(',', $poller_group);
2015-07-13 18:10:26 +00:00
return getpollergroup($poller_group_array[0]);
} else {
if (Config::get('distributed_poller_group')) {
2015-07-13 18:10:26 +00:00
//If not use the poller's group from the config
return getpollergroup(Config::get('distributed_poller_group'));
} else {
2015-07-13 18:10:26 +00:00
//If all else fails use default
return '0';
}
}
}
}
/**
* Add a host to the database
*
2021-09-08 21:35:56 +00:00
* @param string $host The IP or hostname to add
* @param string $community The snmp community
* @param string $snmpver snmp version: v1 | v2c | v3
* @param int $port SNMP port number
* @param string $transport SNMP transport: udp | udp6 | udp | tcp6
* @param array $v3 SNMPv3 settings required array keys: authlevel, authname, authpass, authalgo, cryptopass, cryptoalgo
* @param int $poller_group distributed poller group to assign this host to
* @param string $port_assoc_mode field to use to identify ports: ifIndex, ifName, ifDescr, ifAlias
* @param bool $force_add Do not detect the host os
* @param array $additional an array with additional parameters to take into consideration when adding devices
* @return int the id of the added host
2021-09-10 18:09:53 +00:00
*
* @throws HostExistsException Throws this exception if the host already exists
* @throws Exception Throws this exception if insertion into the database fails
*/
function createHost(
$host,
$community,
$snmpver,
$port = 161,
$transport = 'udp',
2020-09-21 13:40:17 +00:00
$v3 = [],
$poller_group = 0,
$port_assoc_mode = 'ifIndex',
$force_add = false,
$overwrite_ip = null,
2020-09-21 13:40:17 +00:00
$additional = []
) {
2015-07-13 18:10:26 +00:00
$host = trim(strtolower($host));
2020-09-21 13:40:17 +00:00
$poller_group = getpollergroup($poller_group);
2015-07-13 18:10:26 +00:00
/* Get port_assoc_mode id if necessary
* We can work with names of IDs here */
if (! is_int($port_assoc_mode)) {
$port_assoc_mode = get_port_assoc_mode_id($port_assoc_mode);
}
$device = new Device(array_merge([
'hostname' => $host,
'overwrite_ip' => $overwrite_ip,
'sysName' => $additional['sysName'] ?? $host,
'os' => $additional['os'] ?? 'generic',
'hardware' => $additional['hardware'] ?? null,
2015-07-13 18:10:26 +00:00
'community' => $community,
'port' => $port,
'transport' => $transport,
'status' => '1',
'snmpver' => $snmpver,
2015-08-11 11:38:05 +00:00
'poller_group' => $poller_group,
'status_reason' => '',
'port_association_mode' => $port_assoc_mode,
'snmp_disable' => $additional['snmp_disable'] ?? 0,
], $v3));
if ($force_add !== true) {
$device->os = Core::detectOS($device);
$device->sysName = SnmpQuery::device($device)->get('SNMPv2-MIB::sysName.0')->value();
if (host_exists($host, $device->sysName)) {
throw new HostExistsException("Already have host $host ({$device->sysName}) due to duplicate sysName");
}
}
if ($device->save()) {
return $device->device_id;
}
2020-09-21 13:59:34 +00:00
throw new \Exception('Failed to add host to the database, please run ./validate.php');
}
function isDomainResolves($domain)
{
if (gethostbyname($domain) != $domain) {
return true;
}
$records = dns_get_record($domain); // returns array or false
2020-09-21 13:40:17 +00:00
return ! empty($records);
}
function match_network($nets, $ip, $first = false)
{
2015-07-13 18:10:26 +00:00
$return = false;
2020-09-21 13:40:17 +00:00
if (! is_array($nets)) {
$nets = [$nets];
}
2015-07-13 18:10:26 +00:00
foreach ($nets as $net) {
$rev = (preg_match("/^\!/", $net)) ? true : false;
2020-09-21 13:59:34 +00:00
$net = preg_replace("/^\!/", '', $net);
2020-09-21 13:40:17 +00:00
$ip_arr = explode('/', $net);
2015-07-13 18:10:26 +00:00
$net_long = ip2long($ip_arr[0]);
2020-09-21 13:40:17 +00:00
$x = ip2long($ip_arr[1]);
2021-09-08 01:33:54 +00:00
$mask = long2ip($x) == $ip_arr[1] ? $x : 0xFFFFFFFF << (32 - $ip_arr[1]);
2020-09-21 13:40:17 +00:00
$ip_long = ip2long($ip);
2015-07-13 18:10:26 +00:00
if ($rev) {
if (($ip_long & $mask) == ($net_long & $mask)) {
return false;
}
} else {
2015-07-13 18:10:26 +00:00
if (($ip_long & $mask) == ($net_long & $mask)) {
$return = true;
}
if ($first && $return) {
return true;
}
}
}
return $return;
}
// FIXME port to LibreNMS\Util\IPv6 class
function snmp2ipv6($ipv6_snmp)
{
// Workaround stupid Microsoft bug in Windows 2008 -- this is fixed length!
// < fenestro> "because whoever implemented this mib for Microsoft was ignorant of RFC 2578 section 7.7 (2)"
$ipv6 = array_slice(explode('.', $ipv6_snmp), -16);
$ipv6_2 = [];
for ($i = 0; $i <= 15; $i++) {
$ipv6[$i] = zeropad(dechex($ipv6[$i]));
}
for ($i = 0; $i <= 15; $i += 2) {
$ipv6_2[] = $ipv6[$i] . $ipv6[$i + 1];
}
return implode(':', $ipv6_2);
}
function get_astext($asn)
{
global $cache;
2015-07-13 18:10:26 +00:00
if (Config::has("astext.$asn")) {
return Config::get("astext.$asn");
}
if (isset($cache['astext'][$asn])) {
return $cache['astext'][$asn];
}
2018-09-11 02:40:41 +00:00
$result = @dns_get_record("AS$asn.asn.cymru.com", DNS_TXT);
2020-09-21 13:40:17 +00:00
if (! empty($result[0]['txt'])) {
$txt = explode('|', $result[0]['txt']);
$result = trim($txt[4], ' "');
$cache['astext'][$asn] = $result;
2020-09-21 13:40:17 +00:00
return $result;
2015-07-13 18:10:26 +00:00
}
return '';
}
/**
* Log events to the event table
*
2021-09-08 21:35:56 +00:00
* @param string $text message describing the event
* @param array|int $device device array or device_id
* @param string $type brief category for this event. Examples: sensor, state, stp, system, temperature, interface
* @param int $severity 1: ok, 2: info, 3: notice, 4: warning, 5: critical, 0: unknown
* @param int $reference the id of the referenced entity. Supported types: interface
*/
function log_event($text, $device = null, $type = null, $severity = 2, $reference = null)
{
// handle legacy device array
if (is_array($device) && isset($device['device_id'])) {
$device = $device['device_id'];
}
Log::event($text, $device, $type, $severity, $reference);
}
// Parse string with emails. Return array with email (as key) and name (as value)
function parse_email($emails)
{
return \LibreNMS\Util\Mail::parseEmails($emails);
}
function send_mail($emails, $subject, $message, $html = false)
{
return \LibreNMS\Util\Mail::send($emails, $subject, $message, $html);
}
function hex2str($hex)
{
2020-09-21 13:40:17 +00:00
$string = '';
2020-09-21 13:40:17 +00:00
for ($i = 0; $i < strlen($hex) - 1; $i += 2) {
$string .= chr(hexdec(substr($hex, $i, 2)));
2015-07-13 18:10:26 +00:00
}
2015-07-13 18:10:26 +00:00
return $string;
}
2020-09-21 13:40:17 +00:00
// Convert an SNMP hex string to regular string
function snmp_hexstring($hex)
{
return hex2str(str_replace(' ', '', str_replace(' 00', '', $hex)));
}
2020-09-21 13:40:17 +00:00
// Check if the supplied string is an SNMP hex string
function isHexString($str)
{
2020-09-21 13:59:34 +00:00
return (bool) preg_match('/^[a-f0-9][a-f0-9]( [a-f0-9][a-f0-9])*$/is', trim($str));
}
2020-09-21 13:40:17 +00:00
// Include all .inc.php files in $dir
2020-09-21 13:59:34 +00:00
function include_dir($dir, $regex = '')
{
global $device, $valid;
2020-09-21 13:59:34 +00:00
if ($regex == '') {
2015-07-13 18:10:26 +00:00
$regex = "/\.inc\.php$/";
}
if ($handle = opendir(Config::get('install_dir') . '/' . $dir)) {
2015-07-13 18:10:26 +00:00
while (false !== ($file = readdir($handle))) {
if (filetype(Config::get('install_dir') . '/' . $dir . '/' . $file) == 'file' && preg_match($regex, $file)) {
2020-09-21 13:59:34 +00:00
d_echo('Including: ' . Config::get('install_dir') . '/' . $dir . '/' . $file . "\n");
2020-09-21 13:40:17 +00:00
include Config::get('install_dir') . '/' . $dir . '/' . $file;
2015-07-13 18:10:26 +00:00
}
}
2015-07-13 18:10:26 +00:00
closedir($handle);
}
}
/**
* Check if port is valid to poll.
* Settings: empty_ifdescr, good_if, bad_if, bad_if_regexp, bad_ifname_regexp, bad_ifalias_regexp, bad_iftype, bad_ifoperstatus
*
2021-09-08 21:35:56 +00:00
* @param array $port
* @param array $device
* @return bool
*/
function is_port_valid($port, $device)
{
// check empty values first
if (empty($port['ifDescr'])) {
// If these are all empty, we are just going to show blank names in the ui
if (empty($port['ifAlias']) && empty($port['ifName'])) {
d_echo("ignored: empty ifDescr, ifAlias and ifName\n");
2020-09-21 13:40:17 +00:00
return false;
2015-07-13 18:10:26 +00:00
}
// ifDescr should not be empty unless it is explicitly allowed
2020-09-21 13:40:17 +00:00
if (! Config::getOsSetting($device['os'], 'empty_ifdescr', Config::get('empty_ifdescr', false))) {
d_echo("ignored: empty ifDescr\n");
2020-09-21 13:40:17 +00:00
return false;
2015-07-13 18:10:26 +00:00
}
}
$ifDescr = $port['ifDescr'];
2020-09-21 13:40:17 +00:00
$ifName = $port['ifName'];
$ifAlias = $port['ifAlias'];
2020-09-21 13:40:17 +00:00
$ifType = $port['ifType'];
$ifOperStatus = $port['ifOperStatus'];
if (str_i_contains($ifDescr, Config::getOsSetting($device['os'], 'good_if', Config::get('good_if')))) {
return true;
}
foreach (Config::getCombined($device['os'], 'bad_if') as $bi) {
if (str_i_contains($ifDescr, $bi)) {
d_echo("ignored by ifDescr: $ifDescr (matched: $bi)\n");
2020-09-21 13:40:17 +00:00
return false;
2015-07-13 18:10:26 +00:00
}
}
foreach (Config::getCombined($device['os'], 'bad_if_regexp') as $bir) {
2020-09-21 13:59:34 +00:00
if (preg_match($bir . 'i', $ifDescr)) {
d_echo("ignored by ifDescr: $ifDescr (matched: $bir)\n");
2020-09-21 13:40:17 +00:00
return false;
}
}
foreach (Config::getCombined($device['os'], 'bad_ifname_regexp') as $bnr) {
2020-09-21 13:59:34 +00:00
if (preg_match($bnr . 'i', $ifName)) {
d_echo("ignored by ifName: $ifName (matched: $bnr)\n");
2020-09-21 13:40:17 +00:00
return false;
}
}
foreach (Config::getCombined($device['os'], 'bad_ifalias_regexp') as $bar) {
2020-09-21 13:59:34 +00:00
if (preg_match($bar . 'i', $ifAlias)) {
d_echo("ignored by ifName: $ifAlias (matched: $bar)\n");
2020-09-21 13:40:17 +00:00
return false;
}
}
foreach (Config::getCombined($device['os'], 'bad_iftype') as $bt) {
if (Str::contains($ifType, $bt)) {
d_echo("ignored by ifType: $ifType (matched: $bt )\n");
2020-09-21 13:40:17 +00:00
return false;
}
}
foreach (Config::getCombined($device['os'], 'bad_ifoperstatus') as $bos) {
if (Str::contains($ifOperStatus, $bos)) {
d_echo("ignored by ifOperStatus: $ifOperStatus (matched: $bos)\n");
2020-09-21 13:40:17 +00:00
return false;
}
}
return true;
}
/**
* Try to fill in data for ifDescr, ifName, and ifAlias if devices do not provide them.
* Will not fill ifAlias if the user has overridden it
*
2021-09-08 21:35:56 +00:00
* @param array $port
* @param array $device
*/
function port_fill_missing(&$port, $device)
{
// When devices do not provide data, populate with other data if available
if ($port['ifDescr'] == '' || $port['ifDescr'] == null) {
$port['ifDescr'] = $port['ifName'];
d_echo(' Using ifName as ifDescr');
}
2020-09-21 13:40:17 +00:00
if (! empty($device['attribs']['ifName:' . $port['ifName']])) {
// ifAlias overridden by user, don't update it
unset($port['ifAlias']);
d_echo(' ifAlias overriden by user');
} elseif ($port['ifAlias'] == '' || $port['ifAlias'] == null) {
$port['ifAlias'] = $port['ifDescr'];
d_echo(' Using ifDescr as ifAlias');
}
if ($port['ifName'] == '' || $port['ifName'] == null) {
$port['ifName'] = $port['ifDescr'];
d_echo(' Using ifDescr as ifName');
}
}
function validate_device_id($id)
{
2020-09-21 13:40:17 +00:00
if (empty($id) || ! is_numeric($id)) {
2015-07-13 18:10:26 +00:00
$return = false;
} else {
2020-09-21 13:59:34 +00:00
$device_id = dbFetchCell('SELECT `device_id` FROM `devices` WHERE `device_id` = ?', [$id]);
if ($device_id == $id) {
2015-07-13 18:10:26 +00:00
$return = true;
} else {
2015-07-13 18:10:26 +00:00
$return = false;
}
}
2020-09-21 13:40:17 +00:00
return $return;
2014-06-24 15:14:42 +00:00
}
2014-09-17 20:03:02 +00:00
function convert_delay($delay)
{
if (preg_match('/(\d+)([mhd]?)/', $delay, $matches)) {
$multipliers = [
'm' => 60,
'h' => 3600,
'd' => 86400,
];
$multiplier = $multipliers[$matches[2]] ?? 1;
return $matches[1] * $multiplier;
2014-11-30 17:49:52 +00:00
}
2020-09-21 13:40:17 +00:00
return $delay === '' ? 0 : 300;
2014-11-30 17:49:52 +00:00
}
function normalize_snmp_ip_address($data)
{
// $data is received from snmpwalk, can be ipv4 xxx.xxx.xxx.xxx or ipv6 xx:xx:...:xx (16 chunks)
// ipv4 is returned unchanged, ipv6 is returned with one ':' removed out of two, like
// xxxx:xxxx:...:xxxx (8 chuncks)
2020-09-21 13:40:17 +00:00
return preg_replace('/([0-9a-fA-F]{2}):([0-9a-fA-F]{2})/', '\1\2', explode('%', $data, 2)[0]);
}
function guidv4($data)
{
// http://stackoverflow.com/questions/2040240/php-function-to-generate-v4-uuid#15875555
// From: Jack http://stackoverflow.com/users/1338292/ja%CD%A2ck
assert(strlen($data) == 16);
2021-09-08 01:33:54 +00:00
$data[6] = chr(ord($data[6]) & 0x0F | 0x40); // set version to 0100
$data[8] = chr(ord($data[8]) & 0x3F | 0x80); // set bits 6-7 to 10
return vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($data), 4));
}
/**
* @param $curl
*/
function set_curl_proxy($curl)
{
\LibreNMS\Util\Proxy::applyToCurl($curl);
2015-04-13 09:51:16 +00:00
}
function target_to_id($target)
{
2020-09-21 13:59:34 +00:00
if ($target[0] . $target[1] == 'g:') {
$target = 'g' . dbFetchCell('SELECT id FROM device_groups WHERE name = ?', [substr($target, 2)]);
} else {
2020-09-21 13:40:17 +00:00
$target = dbFetchCell('SELECT device_id FROM devices WHERE hostname = ?', [$target]);
}
2020-09-21 13:40:17 +00:00
return $target;
}
function fix_integer_value($value)
{
if ($value < 0) {
2020-09-21 13:40:17 +00:00
$return = 4294967296 + $value;
} else {
$return = $value;
}
2020-09-21 13:40:17 +00:00
return $return;
}
/**
* Find a device that has this IP. Checks ipv4_addresses and ipv6_addresses tables.
*
2021-09-08 21:35:56 +00:00
* @param string $ip
* @return \App\Models\Device|false
*/
function device_has_ip($ip)
{
if (IPv6::isValid($ip)) {
$ip_address = \App\Models\Ipv6Address::query()
->where('ipv6_address', IPv6::parse($ip, true)->uncompressed())
->with('port.device')
->first();
} elseif (IPv4::isValid($ip)) {
$ip_address = \App\Models\Ipv4Address::query()
->where('ipv4_address', $ip)
->with('port.device')
->first();
2015-07-13 18:10:26 +00:00
}
if (isset($ip_address) && $ip_address->port) {
return $ip_address->port->device;
}
return false; // not an ipv4 or ipv6 address...
}
/**
* Checks if the $hostname provided exists in the DB already
*
2021-09-08 21:35:56 +00:00
* @param string $hostname The hostname to check for
* @param string $sysName The sysName to check
* @return bool true if hostname already exists
* false if hostname doesn't exist
*/
function host_exists($hostname, $sysName = null)
{
2020-09-21 13:59:34 +00:00
$query = 'SELECT COUNT(*) FROM `devices` WHERE `hostname`=?';
2020-09-21 13:40:17 +00:00
$params = [$hostname];
2020-09-21 13:40:17 +00:00
if (! empty($sysName) && ! Config::get('allow_duplicate_sysName')) {
2020-09-21 13:59:34 +00:00
$query .= ' OR `sysName`=?';
$params[] = $sysName;
2020-09-21 13:40:17 +00:00
if (! empty(Config::get('mydomain'))) {
$full_sysname = rtrim($sysName, '.') . '.' . Config::get('mydomain');
2020-09-21 13:59:34 +00:00
$query .= ' OR `sysName`=?';
$params[] = $full_sysname;
}
}
2020-09-21 13:40:17 +00:00
return dbFetchCell($query, $params) > 0;
}
function oxidized_reload_nodes()
{
if (Config::get('oxidized.enabled') === true && Config::get('oxidized.reload_nodes') === true && Config::has('oxidized.url')) {
$oxidized_reload_url = Config::get('oxidized.url') . '/reload.json';
2016-01-17 16:47:26 +00:00
$ch = curl_init($oxidized_reload_url);
curl_setopt($ch, CURLOPT_TIMEOUT, 5);
curl_setopt($ch, CURLOPT_TIMEOUT_MS, 5000);
2016-01-17 16:47:26 +00:00
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
2021-07-17 15:21:01 +00:00
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
2016-01-17 16:47:26 +00:00
curl_setopt($ch, CURLOPT_HEADER, 1);
curl_exec($ch);
curl_close($ch);
}
}
/**
* Perform DNS lookup
*
2021-09-08 21:35:56 +00:00
* @param array $device Device array from database
* @param string $type The type of record to lookup
* @return string ip
*
2020-09-21 13:40:17 +00:00
**/
function dnslookup($device, $type = false, $return = false)
{
2016-12-30 00:13:12 +00:00
if (filter_var($device['hostname'], FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) == true || filter_var($device['hostname'], FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) == true) {
return false;
2016-01-18 00:04:30 +00:00
}
if (empty($type)) {
// We are going to use the transport to work out the record type
if ($device['transport'] == 'udp6' || $device['transport'] == 'tcp6') {
$type = DNS_AAAA;
$return = 'ipv6';
} else {
$type = DNS_A;
$return = 'ip';
}
}
2016-01-18 12:54:25 +00:00
if (empty($return)) {
return false;
2016-01-18 12:54:25 +00:00
}
$record = dns_get_record($device['hostname'], $type);
2020-09-21 13:40:17 +00:00
return $record[0][$return];
}//end dnslookup
/**
* Create a new state index. Update translations if $states is given.
*
* For for backward compatibility:
* Returns null if $states is empty, $state_name already exists, and contains state translations
*
2021-09-08 21:35:56 +00:00
* @param string $state_name the unique name for this state translation
* @param array $states array of states, each must contain keys: descr, graph, value, generic
* @return int|null
*/
2020-09-21 13:40:17 +00:00
function create_state_index($state_name, $states = [])
{
2020-09-21 13:40:17 +00:00
$state_index_id = dbFetchCell('SELECT `state_index_id` FROM state_indexes WHERE state_name = ? LIMIT 1', [$state_name]);
if (! is_numeric($state_index_id)) {
$state_index_id = dbInsert(['state_name' => $state_name], 'state_indexes');
// legacy code, return index so states are created
if (empty($states)) {
return $state_index_id;
}
}
// check or synchronize states
if (empty($states)) {
2020-09-21 13:40:17 +00:00
$translations = dbFetchRows('SELECT * FROM `state_translations` WHERE `state_index_id` = ?', [$state_index_id]);
if (count($translations) == 0) {
// If we don't have any translations something has gone wrong so return the state_index_id so they get created.
return $state_index_id;
}
} else {
sync_sensor_states($state_index_id, $states);
}
return null;
}
/**
* Synchronize the sensor state translations with the database
*
2021-09-08 21:35:56 +00:00
* @param int $state_index_id index of the state
* @param array $states array of states, each must contain keys: descr, graph, value, generic
*/
function sync_sensor_states($state_index_id, $states)
{
$new_translations = array_reduce($states, function ($array, $state) use ($state_index_id) {
2020-09-21 13:40:17 +00:00
$array[$state['value']] = [
'state_index_id' => $state_index_id,
'state_descr' => $state['descr'],
'state_draw_graph' => $state['graph'],
'state_value' => $state['value'],
2020-09-21 13:40:17 +00:00
'state_generic_value' => $state['generic'],
];
return $array;
2020-09-21 13:40:17 +00:00
}, []);
$existing_translations = dbFetchRows(
'SELECT `state_index_id`,`state_descr`,`state_draw_graph`,`state_value`,`state_generic_value` FROM `state_translations` WHERE `state_index_id`=?',
2020-09-21 13:40:17 +00:00
[$state_index_id]
);
foreach ($existing_translations as $translation) {
$value = $translation['state_value'];
if (isset($new_translations[$value])) {
if ($new_translations[$value] != $translation) {
dbUpdate(
$new_translations[$value],
'state_translations',
'`state_index_id`=? AND `state_value`=?',
2020-09-21 13:40:17 +00:00
[$state_index_id, $value]
);
}
// this translation is synchronized, it doesn't need to be inserted
unset($new_translations[$value]);
} else {
2020-09-21 13:40:17 +00:00
dbDelete('state_translations', '`state_index_id`=? AND `state_value`=?', [$state_index_id, $value]);
}
}
// insert any new translations
dbBulkInsert($new_translations, 'state_translations');
2016-02-26 22:40:34 +00:00
}
function create_sensor_to_state_index($device, $state_name, $index)
{
2020-09-21 13:40:17 +00:00
$sensor_entry = dbFetchRow('SELECT sensor_id FROM `sensors` WHERE `sensor_class` = ? AND `device_id` = ? AND `sensor_type` = ? AND `sensor_index` = ?', [
2016-02-26 22:40:34 +00:00
'state',
$device['device_id'],
$state_name,
2020-09-21 13:40:17 +00:00
$index,
]);
$state_indexes_entry = dbFetchRow('SELECT state_index_id FROM `state_indexes` WHERE `state_name` = ?', [
$state_name,
]);
if (! empty($sensor_entry['sensor_id']) && ! empty($state_indexes_entry['state_index_id'])) {
$insert = [
2016-02-26 22:40:34 +00:00
'sensor_id' => $sensor_entry['sensor_id'],
'state_index_id' => $state_indexes_entry['state_index_id'],
2020-09-21 13:40:17 +00:00
];
foreach ($insert as $key => $val_check) {
2020-09-21 13:40:17 +00:00
if (! isset($val_check)) {
2016-02-26 22:40:34 +00:00
unset($insert[$key]);
}
}
2016-02-26 22:56:54 +00:00
dbInsert($insert, 'sensors_to_state_indexes');
2016-02-26 22:40:34 +00:00
}
}
function delta_to_bits($delta, $period)
{
2016-07-07 18:10:37 +00:00
return round(($delta * 8 / $period), 2);
}
2016-07-07 18:11:13 +00:00
function report_this($message)
{
return '<h2>' . $message . ' Please <a href="' . Config::get('project_issues') . '">report this</a> to the ' . Config::get('project_name') . ' developers.</h2>';
}//end report_this()
2016-08-23 18:44:45 +00:00
function hytera_h2f($number, $nd)
2016-08-23 18:44:45 +00:00
{
2020-09-21 13:59:34 +00:00
if (strlen(str_replace(' ', '', $number)) == 4) {
2016-08-23 18:44:45 +00:00
$hex = '';
for ($i = 0; $i < strlen($number); $i++) {
$byte = strtoupper(dechex(ord($number[$i])));
2020-09-21 13:40:17 +00:00
$byte = str_repeat('0', 2 - strlen($byte)) . $byte;
2020-09-21 13:59:34 +00:00
$hex .= $byte . ' ';
2016-08-23 18:44:45 +00:00
}
$number = $hex;
unset($hex);
}
$r = '';
$y = explode(' ', $number);
foreach ($y as $z) {
$r = $z . '' . $r;
}
2020-09-21 13:40:17 +00:00
$hex = [];
2016-08-23 18:44:45 +00:00
$number = substr($r, 0, -1);
//$number = str_replace(" ", "", $number);
2020-09-21 13:40:17 +00:00
for ($i = 0; $i < strlen($number); $i++) {
$hex[] = substr($number, $i, 1);
2016-08-23 18:44:45 +00:00
}
2020-09-21 13:40:17 +00:00
$dec = [];
2016-08-23 18:44:45 +00:00
$hexCount = count($hex);
2020-09-21 13:40:17 +00:00
for ($i = 0; $i < $hexCount; $i++) {
$dec[] = hexdec($hex[$i]);
2016-08-23 18:44:45 +00:00
}
2020-09-21 13:59:34 +00:00
$binfinal = '';
2016-08-23 18:44:45 +00:00
$decCount = count($dec);
2020-09-21 13:40:17 +00:00
for ($i = 0; $i < $decCount; $i++) {
2020-09-21 13:59:34 +00:00
$binfinal .= sprintf('%04d', decbin($dec[$i]));
2016-08-23 18:44:45 +00:00
}
2020-09-21 13:40:17 +00:00
$sign = substr($binfinal, 0, 1);
$exp = substr($binfinal, 1, 8);
$exp = bindec($exp);
$exp -= 127;
$scibin = substr($binfinal, 9);
$binint = substr($scibin, 0, $exp);
$binpoint = substr($scibin, $exp);
2020-09-21 13:59:34 +00:00
$intnumber = bindec('1' . $binint);
2016-08-23 18:44:45 +00:00
2019-05-30 14:56:27 +00:00
$tmppoint = [];
2020-09-21 13:40:17 +00:00
for ($i = 0; $i < strlen($binpoint); $i++) {
$tmppoint[] = substr($binpoint, $i, 1);
2016-08-23 18:44:45 +00:00
}
2020-09-21 13:40:17 +00:00
$tmppoint = array_reverse($tmppoint);
$tpointnumber = number_format($tmppoint[0] / 2, strlen($binpoint), '.', '');
2016-08-23 18:44:45 +00:00
2020-09-21 13:59:34 +00:00
$pointnumber = '';
2020-09-21 13:40:17 +00:00
for ($i = 1; $i < strlen($binpoint); $i++) {
$pointnumber = number_format($tpointnumber / 2, strlen($binpoint), '.', '');
$tpointnumber = $tmppoint[$i + 1] . substr($pointnumber, 1);
2016-08-23 18:44:45 +00:00
}
2020-09-21 13:40:17 +00:00
$floatfinal = $intnumber + $pointnumber;
2016-08-23 18:44:45 +00:00
2020-09-21 13:40:17 +00:00
if ($sign == 1) {
$floatfinal = -$floatfinal;
2016-08-23 18:44:45 +00:00
}
return number_format($floatfinal, $nd, '.', '');
2016-08-23 18:44:45 +00:00
}
/*
* Cisco CIMC functions
*/
// Create an entry in the entPhysical table if it doesnt already exist.
function setCIMCentPhysical($location, $data, &$entphysical, &$index)
{
// Go get the location, this will create it if it doesnt exist.
$entPhysicalIndex = getCIMCentPhysical($location, $entphysical, $index);
// See if we need to update
2020-09-21 13:40:17 +00:00
$update = [];
foreach ($data as $key => $value) {
// Is the Array(DB) value different to the supplied data
if ($entphysical[$location][$key] != $value) {
$update[$key] = $value;
$entphysical[$location][$key] = $value;
} // End if
} // end foreach
// Do we need to update
if (count($update) > 0) {
2020-09-21 13:40:17 +00:00
dbUpdate($update, 'entPhysical', '`entPhysical_id` = ?', [$entphysical[$location]['entPhysical_id']]);
}
$entPhysicalId = $entphysical[$location]['entPhysical_id'];
2020-09-21 13:40:17 +00:00
return [$entPhysicalId, $entPhysicalIndex];
}
function getCIMCentPhysical($location, &$entphysical, &$index)
{
global $device;
// Level 1 - Does the location exist
if (isset($entphysical[$location])) {
// Yes, return the entPhysicalIndex.
return $entphysical[$location]['entPhysicalIndex'];
} else {
/*
* No, the entry doesnt exist.
* Find its parent so we can create it.
*/
// Pull apart the location
$parts = explode('/', $location);
// Level 2 - Are we at the root
if (count($parts) == 1) {
// Level 2 - Yes. We are the root, there is no parent
2020-09-21 13:59:34 +00:00
d_echo('ROOT - ' . $location . "\n");
$shortlocation = $location;
$parent = 0;
} else {
// Level 2 - No. Need to go deeper.
2020-09-21 13:59:34 +00:00
d_echo('NON-ROOT - ' . $location . "\n");
$shortlocation = array_pop($parts);
$parentlocation = implode('/', $parts);
2020-09-21 13:59:34 +00:00
d_echo('Decend - parent location: ' . $parentlocation . "\n");
$parent = getCIMCentPhysical($parentlocation, $entphysical, $index);
} // end if - Level 2
2020-09-21 13:59:34 +00:00
d_echo('Parent: ' . $parent . "\n");
// Now we have an ID, create the entry.
$index++;
2020-09-21 13:40:17 +00:00
$insert = [
'device_id' => $device['device_id'],
'entPhysicalIndex' => $index,
'entPhysicalClass' => 'container',
'entPhysicalVendorType' => $location,
'entPhysicalName' => $shortlocation,
'entPhysicalContainedIn' => $parent,
'entPhysicalParentRelPos' => '-1',
2020-09-21 13:40:17 +00:00
];
// Add to the DB and Array.
$id = dbInsert($insert, 'entPhysical');
2020-09-21 13:40:17 +00:00
$entphysical[$location] = dbFetchRow('SELECT * FROM entPhysical WHERE entPhysical_id=?', [$id]);
return $index;
} // end if - Level 1
} // end function
/* idea from https://php.net/manual/en/function.hex2bin.php comments */
function hex2bin_compat($str)
{
if (strlen($str) % 2 !== 0) {
2020-09-21 13:40:17 +00:00
trigger_error(__FUNCTION__ . '(): Hexadecimal input string must have an even length', E_USER_WARNING);
}
2020-09-21 13:40:17 +00:00
2020-09-21 13:59:34 +00:00
return pack('H*', $str);
}
2020-09-21 13:40:17 +00:00
if (! function_exists('hex2bin')) {
// This is only a hack
function hex2bin($str)
{
return hex2bin_compat($str);
}
}
function q_bridge_bits2indices($hex_data)
{
/* convert hex string to an array of 1-based indices of the nonzero bits
* ie. '9a00' -> '100110100000' -> array(1, 4, 5, 7)
*/
$hex_data = str_replace([' ', "\n"], '', $hex_data);
2021-03-30 20:26:01 +00:00
// we need an even number of digits for hex2bin
if (strlen($hex_data) % 2 === 1) {
$hex_data = '0' . $hex_data;
}
$value = hex2bin($hex_data);
$length = strlen($value);
2020-09-21 13:40:17 +00:00
$indices = [];
for ($i = 0; $i < $length; $i++) {
$byte = ord($value[$i]);
for ($j = 7; $j >= 0; $j--) {
if ($byte & (1 << $j)) {
2020-09-21 13:40:17 +00:00
$indices[] = 8 * $i + 8 - $j;
}
}
}
2020-09-21 13:40:17 +00:00
return $indices;
}
2016-10-14 23:14:18 +00:00
function update_device_logo(&$device)
{
$icon = getImageName($device, false);
if ($icon != $device['icon']) {
log_event('Device Icon changed ' . $device['icon'] . " => $icon", $device, 'system', 3);
$device['icon'] = $icon;
2020-09-21 13:40:17 +00:00
dbUpdate(['icon' => $icon], 'devices', 'device_id=?', [$device['device_id']]);
echo "Changed Icon! : $icon\n";
}
}
/**
* Function to generate Mac OUI Cache
*/
function cache_mac_oui()
{
// timers:
$mac_oui_refresh_int_min = 86400 * rand(7, 11); // 7 days + a random number between 0 and 4 days
$mac_oui_cache_time = 1296000; // we keep data during 15 days maximum
$lock = Cache::lock('macouidb-refresh', $mac_oui_refresh_int_min); //We want to refresh after at least $mac_oui_refresh_int_min
if (Config::get('mac_oui.enabled') !== true) {
echo 'Mac OUI integration disabled' . PHP_EOL;
return 0;
}
if ($lock->get()) {
echo 'Caching Mac OUI' . PHP_EOL;
try {
$mac_oui_url = 'https://gitlab.com/wireshark/wireshark/-/raw/master/manuf';
//$mac_oui_url_mirror = 'https://raw.githubusercontent.com/wireshark/wireshark/master/manuf';
echo ' -> Downloading ...' . PHP_EOL;
$get = Http::withOptions(['proxy' => Proxy::forGuzzle()])->get($mac_oui_url);
echo ' -> Processing CSV ...' . PHP_EOL;
$csv_data = $get->body();
foreach (explode("\n", $csv_data) as $csv_line) {
unset($oui);
$entry = str_getcsv($csv_line, "\t");
$length = strlen($entry[0]);
$prefix = strtolower(str_replace(':', '', $entry[0]));
if (is_array($entry) && count($entry) >= 3 && $length == 8) {
// We have a standard OUI xx:xx:xx
$oui = $prefix;
} elseif (is_array($entry) && count($entry) >= 3 && $length == 20) {
// We have a smaller range (xx:xx:xx:X or xx:xx:xx:xx:X)
if (substr($prefix, -2) == '28') {
$oui = substr($prefix, 0, 7);
} elseif (substr($prefix, -2) == '36') {
$oui = substr($prefix, 0, 9);
}
}
if (isset($oui)) {
echo "Adding $oui, $entry[2]" . PHP_EOL;
$key = 'OUIDB-' . $oui;
Cache::put($key, $entry[2], $mac_oui_cache_time);
}
}
} catch (Exception $e) {
echo 'Error processing Mac OUI :' . PHP_EOL;
echo 'Exception: ' . get_class($e) . PHP_EOL;
echo $e->getMessage() . PHP_EOL;
$lock->release(); // we did not succeed so we'll try again next time
return 1;
}
}
return 0;
}
/**
* Function to generate PeeringDB Cache
*/
function cache_peeringdb()
{
if (Config::get('peeringdb.enabled') === true) {
$peeringdb_url = 'https://peeringdb.com/api';
// We cache for 71 hours
2020-09-21 13:59:34 +00:00
$cached = dbFetchCell('SELECT count(*) FROM `pdb_ix` WHERE (UNIX_TIMESTAMP() - timestamp) < 255600');
if ($cached == 0) {
$rand = rand(3, 30);
echo "No cached PeeringDB data found, sleeping for $rand seconds" . PHP_EOL;
sleep($rand);
$peer_keep = [];
$ix_keep = [];
2020-09-21 13:59:34 +00:00
foreach (dbFetchRows('SELECT `bgpLocalAs` FROM `devices` WHERE `disabled` = 0 AND `ignore` = 0 AND `bgpLocalAs` > 0 AND (`bgpLocalAs` < 64512 OR `bgpLocalAs` > 65535) AND `bgpLocalAs` < 4200000000 GROUP BY `bgpLocalAs`') as $as) {
$asn = $as['bgpLocalAs'];
$get = Http::withOptions(['proxy' => Proxy::forGuzzle()])->get($peeringdb_url . '/net?depth=2&asn=' . $asn);
$json_data = $get->body();
$data = json_decode($json_data);
$ixs = $data->{'data'}[0]->{'netixlan_set'};
foreach ($ixs as $ix) {
$ixid = $ix->{'ix_id'};
2020-09-21 13:59:34 +00:00
$tmp_ix = dbFetchRow('SELECT * FROM `pdb_ix` WHERE `ix_id` = ? AND asn = ?', [$ixid, $asn]);
if ($tmp_ix) {
$pdb_ix_id = $tmp_ix['pdb_ix_id'];
2020-09-21 13:40:17 +00:00
$update = ['name' => $ix->{'name'}, 'timestamp' => time()];
dbUpdate($update, 'pdb_ix', '`ix_id` = ? AND `asn` = ?', [$ixid, $asn]);
} else {
2020-09-21 13:40:17 +00:00
$insert = [
'ix_id' => $ixid,
'name' => $ix->{'name'},
'asn' => $asn,
2020-09-21 13:40:17 +00:00
'timestamp' => time(),
];
$pdb_ix_id = dbInsert($insert, 'pdb_ix');
}
$ix_keep[] = $pdb_ix_id;
$get_ix = Http::withOptions(['proxy' => Proxy::forGuzzle()])->get("$peeringdb_url/netixlan?ix_id=$ixid");
$ix_json = $get_ix->body();
$ix_data = json_decode($ix_json);
$peers = $ix_data->{'data'};
foreach ($peers as $index => $peer) {
$peer_name = get_astext($peer->{'asn'});
2020-09-21 13:59:34 +00:00
$tmp_peer = dbFetchRow('SELECT * FROM `pdb_ix_peers` WHERE `peer_id` = ? AND `ix_id` = ?', [$peer->{'id'}, $ixid]);
if ($tmp_peer) {
$peer_keep[] = $tmp_peer['pdb_ix_peers_id'];
2020-09-21 13:40:17 +00:00
$update = [
'remote_asn' => $peer->{'asn'},
'remote_ipaddr4' => $peer->{'ipaddr4'},
'remote_ipaddr6' => $peer->{'ipaddr6'},
'name' => $peer_name,
2020-09-21 13:40:17 +00:00
];
dbUpdate($update, 'pdb_ix_peers', '`pdb_ix_peers_id` = ?', [$tmp_peer['pdb_ix_peers_id']]);
} else {
2020-09-21 13:40:17 +00:00
$peer_insert = [
'ix_id' => $ixid,
'peer_id' => $peer->{'id'},
'remote_asn' => $peer->{'asn'},
'remote_ipaddr4' => $peer->{'ipaddr4'},
'remote_ipaddr6' => $peer->{'ipaddr6'},
'name' => $peer_name,
2020-09-21 13:40:17 +00:00
'timestamp' => time(),
];
$peer_keep[] = dbInsert($peer_insert, 'pdb_ix_peers');
}
}
}
}
// cleanup
if (empty($peer_keep)) {
dbDelete('pdb_ix_peers');
} else {
2020-09-21 13:59:34 +00:00
dbDelete('pdb_ix_peers', '`pdb_ix_peers_id` NOT IN ' . dbGenPlaceholders(count($peer_keep)), $peer_keep);
}
if (empty($ix_keep)) {
dbDelete('pdb_ix');
} else {
2020-09-21 13:59:34 +00:00
dbDelete('pdb_ix', '`pdb_ix_id` NOT IN ' . dbGenPlaceholders(count($ix_keep)), $ix_keep);
}
} else {
2020-09-21 13:59:34 +00:00
echo 'Cached PeeringDB data found.....' . PHP_EOL;
}
} else {
echo 'Peering DB integration disabled' . PHP_EOL;
}
}
/**
* Get an array of the schema files.
* schema_version => full_file_name
*
* @return mixed
*/
function get_schema_list()
{
// glob returns an array sorted by filename
$files = glob(Config::get('install_dir') . '/sql-schema/*.sql');
// set the keys to the db schema version
$files = array_reduce($files, function ($array, $file) {
2020-09-21 13:40:17 +00:00
$array[(int) basename($file, '.sql')] = $file;
return $array;
}, []);
ksort($files); // fix dbSchema 1000 order
2020-09-21 13:40:17 +00:00
return $files;
}
feature: Wireless Sensors Overhaul (#6471) * feature: Wireless Sensors Includes client counts for ios and unifi Graphing could use some improvement. Alerting and threshold ui not implemented WIP: starting OO based wireless sensors. Class based functionality working remove old functional files add schema file discovery needs to be enabled, not polling fix up schema fix Unifi discovery not returning an array Add some debug when discovering a sensor. Fix style. Add missing semicolin Add a null object (Generic) for OS. Fill out some phpdocs Re-organized code Each sensor type now has it's own discovery and polling interface Custom polling tested with Unifi CCQ Left to do: Implement UI (Graphs and Custom thresholds) Alerting Testing Fix event message text Remove runDiscovery and runPolling from OS, they are unused and don't belong there. Cleanups/docs Missed this file. Remove the requirement to fetch the current value to check validity. Do that automatically if current is not specified A few cleanups here and there First pass at graphing. device_ and wireless_ graphs added. Add RouterOS support Singleton OS instance isn't required right now. Remove that to allow some memory to be freed. Add wireless to the device list metrics. Make all metrics clickable Tweak graphs a bit Implement limit configuration page. Use sensors page as common code instead of duplicating. Clean up some javascript interactions: Allow enter on values to save. Cancel if update is not needed. Enable the clear custom button after setting a custom value. Add some wireless alert rules to the library. Add documentation. Add unifi client counts by ssid in addition to radio. Optimize Sensor polling a bit. Add HP MSM clients support (for full controller) Fix function accessibility Formalize the discovery and poller interfaces. Add Xirrus clients and noise floor move module interfaces to a more appropriate place. push caching code up to os, unsure about this do to the limitations No point in selectively enabling wireless discovery. We only discover if the device supports something. Add RSSI, Power, and Rate. Add these sensors for Ubnt Airos. Clean up some copyrights. Reduce the amount of files need to add new types. Leave graph files for consistency and to allow customization. Remove the old wifi clients graph completely. ciscowlc should have improved counts (total and per-ssid) Schema didn't get added. Impelement the rest of the AirOS sensors Reformat and re-organize the Airos.php class. Add several UBNT AirFiber sensors A few fixes add links to the section headers Add HP MSM mibs. * Schema file got dropped in rebase. * Add wireless menu to view sensors across all devices. Icons in the menu need help :/ * Add HeliOS, Mimosa, and Siklu support Sensors added SNR + Noise * Add power and utilization to Unifi * Update polling to prefetch all sensor data in a few snmp requests as possible * Add Extendair: tx+rx power, aggregate rate, frequency * Add a check for duplicate sensors in discovery. Just print an error for now. * Add Bit Error Ratio (named error-ratio to allow for bit error rate to be added if needed) Fix an incorrect link in the wireless sensors table * Add error rate and change all bps and Hz to use si units * Fixes to limits and frequency display * Fix overview graph frequency display A few decimal place tweaks * Don't allow switching sensor and wireless-sensor graphs, it doesn't work. Change individual distance graphs to use si units * Go through the OS and make sure I got all the sensors I can (probably missed some still) Because pollWirelessChannelAsFrequency() is generic and a little complex, so pull it up to OS. Message to help developers adding supports that don't return an array from discover functions. * Fix some issues * Remove noise and signal for now at least A couple more fixes Add a notification * Oopsie * Bonus AirFiber sensors
2017-05-02 04:49:11 +00:00
/**
* @param $device
* @return int|null
*/
function get_device_oid_limit($device)
{
// device takes priority
if (! empty($device['attribs']['snmp_max_oid'])) {
return $device['attribs']['snmp_max_oid'];
}
feature: Wireless Sensors Overhaul (#6471) * feature: Wireless Sensors Includes client counts for ios and unifi Graphing could use some improvement. Alerting and threshold ui not implemented WIP: starting OO based wireless sensors. Class based functionality working remove old functional files add schema file discovery needs to be enabled, not polling fix up schema fix Unifi discovery not returning an array Add some debug when discovering a sensor. Fix style. Add missing semicolin Add a null object (Generic) for OS. Fill out some phpdocs Re-organized code Each sensor type now has it's own discovery and polling interface Custom polling tested with Unifi CCQ Left to do: Implement UI (Graphs and Custom thresholds) Alerting Testing Fix event message text Remove runDiscovery and runPolling from OS, they are unused and don't belong there. Cleanups/docs Missed this file. Remove the requirement to fetch the current value to check validity. Do that automatically if current is not specified A few cleanups here and there First pass at graphing. device_ and wireless_ graphs added. Add RouterOS support Singleton OS instance isn't required right now. Remove that to allow some memory to be freed. Add wireless to the device list metrics. Make all metrics clickable Tweak graphs a bit Implement limit configuration page. Use sensors page as common code instead of duplicating. Clean up some javascript interactions: Allow enter on values to save. Cancel if update is not needed. Enable the clear custom button after setting a custom value. Add some wireless alert rules to the library. Add documentation. Add unifi client counts by ssid in addition to radio. Optimize Sensor polling a bit. Add HP MSM clients support (for full controller) Fix function accessibility Formalize the discovery and poller interfaces. Add Xirrus clients and noise floor move module interfaces to a more appropriate place. push caching code up to os, unsure about this do to the limitations No point in selectively enabling wireless discovery. We only discover if the device supports something. Add RSSI, Power, and Rate. Add these sensors for Ubnt Airos. Clean up some copyrights. Reduce the amount of files need to add new types. Leave graph files for consistency and to allow customization. Remove the old wifi clients graph completely. ciscowlc should have improved counts (total and per-ssid) Schema didn't get added. Impelement the rest of the AirOS sensors Reformat and re-organize the Airos.php class. Add several UBNT AirFiber sensors A few fixes add links to the section headers Add HP MSM mibs. * Schema file got dropped in rebase. * Add wireless menu to view sensors across all devices. Icons in the menu need help :/ * Add HeliOS, Mimosa, and Siklu support Sensors added SNR + Noise * Add power and utilization to Unifi * Update polling to prefetch all sensor data in a few snmp requests as possible * Add Extendair: tx+rx power, aggregate rate, frequency * Add a check for duplicate sensors in discovery. Just print an error for now. * Add Bit Error Ratio (named error-ratio to allow for bit error rate to be added if needed) Fix an incorrect link in the wireless sensors table * Add error rate and change all bps and Hz to use si units * Fixes to limits and frequency display * Fix overview graph frequency display A few decimal place tweaks * Don't allow switching sensor and wireless-sensor graphs, it doesn't work. Change individual distance graphs to use si units * Go through the OS and make sure I got all the sensors I can (probably missed some still) Because pollWirelessChannelAsFrequency() is generic and a little complex, so pull it up to OS. Message to help developers adding supports that don't return an array from discover functions. * Fix some issues * Remove noise and signal for now at least A couple more fixes Add a notification * Oopsie * Bonus AirFiber sensors
2017-05-02 04:49:11 +00:00
// then os
$os_max = Config::getOsSetting($device['os'], 'snmp_max_oid', 0);
if ($os_max > 0) {
return $os_max;
feature: Wireless Sensors Overhaul (#6471) * feature: Wireless Sensors Includes client counts for ios and unifi Graphing could use some improvement. Alerting and threshold ui not implemented WIP: starting OO based wireless sensors. Class based functionality working remove old functional files add schema file discovery needs to be enabled, not polling fix up schema fix Unifi discovery not returning an array Add some debug when discovering a sensor. Fix style. Add missing semicolin Add a null object (Generic) for OS. Fill out some phpdocs Re-organized code Each sensor type now has it's own discovery and polling interface Custom polling tested with Unifi CCQ Left to do: Implement UI (Graphs and Custom thresholds) Alerting Testing Fix event message text Remove runDiscovery and runPolling from OS, they are unused and don't belong there. Cleanups/docs Missed this file. Remove the requirement to fetch the current value to check validity. Do that automatically if current is not specified A few cleanups here and there First pass at graphing. device_ and wireless_ graphs added. Add RouterOS support Singleton OS instance isn't required right now. Remove that to allow some memory to be freed. Add wireless to the device list metrics. Make all metrics clickable Tweak graphs a bit Implement limit configuration page. Use sensors page as common code instead of duplicating. Clean up some javascript interactions: Allow enter on values to save. Cancel if update is not needed. Enable the clear custom button after setting a custom value. Add some wireless alert rules to the library. Add documentation. Add unifi client counts by ssid in addition to radio. Optimize Sensor polling a bit. Add HP MSM clients support (for full controller) Fix function accessibility Formalize the discovery and poller interfaces. Add Xirrus clients and noise floor move module interfaces to a more appropriate place. push caching code up to os, unsure about this do to the limitations No point in selectively enabling wireless discovery. We only discover if the device supports something. Add RSSI, Power, and Rate. Add these sensors for Ubnt Airos. Clean up some copyrights. Reduce the amount of files need to add new types. Leave graph files for consistency and to allow customization. Remove the old wifi clients graph completely. ciscowlc should have improved counts (total and per-ssid) Schema didn't get added. Impelement the rest of the AirOS sensors Reformat and re-organize the Airos.php class. Add several UBNT AirFiber sensors A few fixes add links to the section headers Add HP MSM mibs. * Schema file got dropped in rebase. * Add wireless menu to view sensors across all devices. Icons in the menu need help :/ * Add HeliOS, Mimosa, and Siklu support Sensors added SNR + Noise * Add power and utilization to Unifi * Update polling to prefetch all sensor data in a few snmp requests as possible * Add Extendair: tx+rx power, aggregate rate, frequency * Add a check for duplicate sensors in discovery. Just print an error for now. * Add Bit Error Ratio (named error-ratio to allow for bit error rate to be added if needed) Fix an incorrect link in the wireless sensors table * Add error rate and change all bps and Hz to use si units * Fixes to limits and frequency display * Fix overview graph frequency display A few decimal place tweaks * Don't allow switching sensor and wireless-sensor graphs, it doesn't work. Change individual distance graphs to use si units * Go through the OS and make sure I got all the sensors I can (probably missed some still) Because pollWirelessChannelAsFrequency() is generic and a little complex, so pull it up to OS. Message to help developers adding supports that don't return an array from discover functions. * Fix some issues * Remove noise and signal for now at least A couple more fixes Add a notification * Oopsie * Bonus AirFiber sensors
2017-05-02 04:49:11 +00:00
}
// then global
$global_max = Config::get('snmp.max_oid', 10);
2020-09-21 13:40:17 +00:00
return $global_max > 0 ? $global_max : 10;
feature: Wireless Sensors Overhaul (#6471) * feature: Wireless Sensors Includes client counts for ios and unifi Graphing could use some improvement. Alerting and threshold ui not implemented WIP: starting OO based wireless sensors. Class based functionality working remove old functional files add schema file discovery needs to be enabled, not polling fix up schema fix Unifi discovery not returning an array Add some debug when discovering a sensor. Fix style. Add missing semicolin Add a null object (Generic) for OS. Fill out some phpdocs Re-organized code Each sensor type now has it's own discovery and polling interface Custom polling tested with Unifi CCQ Left to do: Implement UI (Graphs and Custom thresholds) Alerting Testing Fix event message text Remove runDiscovery and runPolling from OS, they are unused and don't belong there. Cleanups/docs Missed this file. Remove the requirement to fetch the current value to check validity. Do that automatically if current is not specified A few cleanups here and there First pass at graphing. device_ and wireless_ graphs added. Add RouterOS support Singleton OS instance isn't required right now. Remove that to allow some memory to be freed. Add wireless to the device list metrics. Make all metrics clickable Tweak graphs a bit Implement limit configuration page. Use sensors page as common code instead of duplicating. Clean up some javascript interactions: Allow enter on values to save. Cancel if update is not needed. Enable the clear custom button after setting a custom value. Add some wireless alert rules to the library. Add documentation. Add unifi client counts by ssid in addition to radio. Optimize Sensor polling a bit. Add HP MSM clients support (for full controller) Fix function accessibility Formalize the discovery and poller interfaces. Add Xirrus clients and noise floor move module interfaces to a more appropriate place. push caching code up to os, unsure about this do to the limitations No point in selectively enabling wireless discovery. We only discover if the device supports something. Add RSSI, Power, and Rate. Add these sensors for Ubnt Airos. Clean up some copyrights. Reduce the amount of files need to add new types. Leave graph files for consistency and to allow customization. Remove the old wifi clients graph completely. ciscowlc should have improved counts (total and per-ssid) Schema didn't get added. Impelement the rest of the AirOS sensors Reformat and re-organize the Airos.php class. Add several UBNT AirFiber sensors A few fixes add links to the section headers Add HP MSM mibs. * Schema file got dropped in rebase. * Add wireless menu to view sensors across all devices. Icons in the menu need help :/ * Add HeliOS, Mimosa, and Siklu support Sensors added SNR + Noise * Add power and utilization to Unifi * Update polling to prefetch all sensor data in a few snmp requests as possible * Add Extendair: tx+rx power, aggregate rate, frequency * Add a check for duplicate sensors in discovery. Just print an error for now. * Add Bit Error Ratio (named error-ratio to allow for bit error rate to be added if needed) Fix an incorrect link in the wireless sensors table * Add error rate and change all bps and Hz to use si units * Fixes to limits and frequency display * Fix overview graph frequency display A few decimal place tweaks * Don't allow switching sensor and wireless-sensor graphs, it doesn't work. Change individual distance graphs to use si units * Go through the OS and make sure I got all the sensors I can (probably missed some still) Because pollWirelessChannelAsFrequency() is generic and a little complex, so pull it up to OS. Message to help developers adding supports that don't return an array from discover functions. * Fix some issues * Remove noise and signal for now at least A couple more fixes Add a notification * Oopsie * Bonus AirFiber sensors
2017-05-02 04:49:11 +00:00
}
/**
* If Distributed, create a lock, then purge the mysql table
*
2021-09-08 21:35:56 +00:00
* @param string $table
* @param string $sql
* @return int exit code
*/
function lock_and_purge($table, $sql)
{
$purge_name = $table . '_purge';
$lock = Cache::lock($purge_name, 86000);
if ($lock->get()) {
$purge_days = Config::get($purge_name);
$name = str_replace('_', ' ', ucfirst($table));
if (is_numeric($purge_days)) {
2020-09-21 13:40:17 +00:00
if (dbDelete($table, $sql, [$purge_days])) {
echo "$name cleared for entries over $purge_days days\n";
}
}
$lock->release();
2020-09-21 13:40:17 +00:00
return 0;
}
return -1;
}
/**
* If Distributed, create a lock, then purge the mysql table according to the sql query
*
2021-09-08 21:35:56 +00:00
* @param string $table
* @param string $sql
* @param string $msg
* @return int exit code
*/
function lock_and_purge_query($table, $sql, $msg)
{
$purge_name = $table . '_purge';
$purge_duration = Config::get($purge_name);
2020-09-21 13:40:17 +00:00
if (! (is_numeric($purge_duration) && $purge_duration > 0)) {
return -2;
}
$lock = Cache::lock($purge_name, 86000);
if ($lock->get()) {
2020-09-21 13:40:17 +00:00
if (dbQuery($sql, [$purge_duration])) {
printf($msg, $purge_duration);
}
$lock->release();
2020-09-21 13:40:17 +00:00
return 0;
}
2020-09-21 13:40:17 +00:00
return -1;
}
/**
* Check if disk is valid to poll.
* Settings: bad_disk_regexp
*
2021-09-08 21:35:56 +00:00
* @param array $disk
* @param array $device
* @return bool
*/
function is_disk_valid($disk, $device)
{
foreach (Config::getCombined($device['os'], 'bad_disk_regexp') as $bir) {
2020-09-21 13:59:34 +00:00
if (preg_match($bir . 'i', $disk['diskIODevice'])) {
d_echo("Ignored Disk: {$disk['diskIODevice']} (matched: $bir)\n");
2020-09-21 13:40:17 +00:00
return false;
}
}
2020-09-21 13:40:17 +00:00
return true;
}
/**
* Queues a hostname to be refreshed by Oxidized
* Settings: oxidized.url
*
2021-09-08 21:35:56 +00:00
* @param string $hostname
* @param string $msg
* @param string $username
* @return bool
*/
function oxidized_node_update($hostname, $msg, $username = 'not_provided')
{
// Work around https://github.com/rack/rack/issues/337
2020-09-21 13:59:34 +00:00
$msg = str_replace('%', '', $msg);
$postdata = ['user' => $username, 'msg' => $msg];
$oxidized_url = Config::get('oxidized.url');
2020-09-21 13:40:17 +00:00
if (! empty($oxidized_url)) {
$response = Http::withOptions(['proxy' => Proxy::forGuzzle($oxidized_url)])->put("$oxidized_url/node/next/$hostname", $postdata);
2020-09-21 13:40:17 +00:00
if ($response->successful()) {
return true;
}
}
2020-09-21 13:40:17 +00:00
return false;
}//end oxidized_node_update()
/**
* Take a BGP error code and subcode to return a string representation of it
*
* @params int code
* @params int subcode
2021-09-10 18:09:53 +00:00
*
* @return string
*/
function describe_bgp_error_code($code, $subcode)
{
// https://www.iana.org/assignments/bgp-parameters/bgp-parameters.xhtml#bgp-parameters-3
2020-09-21 13:59:34 +00:00
$message = 'Unknown';
2020-09-21 13:59:34 +00:00
$error_code_key = 'bgp.error_codes.' . $code;
$error_subcode_key = 'bgp.error_subcodes.' . $code . '.' . $subcode;
$error_code_message = __($error_code_key);
$error_subcode_message = __($error_subcode_key);
if ($error_subcode_message != $error_subcode_key) {
2020-09-21 13:59:34 +00:00
$message = $error_code_message . ' - ' . $error_subcode_message;
} elseif ($error_code_message != $error_code_key) {
$message = $error_code_message;
}
return $message;
}