mirror of
https://github.com/librenms/librenms.git
synced 2024-10-07 16:52:45 +00:00
version and git helper improvements (#14412)
* Fix up version and git helpers Improve method names Move all git calls into the git helper Allow runtime and external cache of results where appropriate Consolidate version headers for discovery, poller, and validate * Style fixes * improve consistency in git calls * fix style * don't send name inconsistently * Improve database versions * No need to cache Version it is not used more than once currently.
This commit is contained in:
@ -167,7 +167,7 @@ class Schema
|
||||
$update_cache = true;
|
||||
$cache = [];
|
||||
$cache_file = Config::get('install_dir') . "/cache/{$base}_relationships.cache";
|
||||
$db_version = Version::get()->database();
|
||||
$db_version = Version::get()->databaseMigrationCount();
|
||||
|
||||
if (is_file($cache_file)) {
|
||||
$cache = unserialize(file_get_contents($cache_file));
|
||||
|
@ -25,6 +25,7 @@ use LibreNMS\DB\Eloquent;
|
||||
use LibreNMS\Enum\AlertState;
|
||||
use LibreNMS\Util\Number;
|
||||
use LibreNMS\Util\Time;
|
||||
use LibreNMS\Util\Version;
|
||||
use Permissions;
|
||||
|
||||
class IRCBot
|
||||
@ -781,11 +782,9 @@ class IRCBot
|
||||
|
||||
private function _version($params)
|
||||
{
|
||||
$versions = version_info();
|
||||
$schema_version = $versions['db_schema'];
|
||||
$version = $versions['local_ver'];
|
||||
$version = Version::get();
|
||||
|
||||
$msg = $this->config['project_name'] . ', Version: ' . $version . ', DB schema: ' . $schema_version . ', PHP: ' . PHP_VERSION;
|
||||
$msg = $this->config['project_name'] . ', Version: ' . $version->name() . ', DB schema: ' . $version->databaseMigrationCount() . ', PHP: ' . PHP_VERSION;
|
||||
|
||||
return $this->respond($msg);
|
||||
}
|
||||
|
@ -40,9 +40,9 @@ use LibreNMS\Polling\ConnectivityHelper;
|
||||
use LibreNMS\RRD\RrdDefinition;
|
||||
use LibreNMS\Util\Debug;
|
||||
use LibreNMS\Util\Dns;
|
||||
use LibreNMS\Util\Git;
|
||||
use LibreNMS\Util\Module;
|
||||
use LibreNMS\Util\StringHelpers;
|
||||
use LibreNMS\Util\Version;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Throwable;
|
||||
|
||||
@ -362,27 +362,7 @@ EOH, $this->device->hostname, $group ? " ($group)" : '', $this->device->device_i
|
||||
private function printHeader(): void
|
||||
{
|
||||
if (Debug::isEnabled() || Debug::isVerbose()) {
|
||||
$version = \LibreNMS\Util\Version::get();
|
||||
$this->logger->info(sprintf(<<<'EOH'
|
||||
===================================
|
||||
Version info:
|
||||
Commit SHA: %s
|
||||
Commit Date: %s
|
||||
DB Schema: %s
|
||||
PHP: %s
|
||||
Database: %s
|
||||
RRDTool: %s
|
||||
SNMP: %s
|
||||
==================================
|
||||
EOH,
|
||||
Git::localCommit(),
|
||||
Git::localDate(),
|
||||
vsprintf('%s (%s)', $version->database()),
|
||||
phpversion(),
|
||||
$version->databaseServer(),
|
||||
$version->rrdtool(),
|
||||
$version->netSnmp()
|
||||
));
|
||||
$this->logger->info(Version::get()->header());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
55
LibreNMS/Traits/RuntimeClassCache.php
Normal file
55
LibreNMS/Traits/RuntimeClassCache.php
Normal file
@ -0,0 +1,55 @@
|
||||
<?php
|
||||
/**
|
||||
* RuntimeClassCache.php
|
||||
*
|
||||
* Adds the ability to cache the output of functions either on the instance
|
||||
* or in the global cache. Set $runtimeCacheExternalTTL to enable global cache.
|
||||
*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @link https://www.librenms.org
|
||||
*
|
||||
* @copyright 2022 Tony Murray
|
||||
* @author Tony Murray <murraytony@gmail.com>
|
||||
*/
|
||||
|
||||
namespace LibreNMS\Traits;
|
||||
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use LibreNMS\Util\Laravel;
|
||||
|
||||
trait RuntimeClassCache
|
||||
{
|
||||
/** @var array */
|
||||
private $runtimeCache = [];
|
||||
|
||||
/** @var int Setting this installs the data in the external cache to be shared across instances */
|
||||
protected $runtimeCacheExternalTTL = 0;
|
||||
|
||||
/**
|
||||
* We want these each runtime, so don't use the global cache
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
protected function cacheGet(string $name, callable $actual)
|
||||
{
|
||||
if (! array_key_exists($name, $this->runtimeCache)) {
|
||||
$this->runtimeCache[$name] = $this->runtimeCacheExternalTTL && Laravel::isBooted()
|
||||
? Cache::remember('runtimeCache' . __CLASS__ . $name, $this->runtimeCacheExternalTTL, $actual)
|
||||
: $actual();
|
||||
}
|
||||
|
||||
return $this->runtimeCache[$name];
|
||||
}
|
||||
}
|
@ -25,74 +25,210 @@
|
||||
|
||||
namespace LibreNMS\Util;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Contracts\Container\BindingResolutionException;
|
||||
use Illuminate\Http\Client\ConnectionException;
|
||||
use Illuminate\Support\Str;
|
||||
use LibreNMS\Config;
|
||||
use LibreNMS\Traits\RuntimeClassCache;
|
||||
use Symfony\Component\Process\Process;
|
||||
|
||||
class Git
|
||||
{
|
||||
public static function repoPresent(): bool
|
||||
{
|
||||
$install_dir = Config::get('install_dir', realpath(__DIR__ . '/../..'));
|
||||
use RuntimeClassCache;
|
||||
|
||||
return file_exists("$install_dir/.git");
|
||||
/** @var string */
|
||||
private $install_dir;
|
||||
|
||||
public function __construct(int $cache = 0)
|
||||
{
|
||||
$this->runtimeCacheExternalTTL = $cache;
|
||||
$this->install_dir = Config::get('install_dir', realpath(__DIR__ . '/../..'));
|
||||
}
|
||||
|
||||
public static function binaryExists(): bool
|
||||
public static function make(int $cache = 0): Git
|
||||
{
|
||||
exec('git > /dev/null 2>&1', $response, $exit_code);
|
||||
try {
|
||||
$git = app()->make('git'); // get the singleton
|
||||
$git->runtimeCacheExternalTTL = $cache;
|
||||
|
||||
return $exit_code === 1;
|
||||
return $git;
|
||||
} catch (BindingResolutionException $e) {
|
||||
return new static($cache); // no container, just return a regular instance
|
||||
}
|
||||
}
|
||||
|
||||
public static function localCommit(): string
|
||||
public function isAvailable(): bool
|
||||
{
|
||||
return rtrim(exec("git show --pretty='%H' -s HEAD"));
|
||||
return $this->cacheGet('isAvailable', function () {
|
||||
return $this->repoPresent() && $this->binaryExists();
|
||||
});
|
||||
}
|
||||
|
||||
public static function localDate(): Carbon
|
||||
public function repoPresent(): bool
|
||||
{
|
||||
return \Date::createFromTimestamp(exec("git show --pretty='%ct' -s HEAD"));
|
||||
return $this->cacheGet('repoPresent', function () {
|
||||
return file_exists("$this->install_dir/.git");
|
||||
});
|
||||
}
|
||||
|
||||
public static function unchanged(): bool
|
||||
public function binaryExists(): bool
|
||||
{
|
||||
$process = new Process(['git', 'diff-index', '--quiet', 'HEAD']);
|
||||
$process->disableOutput();
|
||||
$process->run();
|
||||
return $this->cacheGet('binaryExists', function () {
|
||||
return $this->run('help', [])->isSuccessful();
|
||||
});
|
||||
}
|
||||
|
||||
return $process->getExitCode() === 0;
|
||||
public function tag(): string
|
||||
{
|
||||
return $this->cacheGet('tag', function () {
|
||||
return $this->isAvailable()
|
||||
? rtrim($this->run('describe', ['--tags'])->getOutput())
|
||||
: '';
|
||||
});
|
||||
}
|
||||
|
||||
public function shortTag(): string
|
||||
{
|
||||
return $this->cacheGet('shortTag', function () {
|
||||
return $this->isAvailable()
|
||||
? rtrim($this->run('describe', ['--tags', '--abbrev=0'])->getOutput())
|
||||
: '';
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the commit hash of the local HEAD commit
|
||||
*/
|
||||
public function commitHash(): string
|
||||
{
|
||||
return $this->headCommit()[0] ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the date of the local HEAD commit
|
||||
*/
|
||||
public function commitDate(): string
|
||||
{
|
||||
return $this->headCommit()[1] ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current branch
|
||||
*/
|
||||
public function branch(): string
|
||||
{
|
||||
return $this->cacheGet('branch', function () {
|
||||
return $this->isAvailable()
|
||||
? rtrim($this->run('rev-parse', ['--abbrev-ref', 'HEAD'])->getOutput())
|
||||
: '';
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect if there are local uncommitted changes.
|
||||
*/
|
||||
public function hasChanges(): bool
|
||||
{
|
||||
return $this->cacheGet('hasChanges', function () {
|
||||
return $this->isAvailable() && ! $this->run('diff-index', ['--quiet', 'HEAD'])->isSuccessful();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Note: It assumes origin/master points to github.com/librenms/librenms for this to work.
|
||||
*/
|
||||
public static function officalCommit(?string $hash = null, string $remote = 'origin/master'): bool
|
||||
public function isOfficialCommit(): bool
|
||||
{
|
||||
if ($hash === null) {
|
||||
$process = new Process(['git', 'rev-parse', 'HEAD']);
|
||||
$process->run();
|
||||
|
||||
$hash = trim($process->getOutput());
|
||||
}
|
||||
|
||||
$process = new Process(['git', 'branch', '--remotes', '--contains', $hash, $remote]);
|
||||
$process->run();
|
||||
|
||||
if ($process->isSuccessful()) {
|
||||
if (trim($process->getOutput()) == $remote) {
|
||||
return true;
|
||||
return $this->cacheGet('isOfficialCommit', function () {
|
||||
if (! $this->isAvailable()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
$process = $this->run('branch', ['--remotes', '--contains', $this->commitHash(), 'origin/master']);
|
||||
|
||||
return $process->isSuccessful() && trim($process->getOutput()) == 'origin/master';
|
||||
});
|
||||
}
|
||||
|
||||
public static function remoteUrl(string $remote = 'origin'): string
|
||||
/**
|
||||
* Get the url of the origin remote
|
||||
*/
|
||||
public function remoteUrl(): string
|
||||
{
|
||||
$process = new Process(['git', 'ls-remote', '--get-url', $remote]);
|
||||
$process->run();
|
||||
return $this->cacheGet('remoteUrl', function () {
|
||||
return $this->isAvailable()
|
||||
? rtrim($this->run('ls-remote', ['--get-url', 'origin'])->getOutput())
|
||||
: '';
|
||||
});
|
||||
}
|
||||
|
||||
return trim($process->getOutput());
|
||||
public function message(): string
|
||||
{
|
||||
return $this->cacheGet('remoteUrl', function () {
|
||||
return $this->isAvailable()
|
||||
? rtrim($this->run('log', ['--pretty=format:%s', '-n', '1'])->getOutput())
|
||||
: '';
|
||||
});
|
||||
}
|
||||
|
||||
public function log(int $lines = 10): string
|
||||
{
|
||||
return $this->cacheGet('changelog' . $lines, function () use ($lines) {
|
||||
return $this->isAvailable()
|
||||
? rtrim($this->run('log', ['-' . $lines])->getOutput())
|
||||
: '';
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the remote commit hash from the github api if on the daily release channel
|
||||
*/
|
||||
public function remoteHash(): string
|
||||
{
|
||||
return $this->remoteCommit()['sha'] ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the remote commit from the github api if on the daily release channel
|
||||
*/
|
||||
private function remoteCommit(): array
|
||||
{
|
||||
return $this->cacheGet('remoteCommit', function () {
|
||||
if ($this->isAvailable()) {
|
||||
try {
|
||||
return (array) \Http::withOptions(['proxy' => Proxy::forGuzzle()])->get(Config::get('github_api') . 'commits/master')->json();
|
||||
} catch (ConnectionException $e) {
|
||||
}
|
||||
}
|
||||
|
||||
return [];
|
||||
});
|
||||
}
|
||||
|
||||
private function headCommit(): array
|
||||
{
|
||||
return $this->cacheGet('headCommit', function () {
|
||||
if (! $this->isAvailable()) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$version_process = $this->run('show', ['--quiet', '--pretty=%H|%ct']);
|
||||
|
||||
// failed due to permissions issue
|
||||
if ($version_process->getExitCode() == 128 && Str::startsWith($version_process->getErrorOutput(), 'fatal: unsafe repository')) {
|
||||
$this->run('config', ['--global', '--add', 'safe.directory', $this->install_dir]); // try to fix
|
||||
$version_process = $this->run('show', ['--quiet', '--pretty=%H|%ct']); // and try again
|
||||
}
|
||||
|
||||
return explode('|', rtrim($version_process->getOutput()));
|
||||
});
|
||||
}
|
||||
|
||||
private function run(string $command, array $options): Process
|
||||
{
|
||||
$version_process = new Process(array_merge(['git', $command], $options), $this->install_dir);
|
||||
$version_process->run();
|
||||
|
||||
return $version_process;
|
||||
}
|
||||
}
|
||||
|
@ -26,29 +26,27 @@
|
||||
namespace LibreNMS\Util;
|
||||
|
||||
use DB;
|
||||
use Illuminate\Contracts\Container\BindingResolutionException;
|
||||
use Illuminate\Http\Client\ConnectionException;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Str;
|
||||
use LibreNMS\Config;
|
||||
use LibreNMS\DB\Eloquent;
|
||||
use Symfony\Component\Process\Process;
|
||||
|
||||
class Version
|
||||
{
|
||||
// Update this on release
|
||||
/** @var string Update this on release */
|
||||
public const VERSION = '22.9.0';
|
||||
|
||||
/** @var array */
|
||||
protected $cache = [];
|
||||
/** @var Git convenience instance */
|
||||
public $git;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->git = Git::make();
|
||||
}
|
||||
|
||||
public static function get(): Version
|
||||
{
|
||||
try {
|
||||
return app()->make('version');
|
||||
} catch (BindingResolutionException $e) {
|
||||
return new static; // no container, just return a fresh instance
|
||||
}
|
||||
return new static;
|
||||
}
|
||||
|
||||
public function release(): string
|
||||
@ -56,90 +54,14 @@ class Version
|
||||
return Config::get('update_channel') == 'master' ? 'master' : self::VERSION;
|
||||
}
|
||||
|
||||
public function local(): string
|
||||
public function date(string $format = 'c'): string
|
||||
{
|
||||
return $this->cacheGet('local_version', function () {
|
||||
if ($this->isGitInstall()) {
|
||||
$version = rtrim(shell_exec('git describe --tags 2>/dev/null'));
|
||||
if ($version) {
|
||||
return $version;
|
||||
}
|
||||
}
|
||||
|
||||
return self::VERSION;
|
||||
});
|
||||
return date($format, $this->git->commitDate() ?: filemtime(__FILE__)); // approximate date for non-git installs
|
||||
}
|
||||
|
||||
public function isGitInstall(): bool
|
||||
public function name(): string
|
||||
{
|
||||
return $this->cacheGet('install_type', function () {
|
||||
return (Git::repoPresent() && Git::binaryExists()) ? 'git' : 'other';
|
||||
}) == 'git';
|
||||
}
|
||||
|
||||
/**
|
||||
* Compiles local commit data
|
||||
*
|
||||
* @return array with keys sha, date, and branch
|
||||
*/
|
||||
public function localCommit(): array
|
||||
{
|
||||
return [
|
||||
'sha' => $this->localCommitSha(),
|
||||
'date' => $this->localDate(),
|
||||
'branch' => $this->localBranch(),
|
||||
];
|
||||
}
|
||||
|
||||
public function localCommitSha(): string
|
||||
{
|
||||
return $this->cacheGet('local_commit_sha', function () {
|
||||
if (! $this->isGitInstall()) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return $this->localCommitData()[0] ?? '';
|
||||
});
|
||||
}
|
||||
|
||||
public function localDate(): string
|
||||
{
|
||||
return $this->cacheGet('local_commit_date', function () {
|
||||
return $this->localCommitData()[1] ?? '';
|
||||
});
|
||||
}
|
||||
|
||||
public function localBranch(): string
|
||||
{
|
||||
return $this->cacheGet('local_branch', function () {
|
||||
if (! $this->isGitInstall()) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$branch_process = new Process(['git', 'rev-parse', '--abbrev-ref', 'HEAD'], Config::get('install_dir'));
|
||||
$branch_process->run();
|
||||
|
||||
return rtrim($branch_process->getOutput());
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the remote commit from the github api if on the daily release channel
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function remoteCommit(): array
|
||||
{
|
||||
return json_decode($this->cacheGet('remote_commit', function () {
|
||||
if ($this->isGitInstall()) {
|
||||
try {
|
||||
return \Http::withOptions(['proxy' => Proxy::forGuzzle()])->get(Config::get('github_api') . 'commits/master')->body();
|
||||
} catch (ConnectionException $e) {
|
||||
}
|
||||
}
|
||||
|
||||
return '[]';
|
||||
}), true);
|
||||
return $this->git->tag() ?: self::VERSION;
|
||||
}
|
||||
|
||||
public function databaseServer(): string
|
||||
@ -160,68 +82,74 @@ class Version
|
||||
}
|
||||
}
|
||||
|
||||
public function database(): array
|
||||
/**
|
||||
* Get the database last migration and count as a string
|
||||
*/
|
||||
public function database(): string
|
||||
{
|
||||
if (Eloquent::isConnected()) {
|
||||
try {
|
||||
$query = Eloquent::DB()->table('migrations');
|
||||
|
||||
return [
|
||||
'last' => $query->orderBy('id', 'desc')->value('migration'),
|
||||
'total' => $query->count(),
|
||||
];
|
||||
} catch (\Exception $e) {
|
||||
return ['last' => 'No Schema', 'total' => 0];
|
||||
}
|
||||
}
|
||||
|
||||
return ['last' => 'Not Connected', 'total' => 0];
|
||||
return sprintf('%s (%s)', $this->lastDatabaseMigration(), $this->databaseMigrationCount());
|
||||
}
|
||||
|
||||
public function gitChangelog(): string
|
||||
/**
|
||||
* Get the total number of migrations applied to the database
|
||||
*/
|
||||
public function databaseMigrationCount(): int
|
||||
{
|
||||
return $this->cacheGet('changelog', function () {
|
||||
return $this->isGitInstall()
|
||||
? rtrim(shell_exec('git log -10'))
|
||||
: '';
|
||||
});
|
||||
try {
|
||||
if (Eloquent::isConnected()) {
|
||||
return Eloquent::DB()->table('migrations')->count();
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the name of the last migration that was applied to the database
|
||||
*/
|
||||
public function lastDatabaseMigration(): string
|
||||
{
|
||||
if (! Eloquent::isConnected()) {
|
||||
return 'Not Connected';
|
||||
}
|
||||
|
||||
try {
|
||||
return Eloquent::DB()->table('migrations')->orderBy('id', 'desc')->value('migration');
|
||||
} catch (\Exception $e) {
|
||||
return 'No Schema';
|
||||
}
|
||||
}
|
||||
|
||||
public function python(): string
|
||||
{
|
||||
return $this->cacheGet('python', function () {
|
||||
$proc = new Process(['python3', '--version']);
|
||||
$proc->run();
|
||||
$proc = new Process(['python3', '--version']);
|
||||
$proc->run();
|
||||
|
||||
if ($proc->getExitCode() !== 0) {
|
||||
return '';
|
||||
}
|
||||
if ($proc->getExitCode() !== 0) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return explode(' ', rtrim($proc->getOutput()), 2)[1] ?? '';
|
||||
});
|
||||
return explode(' ', rtrim($proc->getOutput()), 2)[1] ?? '';
|
||||
}
|
||||
|
||||
public function rrdtool(): string
|
||||
{
|
||||
return $this->cacheGet('rrdtool', function () {
|
||||
$process = new Process([Config::get('rrdtool', 'rrdtool'), '--version']);
|
||||
$process->run();
|
||||
preg_match('/^RRDtool ([\w.]+) /', $process->getOutput(), $matches);
|
||||
$process = new Process([Config::get('rrdtool', 'rrdtool'), '--version']);
|
||||
$process->run();
|
||||
preg_match('/^RRDtool ([\w.]+) /', $process->getOutput(), $matches);
|
||||
|
||||
return str_replace('1.7.01.7.0', '1.7.0', $matches[1] ?? '');
|
||||
});
|
||||
return str_replace('1.7.01.7.0', '1.7.0', $matches[1] ?? '');
|
||||
}
|
||||
|
||||
public function netSnmp(): string
|
||||
{
|
||||
return $this->cacheGet('net-snmp', function () {
|
||||
$process = new Process([Config::get('snmpget', 'snmpget'), '-V']);
|
||||
$process = new Process([Config::get('snmpget', 'snmpget'), '-V']);
|
||||
|
||||
$process->run();
|
||||
preg_match('/[\w.]+$/', $process->getErrorOutput(), $matches);
|
||||
$process->run();
|
||||
preg_match('/[\w.]+$/', $process->getErrorOutput(), $matches);
|
||||
|
||||
return $matches[0] ?? '';
|
||||
});
|
||||
return $matches[0] ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
@ -229,61 +157,61 @@ class Version
|
||||
*/
|
||||
public function os(): string
|
||||
{
|
||||
return $this->cacheGet('os', function () {
|
||||
$info = [];
|
||||
$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;
|
||||
}
|
||||
// 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;
|
||||
}
|
||||
// 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]);
|
||||
$only = array_intersect_key($info, ['NAME' => true, 'VERSION_ID' => true]);
|
||||
|
||||
return implode(' ', $only);
|
||||
});
|
||||
return implode(' ', $only);
|
||||
}
|
||||
|
||||
/**
|
||||
* We want these each runtime, so don't use the global cache
|
||||
* Get a formatted header to print out to the user.
|
||||
*/
|
||||
private function cacheGet(string $name, callable $actual): string
|
||||
public function header(): string
|
||||
{
|
||||
if (! array_key_exists($name, $this->cache)) {
|
||||
$this->cache[$name] = $actual($name);
|
||||
}
|
||||
return sprintf(<<<'EOH'
|
||||
===========================================
|
||||
Component | Version
|
||||
--------- | -------
|
||||
LibreNMS | %s (%s)
|
||||
DB Schema | %s (%s)
|
||||
PHP | %s
|
||||
Python | %s
|
||||
Database | %s
|
||||
RRDTool | %s
|
||||
SNMP | %s
|
||||
===========================================
|
||||
|
||||
return $this->cache[$name];
|
||||
}
|
||||
|
||||
private function localCommitData(): array
|
||||
{
|
||||
return explode('|', $this->cacheGet('local_commit_data', function () {
|
||||
$install_dir = Config::get('install_dir');
|
||||
$version_process = new Process(['git', 'show', '--quiet', '--pretty=%H|%ct'], $install_dir);
|
||||
$version_process->run();
|
||||
|
||||
// failed due to permissions issue
|
||||
if ($version_process->getExitCode() == 128 && Str::startsWith($version_process->getErrorOutput(), 'fatal: unsafe repository')) {
|
||||
(new Process(['git', 'config', '--global', '--add', 'safe.directory', $install_dir]))->run();
|
||||
$version_process->run();
|
||||
}
|
||||
|
||||
return rtrim($version_process->getOutput());
|
||||
}));
|
||||
EOH,
|
||||
$this->name(),
|
||||
$this->date(),
|
||||
$this->lastDatabaseMigration(),
|
||||
$this->databaseMigrationCount(),
|
||||
phpversion(),
|
||||
$this->python(),
|
||||
$this->databaseServer(),
|
||||
$this->rrdtool(),
|
||||
$this->netSnmp()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -47,7 +47,7 @@ class Dependencies extends BaseValidation
|
||||
}
|
||||
|
||||
// if git is not installed, do not assume composer is either
|
||||
if (! Git::repoPresent()) {
|
||||
if (! Git::make()->repoPresent()) {
|
||||
$validator->ok('Installed from package; no Composer required');
|
||||
|
||||
return;
|
||||
|
@ -32,7 +32,6 @@ use LibreNMS\ComposerHelper;
|
||||
use LibreNMS\Config;
|
||||
use LibreNMS\Util\EnvHelper;
|
||||
use LibreNMS\Util\Git;
|
||||
use LibreNMS\Util\Version;
|
||||
use LibreNMS\ValidationResult;
|
||||
use LibreNMS\Validator;
|
||||
use Symfony\Component\Process\Process;
|
||||
@ -47,14 +46,14 @@ class Updates extends BaseValidation
|
||||
return;
|
||||
}
|
||||
|
||||
if (! Git::repoPresent()) {
|
||||
if (! Git::make()->repoPresent()) {
|
||||
$validator->warn('Non-git install, updates are manual or from package');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// if git is not available, we cannot do the other tests
|
||||
if (! Git::binaryExists()) {
|
||||
if (! Git::make()->binaryExists()) {
|
||||
$validator->warn('Unable to locate git. This should probably be installed.');
|
||||
|
||||
return;
|
||||
@ -62,10 +61,9 @@ class Updates extends BaseValidation
|
||||
|
||||
// check if users on master update channel are up to date
|
||||
if (Config::get('update_channel') == 'master') {
|
||||
$local_ver = Version::get()->localCommit();
|
||||
$remote_ver = Version::get()->remoteCommit();
|
||||
if ($local_ver['sha'] != ($remote_ver['sha'] ?? null)) {
|
||||
if (empty($local_ver['date'])) {
|
||||
$git = Git::make();
|
||||
if ($git->commitHash() != $git->remoteHash()) {
|
||||
if (! $git->commitDate()) {
|
||||
$process = new Process(['git', 'show', '--quiet', '--pretty=%H|%ct'], base_path());
|
||||
$process->run();
|
||||
$error = rtrim($process->getErrorOutput());
|
||||
@ -73,7 +71,7 @@ class Updates extends BaseValidation
|
||||
$validator->fail('Failed to fetch version from local git: ' . $error);
|
||||
} else {
|
||||
try {
|
||||
$commit_date = new DateTime('@' . $local_ver['date'], new DateTimeZone(date_default_timezone_get()));
|
||||
$commit_date = new DateTime('@' . $git->commitDate(), new DateTimeZone(date_default_timezone_get()));
|
||||
if ($commit_date->diff(new DateTime())->days > 0) {
|
||||
$validator->warn(
|
||||
'Your install is over 24 hours out of date, last update: ' . $commit_date->format('r'),
|
||||
@ -86,13 +84,14 @@ class Updates extends BaseValidation
|
||||
}
|
||||
}
|
||||
|
||||
if ($local_ver['branch'] != 'master') {
|
||||
if ($local_ver['branch'] == 'php53') {
|
||||
$branch = $git->branch();
|
||||
if ($branch != 'master') {
|
||||
if ($branch == 'php53') {
|
||||
$validator->warn(
|
||||
'You are on the PHP 5.3 support branch, this will prevent automatic updates.',
|
||||
'Update to PHP 5.6.4 or newer (PHP ' . Php::PHP_RECOMMENDED_VERSION . ' recommended) to continue to receive updates.'
|
||||
);
|
||||
} elseif ($local_ver['branch'] == 'php56') {
|
||||
} elseif ($branch == 'php56') {
|
||||
$validator->warn(
|
||||
'You are on the PHP 5.6/7.0 support branch, this will prevent automatic updates.',
|
||||
'Update to PHP ' . Php::PHP_MIN_VERSION . ' or newer (PHP ' . Php::PHP_RECOMMENDED_VERSION . ' recommended) to continue to receive updates.'
|
||||
|
@ -74,7 +74,7 @@ class User extends BaseValidation
|
||||
}
|
||||
|
||||
// if no git, then we probably have different permissions by design
|
||||
if (! Git::repoPresent()) {
|
||||
if (! Git::make()->repoPresent()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -66,12 +66,12 @@ class AboutController extends Controller
|
||||
'callback_status' => $callback_status,
|
||||
'callback_uuid' => $callback_status ? Callback::get('uuid') : null,
|
||||
|
||||
'db_schema' => vsprintf('%s (%s)', $version->database()),
|
||||
'git_log' => $version->gitChangelog(),
|
||||
'git_date' => $version->localDate(),
|
||||
'db_schema' => $version->database(),
|
||||
'git_log' => $version->git->log(),
|
||||
'git_date' => $version->date(),
|
||||
'project_name' => Config::get('project_name'),
|
||||
|
||||
'version_local' => $version->local(),
|
||||
'version_local' => $version->name(),
|
||||
'version_database' => $version->databaseServer(),
|
||||
'version_php' => phpversion(),
|
||||
'version_laravel' => App::version(),
|
||||
|
51
app/Logging/Reporting/Middleware/AddGitInformation.php
Normal file
51
app/Logging/Reporting/Middleware/AddGitInformation.php
Normal file
@ -0,0 +1,51 @@
|
||||
<?php
|
||||
/**
|
||||
* AddGitInformation.php
|
||||
*
|
||||
* Add git information to Flare report, but use a cache so we don't destroy servers
|
||||
*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @link https://www.librenms.org
|
||||
*
|
||||
* @copyright 2022 Tony Murray
|
||||
* @author Tony Murray <murraytony@gmail.com>
|
||||
*/
|
||||
|
||||
namespace App\Logging\Reporting\Middleware;
|
||||
|
||||
use Facade\FlareClient\Report;
|
||||
use LibreNMS\Util\Git;
|
||||
|
||||
class AddGitInformation
|
||||
{
|
||||
/**
|
||||
* @param \Facade\FlareClient\Report $report
|
||||
* @param callable $next next in the pipeline
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle(Report $report, $next)
|
||||
{
|
||||
$git = Git::make(180);
|
||||
|
||||
$report->group('git', [
|
||||
'hash' => $git->commitHash(),
|
||||
'message' => $git->message(),
|
||||
'tag' => $git->shortTag(),
|
||||
'remote' => $git->remoteUrl(),
|
||||
]);
|
||||
|
||||
return $next($report);
|
||||
}
|
||||
}
|
@ -43,7 +43,7 @@ class SetGroups
|
||||
$version = Version::get();
|
||||
|
||||
$report->group('LibreNMS', [
|
||||
'Git version' => $version->local(),
|
||||
'Git version' => $version->name(),
|
||||
'App version' => Version::VERSION,
|
||||
]);
|
||||
|
||||
|
@ -31,8 +31,8 @@ class AppServiceProvider extends ServiceProvider
|
||||
$this->app->singleton('device-cache', function ($app) {
|
||||
return new \LibreNMS\Cache\Device();
|
||||
});
|
||||
$this->app->singleton('version', function ($app) {
|
||||
return new \LibreNMS\Util\Version();
|
||||
$this->app->singleton('git', function ($app) {
|
||||
return new \LibreNMS\Util\Git();
|
||||
});
|
||||
|
||||
$this->app->bind(\App\Models\Device::class, function () {
|
||||
|
@ -25,6 +25,7 @@
|
||||
|
||||
namespace App\Providers;
|
||||
|
||||
use App\Logging\Reporting\Middleware\AddGitInformation;
|
||||
use App\Logging\Reporting\Middleware\CleanContext;
|
||||
use App\Logging\Reporting\Middleware\SetGroups;
|
||||
use ErrorException;
|
||||
@ -69,6 +70,9 @@ class ErrorReportingProvider extends \Facade\Ignition\IgnitionServiceProvider
|
||||
return \LibreNMS\Util\Version::VERSION;
|
||||
});
|
||||
|
||||
// add git information, but cache it unlike the upstream provider
|
||||
Flare::registerMiddleware(AddGitInformation::class);
|
||||
|
||||
// Filter some extra fields for privacy
|
||||
// Move to header middleware when switching to spatie/laravel-ignition
|
||||
Flare::registerMiddleware(CleanContext::class);
|
||||
@ -114,20 +118,21 @@ class ErrorReportingProvider extends \Facade\Ignition\IgnitionServiceProvider
|
||||
}
|
||||
|
||||
// Check git
|
||||
if (Git::repoPresent()) {
|
||||
if (! Str::contains(Git::remoteUrl(), ['git@github.com:librenms/librenms.git', 'https://github.com/librenms/librenms.git'])) {
|
||||
$git = Git::make(180);
|
||||
if ($git->isAvailable()) {
|
||||
if (! Str::contains($git->remoteUrl(), ['git@github.com:librenms/librenms.git', 'https://github.com/librenms/librenms.git'])) {
|
||||
\Log::debug('Reporting disabled because LibreNMS is not from the official repository');
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (! Git::unchanged()) {
|
||||
if ($git->hasChanges()) {
|
||||
\Log::debug('Reporting disabled because LibreNMS is not from the official repository');
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (! Git::officalCommit()) {
|
||||
if (! $git->isOfficialCommit()) {
|
||||
\Log::debug('Reporting disabled due to local modifications');
|
||||
|
||||
return false;
|
||||
|
@ -34,14 +34,14 @@ return [
|
||||
|
||||
'reporting' => [
|
||||
'anonymize_ips' => true,
|
||||
'collect_git_information' => true,
|
||||
'collect_git_information' => false,
|
||||
'report_queries' => true,
|
||||
'maximum_number_of_collected_queries' => 200,
|
||||
'maximum_number_of_collected_queries' => 50,
|
||||
'report_query_bindings' => true,
|
||||
'report_view_data' => true,
|
||||
'grouping_type' => null,
|
||||
'report_logs' => false,
|
||||
'maximum_number_of_collected_logs' => 200,
|
||||
'maximum_number_of_collected_logs' => 50,
|
||||
'censor_request_body_fields' => ['username', 'password', 'sysContact', 'community', 'authname', 'authpass', 'cryptopass'],
|
||||
],
|
||||
|
||||
|
@ -64,19 +64,7 @@ if (isset($options['i']) && $options['i'] && isset($options['n'])) {
|
||||
}
|
||||
|
||||
if (Debug::set(isset($options['d']), false) || isset($options['v'])) {
|
||||
$versions = version_info();
|
||||
echo <<<EOH
|
||||
===================================
|
||||
Version info:
|
||||
Commit SHA: {$versions['local_sha']}
|
||||
Commit Date: {$versions['local_date']}
|
||||
DB Schema: {$versions['db_schema']}
|
||||
PHP: {$versions['php_ver']}
|
||||
Database: {$versions['database_ver']}
|
||||
RRDTool: {$versions['rrdtool_ver']}
|
||||
SNMP: {$versions['netsnmp_ver']}
|
||||
==================================
|
||||
EOH;
|
||||
echo \LibreNMS\Util\Version::get()->header();
|
||||
|
||||
echo "DEBUG!\n";
|
||||
Debug::setVerbose(isset($options['v']));
|
||||
|
@ -532,31 +532,6 @@ function parse_location($location)
|
||||
return false;
|
||||
}//end parse_location()
|
||||
|
||||
/**
|
||||
* Returns version info
|
||||
*
|
||||
* @param bool $remote fetch remote version info from github
|
||||
* @return array
|
||||
*/
|
||||
function version_info($remote = false)
|
||||
{
|
||||
$version = \LibreNMS\Util\Version::get();
|
||||
|
||||
return [
|
||||
'local_ver' => $version->local(),
|
||||
'local_sha' => $version->localCommitSha(),
|
||||
'local_date' => $version->localDate(),
|
||||
'local_branch' => $version->localBranch(),
|
||||
'github' => $remote ? $version->remoteCommit() : null,
|
||||
'db_schema' => vsprintf('%s (%s)', $version->database()),
|
||||
'php_ver' => phpversion(),
|
||||
'python_ver' => $version->python(),
|
||||
'database_ver' => $version->databaseServer(),
|
||||
'rrdtool_ver' => $version->rrdtool(),
|
||||
'netsnmp_ver' => $version->netSnmp(),
|
||||
];
|
||||
}//end version_info()
|
||||
|
||||
/**
|
||||
* Convert a MySQL binary v4 (4-byte) or v6 (16-byte) IP address to a printable string.
|
||||
*
|
||||
|
@ -2931,7 +2931,20 @@ function edit_service_for_host(Illuminate\Http\Request $request)
|
||||
*/
|
||||
function server_info()
|
||||
{
|
||||
$versions = version_info();
|
||||
$version = \LibreNMS\Util\Version::get();
|
||||
|
||||
$versions = [
|
||||
'local_ver' => $version->name(),
|
||||
'local_sha' => $version->git->commitHash(),
|
||||
'local_date' => $version->date(),
|
||||
'local_branch' => $version->git->branch(),
|
||||
'db_schema' => $version->database(),
|
||||
'php_ver' => phpversion(),
|
||||
'python_ver' => $version->python(),
|
||||
'database_ver' => $version->databaseServer(),
|
||||
'rrdtool_ver' => $version->rrdtool(),
|
||||
'netsnmp_ver' => $version->netSnmp(),
|
||||
];
|
||||
|
||||
return api_success([
|
||||
$versions,
|
||||
|
14
poller.php
14
poller.php
@ -99,19 +99,7 @@ if (empty($where)) {
|
||||
}
|
||||
|
||||
if (Debug::set(isset($options['d']), false) || isset($options['v'])) {
|
||||
$versions = version_info();
|
||||
echo <<<EOH
|
||||
===================================
|
||||
Version info:
|
||||
Commit SHA: {$versions['local_sha']}
|
||||
Commit Date: {$versions['local_date']}
|
||||
DB Schema: {$versions['db_schema']}
|
||||
PHP: {$versions['php_ver']}
|
||||
Database: {$versions['database_ver']}
|
||||
RRDTool: {$versions['rrdtool_ver']}
|
||||
SNMP: {$versions['netsnmp_ver']}
|
||||
==================================
|
||||
EOH;
|
||||
echo \LibreNMS\Util\Version::get()->header();
|
||||
|
||||
echo "DEBUG!\n";
|
||||
if (isset($options['v'])) {
|
||||
|
23
validate.php
23
validate.php
@ -80,7 +80,7 @@ register_shutdown_function(function () {
|
||||
spl_autoload_register(function ($class) {
|
||||
@include str_replace('\\', '/', $class) . '.php';
|
||||
});
|
||||
print_header(version_info());
|
||||
print_header();
|
||||
}
|
||||
});
|
||||
|
||||
@ -134,7 +134,7 @@ if (\LibreNMS\DB\Eloquent::isConnected()) {
|
||||
}
|
||||
|
||||
$precheck_complete = true; // disable shutdown function
|
||||
print_header(version_info());
|
||||
print_header();
|
||||
|
||||
if (isset($options['g'])) {
|
||||
$modules = explode(',', $options['g']);
|
||||
@ -147,26 +147,13 @@ if (isset($options['g'])) {
|
||||
// run checks
|
||||
$validator->validate($modules, isset($options['s']) || ! empty($modules));
|
||||
|
||||
function print_header($versions)
|
||||
function print_header()
|
||||
{
|
||||
$output = ob_get_clean();
|
||||
@ob_end_clean();
|
||||
|
||||
echo <<< EOF
|
||||
====================================
|
||||
Component | Version
|
||||
--------- | -------
|
||||
LibreNMS | ${versions['local_ver']}
|
||||
DB Schema | ${versions['db_schema']}
|
||||
PHP | ${versions['php_ver']}
|
||||
Python | ${versions['python_ver']}
|
||||
Database | ${versions['database_ver']}
|
||||
RRDTool | ${versions['rrdtool_ver']}
|
||||
SNMP | ${versions['netsnmp_ver']}
|
||||
====================================
|
||||
|
||||
$output
|
||||
EOF;
|
||||
echo \LibreNMS\Util\Version::get()->header() . PHP_EOL;
|
||||
echo $output;
|
||||
}
|
||||
|
||||
// output matches that of ValidationResult
|
||||
|
Reference in New Issue
Block a user