commit 81938d2596888f21ebba4eda1e63ee38a43f27ed Merge: 970abb958 7b868982d Author: BHk24k33|^f~PG Date: Sat Jul 30 00:15:12 2022 +0200 Merge branch 'master' of https://github.com/librenms/librenms commit 7b868982d540d4689bdc6a21e8950961aeb1ba37 Author: Tony Murray Date: Fri Jul 29 11:36:49 2022 -0500 New lnms command to enable and disable plugins (#14147) * New lnms command to enable and disable plugins lnms plugin:disable lnms plugin:enable * cleanup * appease the lint gods * and style * Restore accidental file removal diff --git a/app/Console/Commands/PluginDisable.php b/app/Console/Commands/PluginDisable.php new file mode 100644 index 000000000..094f2f812 --- /dev/null +++ b/app/Console/Commands/PluginDisable.php @@ -0,0 +1,49 @@ +<?php + +namespace App\Console\Commands; + +use App\Console\Commands\Traits\CompletesPluginArgument; +use App\Console\LnmsCommand; +use App\Models\Plugin; +use Symfony\Component\Console\Input\InputArgument; + +class PluginDisable extends LnmsCommand +{ + use CompletesPluginArgument; + + protected $name = 'plugin:disable'; + + public function __construct() + { + parent::__construct(); + $this->addArgument('plugin', InputArgument::REQUIRED); + } + + public function handle(): int + { + try { + $plugin = $this->argument('plugin'); + $query = Plugin::query(); + + if ($plugin !== 'all') { + $query->where('plugin_name', 'like', $plugin); + } + + $updated = $query->update(['plugin_active' => 0]); + + if ($updated == 0 && $query->exists()) { + $this->info(trans('commands.plugin:enable.already_enabled')); + + return 0; + } + + $this->info(trans_choice('commands.plugin:disable.disabled', $updated, ['name' => $updated])); + + return 0; + } catch (\Exception $e) { + $this->error(trans('commands.plugin:disable.failed')); + + return 1; + } + } +} diff --git a/app/Console/Commands/PluginEnable.php b/app/Console/Commands/PluginEnable.php new file mode 100644 index 000000000..325855d2f --- /dev/null +++ b/app/Console/Commands/PluginEnable.php @@ -0,0 +1,52 @@ +<?php + +namespace App\Console\Commands; + +use App\Console\Commands\Traits\CompletesPluginArgument; +use App\Console\LnmsCommand; +use App\Models\Plugin; +use Symfony\Component\Console\Input\InputArgument; + +class PluginEnable extends LnmsCommand +{ + use CompletesPluginArgument; + + protected $name = 'plugin:enable'; + + public function __construct() + { + parent::__construct(); + $this->addArgument('plugin', InputArgument::REQUIRED); + } + + public function handle(): int + { + try { + $plugin = $this->argument('plugin'); + + $query = Plugin::query(); + + if ($plugin !== 'all') { + $query->where('plugin_name', 'like', $plugin) + ->limit(1) + ->orderBy('version', 'DESC'); + } + + $updated = $query->update(['plugin_active' => 1]); + + if ($updated == 0 && $query->exists()) { + $this->info(trans('commands.plugin:enable.already_enabled')); + + return 0; + } + + $this->info(trans_choice('commands.plugin:enable.enabled', $updated, ['count' => $updated])); + + return 0; + } catch (\Exception $e) { + $this->error(trans('commands.plugin:enable.failed')); + + return 1; + } + } +} diff --git a/app/Console/Commands/Traits/CompletesPluginArgument.php b/app/Console/Commands/Traits/CompletesPluginArgument.php new file mode 100644 index 000000000..251899680 --- /dev/null +++ b/app/Console/Commands/Traits/CompletesPluginArgument.php @@ -0,0 +1,48 @@ +<?php +/** + * CompletesPluginArgument.php + * + * -Description- + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * @link https://www.librenms.org + * + * @copyright 2022 Tony Murray + * @author Tony Murray + */ + +namespace App\Console\Commands\Traits; + +use App\Models\Plugin; + +trait CompletesPluginArgument +{ + /** + * @param string $name + * @param string $value + * @param string $previous + * @return array|false + */ + public function completeArgument($name, $value, $previous) + { + if ($name == 'plugin') { + return Plugin::where('plugin_name', 'like', $value . '%') + ->pluck('plugin_name') + ->toArray(); + } + + return false; + } +} diff --git a/resources/lang/en/commands.php b/resources/lang/en/commands.php index cab2d4913..0cabc62dc 100644 --- a/resources/lang/en/commands.php +++ b/resources/lang/en/commands.php @@ -149,6 +149,24 @@ return [ 'optionValue' => 'Selected :option is invalid. Should be one of: :values', ], ], + 'plugin:disable' => [ + 'description' => 'Disable all plugins with the given name', + 'arguments' => [ + 'plugin' => 'The name of the plugin to disable or "all" to disable all plugins', + ], + 'already_disabled' => 'Plugin already disabled', + 'disabled' => ':count plugin disabled|:count plugins disabled', + 'failed' => 'Failed to disable plugin(s)', + ], + 'plugin:enable' => [ + 'description' => 'Enable the newest plugin with the given name', + 'arguments' => [ + 'plugin' => 'The name of the plugin to enable or "all" to disable all plugins', + ], + 'already_enabled' => 'Plugin already enabled', + 'enabled' => ':count plugin enabled|:count plugins enabled', + 'failed' => 'Failed to enable plugin(s)', + ], 'smokeping:generate' => [ 'args-nonsense' => 'Use one of --probes and --targets', 'config-insufficient' => 'In order to generate a smokeping configuration, you must have set "smokeping.probes", "fping", and "fping6" set in your configuration', commit 670f964e9886591c1727b5e9130a044ad40929d1 Author: Jellyfrog Date: Fri Jul 29 18:36:18 2022 +0200 Validate: use "database version" instead of "mysql version" (#14158) * Validate: use "database version" instead of "mysql version" * wip * wip diff --git a/LibreNMS/Poller.php b/LibreNMS/Poller.php index ef1be0ca9..2efb225d9 100644 --- a/LibreNMS/Poller.php +++ b/LibreNMS/Poller.php @@ -43,7 +43,6 @@ use LibreNMS\Util\Debug; use LibreNMS\Util\Dns; use LibreNMS\Util\Git; use LibreNMS\Util\StringHelpers; -use LibreNMS\Util\Version; use Psr\Log\LoggerInterface; use Throwable; @@ -371,7 +370,7 @@ Commit SHA: %s Commit Date: %s DB Schema: %s PHP: %s -MySQL: %s +Database: %s RRDTool: %s SNMP: %s ================================== @@ -380,7 +379,7 @@ EOH, Git::localDate(), vsprintf('%s (%s)', $version->database()), phpversion(), - Version::get()->databaseServer(), + $version->databaseServer(), $version->rrdtool(), $version->netSnmp() )); diff --git a/LibreNMS/Util/Stats.php b/LibreNMS/Util/Stats.php index a31c833ac..7559b7140 100644 --- a/LibreNMS/Util/Stats.php +++ b/LibreNMS/Util/Stats.php @@ -140,7 +140,7 @@ class Stats 'vminfo' => $this->selectTotal('vminfo', ['vm_type']), 'vmware' => $this->selectTotal('vminfo'), 'vrfs' => $this->selectTotal('vrfs'), - 'mysql_version' => $this->selectStatic($version->databaseServer()), + 'database_version' => $this->selectStatic($version->databaseServer()), 'php_version' => $this->selectStatic(phpversion()), 'python_version' => $this->selectStatic($version->python()), 'rrdtool_version' => $this->selectStatic($version->rrdtool()), diff --git a/LibreNMS/Util/Version.php b/LibreNMS/Util/Version.php index 2e0fc1ca2..decf6ff82 100644 --- a/LibreNMS/Util/Version.php +++ b/LibreNMS/Util/Version.php @@ -122,7 +122,20 @@ class Version public function databaseServer(): string { - return Eloquent::isConnected() ? Arr::first(DB::selectOne('select version()')) : 'Not Connected'; + if (! Eloquent::isConnected()) { + return 'Not Connected'; + } + + switch (Eloquent::getDriver()) { + case 'mysql': + $ret = Arr::first(DB::selectOne('select version()')); + + return (str_contains($ret, 'MariaDB') ? 'MariaDB ' : 'MySQL ') . $ret; + case 'sqlite': + return 'SQLite ' . Arr::first(DB::selectOne('select sqlite_version()')); + default: + return 'Unsupported: ' . Eloquent::getDriver(); + } } public function database(): array diff --git a/LibreNMS/Validations/Database/CheckDatabaseServerVersion.php b/LibreNMS/Validations/Database/CheckDatabaseServerVersion.php index 72d05475e..1eb66af02 100644 --- a/LibreNMS/Validations/Database/CheckDatabaseServerVersion.php +++ b/LibreNMS/Validations/Database/CheckDatabaseServerVersion.php @@ -39,22 +39,26 @@ class CheckDatabaseServerVersion implements Validation public function validate(): ValidationResult { $version = Version::get()->databaseServer(); - $version = explode('-', $version); + [$name, $version] = explode(' ', $version, 2); + [$version] = explode('-', $version, 2); - if (isset($version[1]) && $version[1] == 'MariaDB') { - if (version_compare($version[0], Database::MARIADB_MIN_VERSION, '<=')) { - return ValidationResult::fail( - trans('validation.validations.database.CheckDatabaseServerVersion.fail', ['server' => 'MariaDB', 'min' => Database::MARIADB_MIN_VERSION, 'date' => Database::MARIADB_MIN_VERSION_DATE]), - trans('validation.validations.database.CheckDatabaseServerVersion.fix', ['server' => 'MariaDB', 'suggested' => Database::MARIADB_RECOMMENDED_VERSION]), - ); - } - } else { - if (version_compare($version[0], Database::MYSQL_MIN_VERSION, '<=')) { - return ValidationResult::fail( - trans('validation.validations.database.CheckDatabaseServerVersion.fail', ['server' => 'MySQL', 'min' => Database::MYSQL_MIN_VERSION, 'date' => Database::MYSQL_MIN_VERSION_DATE]), - trans('validation.validations.database.CheckDatabaseServerVersion.fix', ['server' => 'MySQL', 'suggested' => Database::MYSQL_RECOMMENDED_VERSION]), - ); - } + switch ($name) { + case 'MariaDB': + if (version_compare($version, Database::MARIADB_MIN_VERSION, '<=')) { + return ValidationResult::fail( + trans('validation.validations.database.CheckDatabaseServerVersion.fail', ['server' => 'MariaDB', 'min' => Database::MARIADB_MIN_VERSION, 'date' => Database::MARIADB_MIN_VERSION_DATE]), + trans('validation.validations.database.CheckDatabaseServerVersion.fix', ['server' => 'MariaDB', 'suggested' => Database::MARIADB_RECOMMENDED_VERSION]), + ); + } + break; + case 'MySQL': + if (version_compare($version, Database::MYSQL_MIN_VERSION, '<=')) { + return ValidationResult::fail( + trans('validation.validations.database.CheckDatabaseServerVersion.fail', ['server' => 'MySQL', 'min' => Database::MYSQL_MIN_VERSION, 'date' => Database::MYSQL_MIN_VERSION_DATE]), + trans('validation.validations.database.CheckDatabaseServerVersion.fix', ['server' => 'MySQL', 'suggested' => Database::MYSQL_RECOMMENDED_VERSION]), + ); + } + break; } return ValidationResult::ok(trans('validation.validations.database.CheckDatabaseServerVersion.ok')); diff --git a/app/Http/Controllers/AboutController.php b/app/Http/Controllers/AboutController.php index 88a99bc44..9059720aa 100644 --- a/app/Http/Controllers/AboutController.php +++ b/app/Http/Controllers/AboutController.php @@ -72,7 +72,7 @@ class AboutController extends Controller 'project_name' => Config::get('project_name'), 'version_local' => $version->local(), - 'version_mysql' => $version->databaseServer(), + 'version_database' => $version->databaseServer(), 'version_php' => phpversion(), 'version_laravel' => App::VERSION(), 'version_python' => $version->python(), diff --git a/discovery.php b/discovery.php index 78c049f06..6e1e67dde 100755 --- a/discovery.php +++ b/discovery.php @@ -72,7 +72,7 @@ Commit SHA: {$versions['local_sha']} Commit Date: {$versions['local_date']} DB Schema: {$versions['db_schema']} PHP: {$versions['php_ver']} -MySQL: {$versions['mysql_ver']} +Database: {$versions['database_ver']} RRDTool: {$versions['rrdtool_ver']} SNMP: {$versions['netsnmp_ver']} ================================== diff --git a/doc/API/System.md b/doc/API/System.md index b09bf14cf..786cdbf7a 100644 --- a/doc/API/System.md +++ b/doc/API/System.md @@ -27,7 +27,7 @@ Output: "local_branch": "master", "db_schema": 249, "php_ver": "7.2.2", - "mysql_ver": "5.5.56-MariaDB", + "database_ver": "MariaDB 5.5.56-MariaDB", "rrdtool_ver": "1.4.8", "netsnmp_ver": "NET-SNMP 5.7.2" } diff --git a/includes/common.php b/includes/common.php index d2c98aedc..5bb22e1bd 100644 --- a/includes/common.php +++ b/includes/common.php @@ -551,7 +551,7 @@ function version_info($remote = false) 'db_schema' => vsprintf('%s (%s)', $version->database()), 'php_ver' => phpversion(), 'python_ver' => $version->python(), - 'mysql_ver' => $version->databaseServer(), + 'database_ver' => $version->databaseServer(), 'rrdtool_ver' => $version->rrdtool(), 'netsnmp_ver' => $version->netSnmp(), ]; diff --git a/poller.php b/poller.php index 5ffbc6fb4..96a3db616 100755 --- a/poller.php +++ b/poller.php @@ -107,7 +107,7 @@ Commit SHA: {$versions['local_sha']} Commit Date: {$versions['local_date']} DB Schema: {$versions['db_schema']} PHP: {$versions['php_ver']} -MySQL: {$versions['mysql_ver']} +Database: {$versions['database_ver']} RRDTool: {$versions['rrdtool_ver']} SNMP: {$versions['netsnmp_ver']} ================================== diff --git a/resources/views/about/index.blade.php b/resources/views/about/index.blade.php index 64b09e01d..4a7555c15 100644 --- a/resources/views/about/index.blade.php +++ b/resources/views/about/index.blade.php @@ -47,8 +47,8 @@ {{ $version_python }} - {{ __('MySQL') }} - {{ $version_mysql }} + {{ __('Database') }} + {{ $version_database }} {{ __('Laravel') }} diff --git a/validate.php b/validate.php index 38d15d9a9..ceedc8e5b 100755 --- a/validate.php +++ b/validate.php @@ -160,7 +160,7 @@ LibreNMS | ${versions['local_ver']} DB Schema | ${versions['db_schema']} PHP | ${versions['php_ver']} Python | ${versions['python_ver']} -MySQL | ${versions['mysql_ver']} +Database | ${versions['database_ver']} RRDTool | ${versions['rrdtool_ver']} SNMP | ${versions['netsnmp_ver']} ==================================== commit 8508710e62ee47fed5d9231e5ef3867a7c624334 Author: Tony Murray Date: Fri Jul 29 08:53:41 2022 -0500 Stats: Collect OS distro and LibreNMS version (#14138) * Stats: Collect OS distro and LibreNMS version Add os distro/version and LibreNMS version to stats collection. To view stats: https://stats.librenms.org * style/lint fixes * Put debug where it will work * One call sends int, which is ok diff --git a/LibreNMS/Util/Stats.php b/LibreNMS/Util/Stats.php new file mode 100644 index 000000000..a31c833ac --- /dev/null +++ b/LibreNMS/Util/Stats.php @@ -0,0 +1,210 @@ +<?php +/* + * Stats.php + * + * -Description- + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * @package LibreNMS + * @link http://librenms.org + * @copyright 2022 Tony Murray + * @author Tony Murray + */ + +namespace LibreNMS\Util; + +use App\Models\Callback; +use DB; +use Illuminate\Database\Query\Builder; +use Illuminate\Support\Collection; +use Illuminate\Support\Str; + +class Stats +{ + public static function submit(): void + { + $stats = new static; + + if ($stats->isEnabled()) { + $response = \Http::withOptions(['proxy' => Proxy::forGuzzle()]) + ->asForm() + ->post(\LibreNMS\Config::get('callback_post'), [ + 'data' => json_encode($stats->collectData()), + ]); + } + } + + public function isEnabled(): bool + { + $enabled = Callback::get('enabled'); + + if ($enabled == 2) { + $this->clearStats(); + + return false; + } + + return $enabled == 1; + } + + public function clearStats(): void + { + $uuid = Callback::get('uuid'); + + $response = \Http::withOptions(['proxy' => Proxy::forGuzzle()]) + ->asForm() + ->post(\LibreNMS\Config::get('callback_clear'), ['uuid' => $uuid]); + + if ($response->successful()) { + Callback::where('name', 'uuid')->delete(); + Callback::set('enabled', 0); + } + } + + private function collectData(): array + { + return [ + 'uuid' => $this->getUuid(), + 'data' => $this->collectStats(), + 'info' => $this->collectDeviceInfo(), + ]; + } + + private function getUuid(): string + { + $uuid = Callback::get('uuid'); + + if (! $uuid) { + $uuid = Str::uuid(); + Callback::set('uuid', $uuid); + } + + return $uuid; + } + + private function collectStats(): array + { + $version = Version::get(); + + return [ + 'alert_rules' => $this->selectTotal(DB::table('alert_rules')->where('disabled', 0), ['severity']), + 'alert_templates' => $this->selectTotal('alert_templates'), + 'api_tokens' => $this->selectTotal(DB::table('api_tokens')->where('disabled', 0)), + 'applications' => $this->selectTotal('applications', ['app_type']), + 'bgppeer_state' => $this->selectTotal('bgpPeers', ['bgpPeerState']), + 'bgppeer_status' => $this->selectTotal('bgpPeers', ['bgpPeerAdminStatus']), + 'bills' => $this->selectTotal('bills', ['bill_type']), + 'cef' => $this->selectTotal('cef_switching'), + 'cisco_asa' => $this->selectTotal(DB::table('ciscoASA')->where('disabled', 0), ['oid']), + 'mempool' => $this->selectTotal('mempools', ['mempool_descr']), + 'dbschema' => $this->selectStatic(DB::table('migrations')->count()), + 'snmp_version' => $this->selectTotal('devices', ['snmpver']), + 'os' => $this->selectTotal('devices', ['os']), + 'type' => $this->selectTotal('devices', ['type']), + 'hardware' => $this->selectTotal('devices', ['hardware']), + 'ipsec' => $this->selectTotal('ipsec_tunnels'), + 'ipv4_addresses' => $this->selectTotal('ipv4_addresses'), + 'ipv4_macaddress' => $this->selectTotal('ipv4_mac'), + 'ipv4_networks' => $this->selectTotal('ipv4_networks'), + 'ipv6_addresses' => $this->selectTotal('ipv6_addresses'), + 'ipv6_networks' => $this->selectTotal('ipv6_networks'), + 'xdp' => $this->selectTotal('links', ['protocol']), + 'ospf' => $this->selectTotal('ospf_instances', ['ospfVersionNumber']), + 'ospf_links' => $this->selectTotal('ospf_ports', ['ospfIfType']), + 'arch' => $this->selectTotal('packages', ['arch']), + 'pollers' => $this->selectTotal('pollers'), + 'port_type' => $this->selectTotal('ports', ['ifType']), + 'port_ifspeed' => DB::table('ports')->select([DB::raw('COUNT(*) AS `total`'), DB::raw('ROUND(`ifSpeed`/1000/1000) as ifSpeed')])->groupBy(['ifSpeed'])->get(), + 'port_vlans' => $this->selectTotal('ports_vlans', ['state']), + 'processes' => $this->selectTotal('processes'), + 'processors' => $this->selectTotal('processors', ['processor_type']), + 'pseudowires' => $this->selectTotal('pseudowires'), + 'sensors' => $this->selectTotal('sensors', ['sensor_class']), + 'sla' => $this->selectTotal('slas', ['rtt_type']), + 'wireless' => $this->selectTotal('wireless_sensors', ['sensor_class']), + 'storage' => $this->selectTotal('storage', ['storage_type']), + 'toner' => $this->selectTotal('printer_supplies', ['supply_type']), + 'vlans' => $this->selectTotal('vlans', ['vlan_type']), + 'vminfo' => $this->selectTotal('vminfo', ['vm_type']), + 'vmware' => $this->selectTotal('vminfo'), + 'vrfs' => $this->selectTotal('vrfs'), + 'mysql_version' => $this->selectStatic($version->databaseServer()), + 'php_version' => $this->selectStatic(phpversion()), + 'python_version' => $this->selectStatic($version->python()), + 'rrdtool_version' => $this->selectStatic($version->rrdtool()), + 'netsnmp_version' => $this->selectStatic($version->netSnmp()), + 'os_version' => $this->selectStatic($version->os()), + 'librenms_release' => $this->selectStatic($version->release(), 'release'), + ]; + } + + private function collectDeviceInfo(): Collection + { + $device_info = DB::table('devices') + ->select([DB::raw('COUNT(*) AS `count`'), 'os', 'sysDescr', 'sysObjectID']) + ->whereNotNull(['sysDescr', 'sysObjectID']) + ->groupBy(['os', 'sysDescr', 'sysObjectID']) + ->get(); + + // sanitize sysDescr + return $device_info->map(function ($entry) { + // remove hostnames from linux, macosx, and SunOS + $entry->sysDescr = preg_replace_callback('/^(Linux |Darwin |FreeBSD |SunOS )[A-Za-z0-9._\-]+ ([0-9.]{3,9})/', function ($matches) { + return $matches[1] . 'hostname ' . $matches[2]; + }, $entry->sysDescr); + + // wipe serial numbers, preserve the format + $sn_patterns = ['/[A-Z]/', '/[a-z]/', '/[0-9]/']; + $sn_replacements = ['A', 'a', '0']; + $entry->sysDescr = preg_replace_callback( + '/((s\/?n|serial num(ber)?)[:=]? ?)([a-z0-9.\-]{4,16})/i', + function ($matches) use ($sn_patterns, $sn_replacements) { + return $matches[1] . preg_replace($sn_patterns, $sn_replacements, $matches[4]); + }, + $entry->sysDescr + ); + + return $entry; + }); + } + + /** + * @param Builder|string $table + * @param array $groups + * @return \Illuminate\Support\Collection + */ + private function selectTotal($table, array $groups = []): Collection + { + $query = $table instanceof Builder ? $table : DB::table($table); + + if (! empty($groups)) { + $query->groupBy($groups); + } + + return $query + ->select(array_merge([DB::raw('COUNT(*) AS `total`')], $groups)) + ->get(); + } + + /** + * @param string|int|float $value + * @param string $name + * @return array[] + */ + private function selectStatic($value, string $name = 'version'): array + { + return [['total' => 1, $name => $value]]; + } +} diff --git a/LibreNMS/Util/Version.php b/LibreNMS/Util/Version.php index d71f20228..2e0fc1ca2 100644 --- a/LibreNMS/Util/Version.php +++ b/LibreNMS/Util/Version.php @@ -53,6 +53,11 @@ class Version return new static; } + public function release(): string + { + return Config::get('update_channel') == 'master' ? 'master' : self::VERSION; + } + public function local(): string { if ($this->is_git_install && $version = $this->fromGit()) { @@ -186,4 +191,36 @@ class Version return $matches[0] ?? ''; } + + /** + * The OS/distribution and version + */ + public function os(): string + { + $info = []; + + // find release file + if (file_exists('/etc/os-release')) { + $info = @parse_ini_file('/etc/os-release'); + } else { + foreach (glob('/etc/*-release') as $file) { + $content = file_get_contents($file); + // normal os release style + $info = @parse_ini_string($content); + if (! empty($info)) { + break; + } + + // just a string of text + if (substr_count($content, PHP_EOL) <= 1) { + $info = ['NAME' => trim(str_replace('release ', '', $content))]; + break; + } + } + } + + $only = array_intersect_key($info, ['NAME' => true, 'VERSION_ID' => true]); + + return implode(' ', $only); + } } diff --git a/daily.php b/daily.php index 98532e5e9..1909876fa 100644 --- a/daily.php +++ b/daily.php @@ -16,11 +16,6 @@ use LibreNMS\Validations\Php; $options = getopt('df:o:t:r:'); -if (isset($options['d'])) { - echo "DEBUG\n"; - Debug::set(); -} - /** * Scripts without dependencies */ @@ -46,6 +41,11 @@ $init_modules = ['alerts']; require __DIR__ . '/includes/init.php'; include_once __DIR__ . '/includes/notifications.php'; +if (isset($options['d'])) { + echo "DEBUG\n"; + Debug::set(); +} + if ($options['f'] === 'update') { if (! Config::get('update')) { exit(0); @@ -126,7 +126,7 @@ if ($options['f'] === 'authlog') { } if ($options['f'] === 'callback') { - include_once 'includes/callback.php'; + \LibreNMS\Util\Stats::submit(); } if ($options['f'] === 'device_perf') { diff --git a/includes/callback.php b/includes/callback.php deleted file mode 100644 index 8b9b31799..000000000 --- a/includes/callback.php +++ /dev/null @@ -1,138 +0,0 @@ -<?php - -/* - * LibreNMS - * - * Copyright (c) 2014 Neil Lathwood - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation, either version 3 of the License, or (at your - * option) any later version. Please see LICENSE.txt at the top level of - * the source code distribution for details. - */ - -$enabled = dbFetchCell("SELECT `value` FROM `callback` WHERE `name` = 'enabled'"); -if ($enabled == 1) { - if (dbFetchCell("SELECT `value` FROM `callback` WHERE `name` = 'uuid'") == '') { - dbInsert(['name' => 'uuid', 'value' => guidv4(openssl_random_pseudo_bytes(16))], 'callback'); - } - - $uuid = dbFetchCell("SELECT `value` FROM `callback` WHERE `name` = 'uuid'"); - - $version = version_info(); - $queries = [ - 'alert_rules' => 'SELECT COUNT(*) AS `total`,`severity` FROM `alert_rules` WHERE `disabled`=0 GROUP BY `severity`', - 'alert_templates' => 'SELECT COUNT(*) AS `total` FROM `alert_templates`', - 'api_tokens' => 'SELECT COUNT(*) AS `total` FROM `api_tokens` WHERE `disabled`=0', - 'applications' => 'SELECT COUNT(*) AS `total`,`app_type` FROM `applications` GROUP BY `app_type`', - 'bgppeer_state' => 'SELECT COUNT(*) AS `total`,`bgpPeerState` FROM `bgpPeers` GROUP BY `bgpPeerState`', - 'bgppeer_status' => 'SELECT COUNT(*) AS `total`,`bgpPeerAdminStatus` FROM `bgpPeers` GROUP BY `bgpPeerAdminStatus`', - 'bills' => 'SELECT COUNT(*) AS `total`,`bill_type` FROM `bills` GROUP BY `bill_type`', - 'cef' => 'SELECT COUNT(*) AS `total` FROM `cef_switching`', - 'cisco_asa' => 'SELECT COUNT(*) AS `total`,`oid` FROM `ciscoASA` WHERE `disabled` = 0 GROUP BY `oid`', - 'mempool' => 'SELECT COUNT(*) AS `total`,`mempool_descr` FROM `mempools` GROUP BY `mempool_descr`', - 'dbschema' => 'SELECT COUNT(*) AS `total`, COUNT(*) AS `version` FROM `migrations`', - 'snmp_version' => 'SELECT COUNT(*) AS `total`,`snmpver` FROM `devices` GROUP BY `snmpver`', - 'os' => 'SELECT COUNT(*) AS `total`,`os` FROM `devices` GROUP BY `os`', - 'type' => 'SELECT COUNT(*) AS `total`,`type` FROM `devices` GROUP BY `type`', - 'hardware' => 'SELECT COUNT(*) AS `total`, `hardware` FROM `devices` GROUP BY `hardware`', - 'ipsec' => 'SELECT COUNT(*) AS `total` FROM `ipsec_tunnels`', - 'ipv4_addresses' => 'SELECT COUNT(*) AS `total` FROM `ipv4_addresses`', - 'ipv4_macaddress' => 'SELECT COUNT(*) AS `total` FROM ipv4_mac', - 'ipv4_networks' => 'SELECT COUNT(*) AS `total` FROM ipv4_networks', - 'ipv6_addresses' => 'SELECT COUNT(*) AS `total` FROM `ipv6_addresses`', - 'ipv6_networks' => 'SELECT COUNT(*) AS `total` FROM `ipv6_networks`', - 'xdp' => 'SELECT COUNT(*) AS `total`,`protocol` FROM `links` GROUP BY `protocol`', - 'ospf' => 'SELECT COUNT(*) AS `total`,`ospfVersionNumber` FROM `ospf_instances` GROUP BY `ospfVersionNumber`', - 'ospf_links' => 'SELECT COUNT(*) AS `total`,`ospfIfType` FROM `ospf_ports` GROUP BY `ospfIfType`', - 'arch' => 'SELECT COUNT(*) AS `total`,`arch` FROM `packages` GROUP BY `arch`', - 'pollers' => 'SELECT COUNT(*) AS `total` FROM `pollers`', - 'port_type' => 'SELECT COUNT(*) AS `total`,`ifType` FROM `ports` GROUP BY `ifType`', - 'port_ifspeed' => 'SELECT COUNT(*) AS `total`,ROUND(`ifSpeed`/1000/1000) FROM `ports` GROUP BY `ifSpeed`', - 'port_vlans' => 'SELECT COUNT(*) AS `total`,`state` FROM `ports_vlans` GROUP BY `state`', - 'processes' => 'SELECT COUNT(*) AS `total` FROM `processes`', - 'processors' => 'SELECT COUNT(*) AS `total`,`processor_type` FROM `processors` GROUP BY `processor_type`', - 'pseudowires' => 'SELECT COUNT(*) AS `total` FROM `pseudowires`', - 'sensors' => 'SELECT COUNT(*) AS `total`,`sensor_class` FROM `sensors` GROUP BY `sensor_class`', - 'sla' => 'SELECT COUNT(*) AS `total`,`rtt_type` FROM `slas` GROUP BY `rtt_type`', - 'wireless' => 'SELECT COUNT(*) AS `total`,`sensor_class` FROM `wireless_sensors` GROUP BY `sensor_class`', - 'storage' => 'SELECT COUNT(*) AS `total`,`storage_type` FROM `storage` GROUP BY `storage_type`', - 'toner' => 'SELECT COUNT(*) AS `total`,`supply_type` FROM `printer_supplies` GROUP BY `supply_type`', - 'vlans' => 'SELECT COUNT(*) AS `total`,`vlan_type` FROM `vlans` GROUP BY `vlan_type`', - 'vminfo' => 'SELECT COUNT(*) AS `total`,`vm_type` FROM `vminfo` GROUP BY `vm_type`', - 'vmware' => 'SELECT COUNT(*) AS `total` FROM `vminfo`', - 'vrfs' => 'SELECT COUNT(*) AS `total` FROM `vrfs`', - 'mysql_version' => 'SELECT 1 AS `total`, @@version AS `version`', - ]; - - foreach ($queries as $name => $query) { - $data = dbFetchRows($query); - $response[$name] = $data; - } - $response['php_version'][] = ['total' => 1, 'version' => $version['php_ver']]; - $response['python_version'][] = ['total' => 1, 'version' => $version['python_ver']]; - $response['rrdtool_version'][] = ['total' => 1, 'version' => $version['rrdtool_ver']]; - $response['netsnmp_version'][] = ['total' => 1, 'version' => $version['netsnmp_ver']]; - - // collect sysDescr and sysObjectID for submission - $device_info = dbFetchRows('SELECT COUNT(*) AS `count`,`os`, `sysDescr`, `sysObjectID` FROM `devices` - WHERE `sysDescr` IS NOT NULL AND `sysObjectID` IS NOT NULL GROUP BY `os`, `sysDescr`, `sysObjectID`'); - - // sanitize sysDescr - $device_info = array_map(function ($entry) { - // remove hostnames from linux, macosx, and SunOS - $entry['sysDescr'] = preg_replace_callback('/^(Linux |Darwin |FreeBSD |SunOS )[A-Za-z0-9._\-]+ ([0-9.]{3,9})/', function ($matches) { - return $matches[1] . 'hostname ' . $matches[2]; - }, $entry['sysDescr']); - - // wipe serial numbers, preserve the format - $sn_patterns = ['/[A-Z]/', '/[a-z]/', '/[0-9]/']; - $sn_replacements = ['A', 'a', '0']; - $entry['sysDescr'] = preg_replace_callback( - '/((s\/?n|serial num(ber)?)[:=]? ?)([a-z0-9.\-]{4,16})/i', - function ($matches) use ($sn_patterns, $sn_replacements) { - return $matches[1] . preg_replace($sn_patterns, $sn_replacements, $matches[4]); - }, - $entry['sysDescr'] - ); - - return $entry; - }, $device_info); - - $output = [ - 'uuid' => $uuid, - 'data' => $response, - 'info' => $device_info, - ]; - $data = json_encode($output); - $submit = ['data' => $data]; - - $fields = ''; - foreach ($submit as $key => $value) { - $fields .= $key . '=' . $value . '&'; - } - - rtrim($fields, '&'); - - $post = curl_init(); - set_curl_proxy($post); - curl_setopt($post, CURLOPT_URL, \LibreNMS\Config::get('callback_post')); - curl_setopt($post, CURLOPT_POST, count($submit)); - curl_setopt($post, CURLOPT_POSTFIELDS, $fields); - curl_setopt($post, CURLOPT_RETURNTRANSFER, 1); - $result = curl_exec($post); -} elseif ($enabled == 2) { - $uuid = dbFetchCell("SELECT `value` FROM `callback` WHERE `name` = 'uuid'"); - $fields = "uuid=$uuid"; - - $clear = curl_init(); - set_curl_proxy($clear); - curl_setopt($clear, CURLOPT_URL, \LibreNMS\Config::get('callback_clear')); - curl_setopt($clear, CURLOPT_POST, count($clear)); - curl_setopt($clear, CURLOPT_POSTFIELDS, $fields); - curl_setopt($clear, CURLOPT_RETURNTRANSFER, 1); - $result = curl_exec($clear); - dbDelete('callback', '`name`="uuid"', []); - dbUpdate(['value' => '0'], 'callback', '`name` = "enabled"', []); -} commit a0e263295d4a88a31941486de1d3f654055cdc10 Author: Félix Bouynot Date: Fri Jul 29 15:48:49 2022 +0200 Update php installation instruction for CentOS 8 to reflect the minimum php version now being 8.1 (#14159) diff --git a/doc/Installation/Install-LibreNMS.md b/doc/Installation/Install-LibreNMS.md index a7b9d2d47..005506dec 100644 --- a/doc/Installation/Install-LibreNMS.md +++ b/doc/Installation/Install-LibreNMS.md @@ -38,16 +38,18 @@ Connect to the server command line and follow the instructions below. === "NGINX" ``` dnf -y install epel-release + dnf -y install dnf-utils http://rpms.remirepo.net/enterprise/remi-release-8.rpm dnf module reset php - dnf module enable php:7.3 + dnf module enable php:8.1 dnf install bash-completion cronie fping git ImageMagick mariadb-server mtr net-snmp net-snmp-utils nginx nmap php-fpm php-cli php-common php-curl php-gd php-gmp php-json php-mbstring php-process php-snmp php-xml php-zip php-mysqlnd python3 python3-PyMySQL python3-redis python3-memcached python3-pip python3-systemd rrdtool unzip ``` === "Apache" ``` dnf -y install epel-release + dnf -y install dnf-utils http://rpms.remirepo.net/enterprise/remi-release-8.rpm dnf module reset php - dnf module enable php:7.3 + dnf module enable php:8.1 dnf install bash-completion cronie fping git httpd ImageMagick mariadb-server mtr net-snmp net-snmp-utils nmap php-fpm php-cli php-common php-curl php-gd php-gmp php-json php-mbstring php-process php-snmp php-xml php-zip php-mysqlnd python3 python3-PyMySQL python3-redis python3-memcached python3-pip python3-systemd rrdtool unzip ``` commit 6ebcde3c04d2acadfe3f13b645fcd14da1db6898 Author: Jellyfrog Date: Fri Jul 29 05:09:09 2022 +0200 Prepare for PHP 8.1 (#14156) diff --git a/LibreNMS/Validations/Php.php b/LibreNMS/Validations/Php.php index aa2fae775..560cfb605 100644 --- a/LibreNMS/Validations/Php.php +++ b/LibreNMS/Validations/Php.php @@ -30,9 +30,9 @@ use LibreNMS\Validator; class Php extends BaseValidation { - const PHP_MIN_VERSION = '7.3'; - const PHP_MIN_VERSION_DATE = 'November, 2020'; - const PHP_RECOMMENDED_VERSION = '8.0'; + const PHP_MIN_VERSION = '8.1'; + const PHP_MIN_VERSION_DATE = 'September, 2022'; + const PHP_RECOMMENDED_VERSION = '8.1'; /** * Validate this module. commit 970abb9589f192f655823ea0407a22ff1062cfeb Merge: 069741141 d248e6561 Author: Robert373 Date: Fri Jul 29 00:15:06 2022 +0200 Merge branch 'master' of https://github.com/librenms/librenms commit d248e6561fcd8965839c5834d7f440ed0cb2a54b Author: rhinoau <78184917> Date: Fri Jul 29 00:38:46 2022 +0800 Standardize device and device group maintenance API (#14153) * Standardise device and devicegroup maintenance API * styleci fixes diff --git a/doc/API/DeviceGroups.md b/doc/API/DeviceGroups.md index 545950457..95a659378 100644 --- a/doc/API/DeviceGroups.md +++ b/doc/API/DeviceGroups.md @@ -150,15 +150,28 @@ Route: `/api/v0/devicesgroups/:name/maintenance` Input (JSON): -- title: Some title for the Maintenance -- notes: Some description for the Maintenance -- start: Start time of Maintenance in format H:m -- duration: Duration of Maintenance in format H:m +- `title`: *optional* - Some title for the Maintenance + Will be replaced with device group name if omitted +- `notes`: *optional* - Some description for the Maintenance +- `start`: *optional* - start time of Maintenance in full format `Y-m-d H:i:00` + eg: 2022-08-01 22:45:00 + Current system time `now()` will be used if omitted +- `duration`: *required* - Duration of Maintenance in format `H:i` / `Hrs:Mins` + eg: 02:00 -Example: +Example with start time: ```curl -curl -X POST -d '{"title":"Device group Maintenance","notes":"A 2 hour Maintenance triggered via API","start":"04:30","duration":"2:00"}' -H 'X-Auth-Token: YOURAPITOKENHERE' https://librenms.org/api/v0/localhost/maintenance +curl -H 'X-Auth-Token: YOURAPITOKENHERE' \ + -X POST https://librenms.org/api/v0/devicegroups/Cisco switches/maintenance/ \ + --data-raw ' +{ + "title":"Device group Maintenance", + "notes":"A 2 hour Maintenance triggered via API with start time", + "start":"2022-08-01 08:00:00", + "duration":"2:00" +} +' ``` Output: @@ -166,6 +179,30 @@ Output: ```json { "status": "ok", - "message": "Device group Cisco switches (2) will begin maintenance mode at 5:00 for 2:00 h" + "message": "Device group Cisco switches (2) will begin maintenance mode at 2022-08-01 22:45:00 for 2:00h" } ``` + +Example with no start time: + +```curl +curl -H 'X-Auth-Token: YOURAPITOKENHERE' \ + -X POST https://librenms.org/api/v0/devicegroups/Cisco switches/maintenance/ \ + --data-raw ' +{ + "title":"Device group Maintenance", + "notes":"A 2 hour Maintenance triggered via API with no start time", + "duration":"2:00" +} +' +``` + +Output: + +```json +{ + "status": "ok", + "message": "Device group Cisco switches (2) moved into maintenance mode for 2:00h" +} +``` + diff --git a/doc/API/Devices.md b/doc/API/Devices.md index e8cb2cab5..ead46d37f 100644 --- a/doc/API/Devices.md +++ b/doc/API/Devices.md @@ -1109,13 +1109,50 @@ Route: `/api/v0/devices/:hostname/maintenance` Input (JSON): -- notes: Some description for the Maintenance -- duration: Duration of Maintenance in format H:m +- `title`: *optional* - Some title for the Maintenance + Will be replaced with hostname if omitted +- `notes`: *optional* - Some description for the Maintenance + Will also be added to device notes if user prefs "Add schedule notes to devices notes" is set +- `start`: *optional* - start time of Maintenance in full format `Y-m-d H:i:00` + eg: 2022-08-01 22:45:00 + Current system time `now()` will be used if omitted +- `duration`: *required* - Duration of Maintenance in format `H:i` / `Hrs:Mins` + eg: 02:00 + +Example with start time: -Example: +```curl +curl -H 'X-Auth-Token: YOURAPITOKENHERE' \ + -X POST https://librenms.org/api/v0/devices/localhost/maintenance/ \ + --data-raw ' + "title":"Device Maintenance", + "notes":"A 2 hour Maintenance triggered via API with start time", + "start":"2022-08-01 08:00:00", + "duration":"2:00" +} +' +``` + +Output: + +```json +{ + "status": "ok", + "message": "Device localhost (1) will begin maintenance mode at 2022-08-01 22:45:00 for 2:00h" +} +``` + +Example with no start time: ```curl -curl -X POST -d '{"notes":"A 2 hour Maintenance triggered via API","duration":"2:00"}' -H 'X-Auth-Token: YOURAPITOKENHERE' https://librenms.org/api/v0/localhost/maintenance +curl -H 'X-Auth-Token: YOURAPITOKENHERE' \ + -X POST https://librenms.org/api/v0/devices/localhost/maintenance/ \ + --data-raw ' + "title":"Device Maintenance", + "notes":"A 2 hour Maintenance triggered via API with no start time", + "duration":"2:00" +} +' ``` Output: @@ -1123,10 +1160,11 @@ Output: ```json { "status": "ok", - "message": "Device localhost.localdomain (57) moved into maintenance mode for 2:00 h" + "message": "Device localhost (1) moved into maintenance mode for 2:00h" } ``` + ### `add_device` Add a new device. Most fields are optional. You may omit snmp diff --git a/includes/html/api_functions.inc.php b/includes/html/api_functions.inc.php index 702994370..2e6e78265 100644 --- a/includes/html/api_functions.inc.php +++ b/includes/html/api_functions.inc.php @@ -457,28 +457,35 @@ function maintenance_device(Illuminate\Http\Request $request) return api_error(400, 'No information has been provided to set this device into maintenance'); } - // This will add a device using the data passed encoded with json $hostname = $request->route('hostname'); // use hostname as device_id if it's all digits - $device = ctype_digit($hostname) ? DeviceCache::get($hostname) : DeviceCache::getByHostname($hostname); + $device = ctype_digit($hostname) ? Device::find($hostname) : Device::findByHostname($hostname); - if (! $device) { - return api_error(404, "Device $hostname does not exist"); + if (is_null($device)) { + return api_error(404, "Device $hostname not found"); + } + + if (! $request->json('duration')) { + return api_error(400, 'Duration not provided'); } $notes = $request->json('notes'); + $title = $request->json('title') ?? $device->displayName(); $alert_schedule = new \App\Models\AlertSchedule([ - 'title' => $device->displayName(), + 'title' => $title, 'notes' => $notes, 'recurring' => 0, - 'start' => date('Y-m-d H:i:s'), ]); + $start = $request->json('start') ?? \Carbon\Carbon::now()->format('Y-m-d H:i:00'); + $alert_schedule->start = $start; + $duration = $request->json('duration'); + if (Str::contains($duration, ':')) { [$duration_hour, $duration_min] = explode(':', $duration); - $alert_schedule->end = \Carbon\Carbon::now() + $alert_schedule->end = \Carbon\Carbon::createFromFormat('Y-m-d H:i:s', $start) ->addHours($duration_hour)->addMinutes($duration_min) ->format('Y-m-d H:i:00'); } @@ -491,7 +498,11 @@ function maintenance_device(Illuminate\Http\Request $request) $device->save(); } - return api_success_noresult(201, "Device {$device->hostname} ({$device->device_id}) moved into maintenance mode" . ($duration ? " for {$duration}h" : '')); + if ($request->json('start')) { + return api_success_noresult(201, "Device {$device->hostname} ({$device->device_id}) will begin maintenance mode at $start" . ($duration ? " for {$duration}h" : '')); + } else { + return api_success_noresult(201, "Device {$device->hostname} ({$device->device_id}) moved into maintenance mode" . ($duration ? " for {$duration}h" : '')); + } } function device_availability(Illuminate\Http\Request $request) @@ -2143,7 +2154,11 @@ function maintenance_devicegroup(Illuminate\Http\Request $request) $device_group = ctype_digit($name) ? DeviceGroup::find($name) : DeviceGroup::where('name', $name)->first(); if (! $device_group) { - return api_error(404, 'Device group not found'); + return api_error(404, "Device group $name not found"); + } + + if (! $request->json('duration')) { + return api_error(400, 'Duration not provided'); } $notes = $request->json('notes'); @@ -2170,7 +2185,11 @@ function maintenance_devicegroup(Illuminate\Http\Request $request) $alert_schedule->save(); $alert_schedule->deviceGroups()->attach($device_group); - return api_success_noresult(201, "Device group {$device_group->name} ({$device_group->id}) will begin maintenance mode at $start" . ($duration ? " for {$duration}h" : '')); + if ($request->json('start')) { + return api_success_noresult(201, "Device group {$device_group->name} ({$device_group->id}) will begin maintenance mode at $start" . ($duration ? " for {$duration}h" : '')); + } else { + return api_success_noresult(201, "Device group {$device_group->name} ({$device_group->id}) moved into maintenance mode" . ($duration ? " for {$duration}h" : '')); + } } function get_devices_by_group(Illuminate\Http\Request $request) commit 91e7b062c5795a51b025dbd9d6dfbe1b4d47c254 Author: Félix Bouynot Date: Thu Jul 28 15:44:23 2022 +0200 Fix typo in Smokeping SELinux documentation (#14155) diff --git a/doc/Extensions/Smokeping.md b/doc/Extensions/Smokeping.md index 8f802d322..f651ba9f3 100644 --- a/doc/Extensions/Smokeping.md +++ b/doc/Extensions/Smokeping.md @@ -314,7 +314,7 @@ If you are using RRDCached with the -B switch and smokeping RRD's inside the Lib ``` cat > smokeping_librenms.te << EOF -odule smokeping_librenms 1.0; +module smokeping_librenms 1.0; require { type httpd_t;