Files

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

328 lines
10 KiB
PHP
Raw Permalink Normal View History

2017-10-26 01:56:09 -05:00
<?php
/**
* Validator.php
*
* Class to run validations. Also allows sharing data between ValidationGroups.
*
* 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
2021-02-09 00:29:04 +01:00
* along with this program. If not, see <https://www.gnu.org/licenses/>.
2017-10-26 01:56:09 -05:00
*
2021-02-09 00:29:04 +01:00
* @link https://www.librenms.org
2021-09-10 20:09:53 +02:00
*
2017-10-26 01:56:09 -05:00
* @copyright 2017 Tony Murray
* @author Tony Murray <murraytony@gmail.com>
*/
namespace LibreNMS;
2020-04-17 17:37:56 -05:00
use Illuminate\Support\Str;
use LibreNMS\Interfaces\ValidationGroup;
use LibreNMS\Util\Laravel;
use ReflectionClass;
2022-04-14 11:22:40 -05:00
use ReflectionException;
2017-10-26 01:56:09 -05:00
class Validator
{
2022-04-14 11:22:40 -05:00
/** @var array */
2017-10-26 01:56:09 -05:00
private $validation_groups = [];
2022-04-14 11:22:40 -05:00
/** @var array */
2017-10-26 01:56:09 -05:00
private $results = [];
2022-04-14 11:22:40 -05:00
/** @var string|null */
2017-10-26 01:56:09 -05:00
private $username;
/**
* Validator constructor.
*/
public function __construct()
{
// load all validations
$pattern = $this->getBaseDir() . '/LibreNMS/Validations/*.php';
2017-10-26 01:56:09 -05:00
foreach (glob($pattern) as $file) {
$class_name = basename($file, '.php');
$class = '\LibreNMS\Validations\\' . $class_name;
2022-04-14 11:22:40 -05:00
try {
$rc = new ReflectionClass($class);
if (! $rc->isAbstract()) {
$validation_name = strtolower($class_name);
$this->validation_groups[$validation_name] = new $class();
$this->results[$validation_name] = [];
}
} catch (ReflectionException $e) {
}
2017-10-26 01:56:09 -05:00
}
}
/**
* Run validations. An empty array will run all default validations.
*
2021-09-08 23:35:56 +02:00
* @param array $validation_groups selected validation groups to run
* @param bool $print_group_status print out group status
2017-10-26 01:56:09 -05:00
*/
2022-04-14 11:22:40 -05:00
public function validate(array $validation_groups = [], bool $print_group_status = false): void
2017-10-26 01:56:09 -05:00
{
foreach ($this->validation_groups as $group_name => $group) {
// only run each group once
if ($group->isCompleted()) {
continue;
}
2017-10-26 01:56:09 -05:00
if ((empty($validation_groups) && $group->isDefault()) || in_array($group_name, $validation_groups)) {
if ($print_group_status && Laravel::isCli()) {
2017-10-26 01:56:09 -05:00
echo "Checking $group_name:";
}
/** @var ValidationGroup $group */
2017-10-26 01:56:09 -05:00
$group->validate($this);
if (Laravel::isCli()) {
2017-10-26 01:56:09 -05:00
if ($print_group_status) {
$status = ValidationResult::getStatusText($this->getGroupStatus($group_name));
c_echo(" $status\n");
}
$this->printResults($group_name);
}
// validation is complete for this group
$group->markCompleted();
2017-10-26 01:56:09 -05:00
}
}
}
/**
* Get the overall status of a validation group.
*
2021-09-08 23:35:56 +02:00
* @param string $validation_group
2017-10-26 01:56:09 -05:00
* @return int
*/
2022-04-14 11:22:40 -05:00
public function getGroupStatus(string $validation_group): int
2017-10-26 01:56:09 -05:00
{
$results = $this->getResults($validation_group);
2022-04-14 11:22:40 -05:00
return array_reduce($results, function ($compound, ValidationResult $result) {
2017-10-26 01:56:09 -05:00
return min($compound, $result->getStatus());
}, ValidationResult::SUCCESS);
}
/**
* Get the ValidationResults for a specific validation group.
*
2022-04-14 11:22:40 -05:00
* @param string|null $validation_group
* @return ValidationResult[]
2017-10-26 01:56:09 -05:00
*/
2022-04-14 11:22:40 -05:00
public function getResults(string $validation_group = null): array
2017-10-26 01:56:09 -05:00
{
if (isset($validation_group)) {
2022-04-14 11:22:40 -05:00
return $this->results[$validation_group] ?? [];
2017-10-26 01:56:09 -05:00
}
return array_reduce($this->results, 'array_merge', []);
}
/**
* Get all of the ValidationResults that have been submitted.
* ValidationResults will be grouped by the validation group.
*
2022-04-14 11:22:40 -05:00
* @return ValidationResult[][]
2017-10-26 01:56:09 -05:00
*/
2022-04-14 11:22:40 -05:00
public function getAllResults(): array
2017-10-26 01:56:09 -05:00
{
return $this->results;
}
/**
* Print all ValidationResults or a group of them.
*
2022-04-14 11:22:40 -05:00
* @param string|null $validation_group
2017-10-26 01:56:09 -05:00
*/
2022-04-14 11:22:40 -05:00
public function printResults(string $validation_group = null): void
2017-10-26 01:56:09 -05:00
{
$results = $this->getResults($validation_group);
foreach ($results as $result) {
$result->consolePrint();
if ($result->hasFixer()) {
$input = readline('Attempt to fix this issue (y or n)?:');
if ($input === 'y') {
$result = app()->make($result->getFixer())->fix();
if ($result) {
echo "Attempted to apply fix.\n";
} else {
echo "Failed to apply fix.\n";
}
}
}
2017-10-26 01:56:09 -05:00
}
}
/**
* Submit a validation result.
* This allows customizing ValidationResults before submitting.
*
2021-09-08 23:35:56 +02:00
* @param ValidationResult $result
2022-04-14 11:22:40 -05:00
* @param string|null $group manually specify the group, otherwise this will be inferred from the callers class name
2017-10-26 01:56:09 -05:00
*/
2022-04-14 11:22:40 -05:00
public function result(ValidationResult $result, string $group = null): void
2017-10-26 01:56:09 -05:00
{
// get the name of the validation that submitted this result
if (empty($group)) {
$group = 'unknown';
$bt = debug_backtrace();
foreach ($bt as $entry) {
2020-04-17 17:37:56 -05:00
if (Str::startsWith($entry['class'], 'LibreNMS\Validations')) {
2017-10-26 01:56:09 -05:00
$group = str_replace('LibreNMS\Validations\\', '', $entry['class']);
break;
}
}
}
$this->results[strtolower($group)][] = $result;
}
/**
* Submit an ok validation result.
*
2021-09-08 23:35:56 +02:00
* @param string $message
2022-04-14 11:22:40 -05:00
* @param string|null $fix
* @param string|null $group manually specify the group, otherwise this will be inferred from the callers class name
2017-10-26 01:56:09 -05:00
*/
2022-04-14 11:22:40 -05:00
public function ok(string $message, string $fix = null, string $group = null): void
2017-10-26 01:56:09 -05:00
{
$this->result(new ValidationResult($message, ValidationResult::SUCCESS, $fix), $group);
}
/**
* Submit a warning validation result.
*
2021-09-08 23:35:56 +02:00
* @param string $message
2022-04-14 11:22:40 -05:00
* @param string|null $fix
* @param string|null $group manually specify the group, otherwise this will be inferred from the callers class name
2017-10-26 01:56:09 -05:00
*/
2022-04-14 11:22:40 -05:00
public function warn(string $message, string $fix = null, string $group = null): void
2017-10-26 01:56:09 -05:00
{
$this->result(new ValidationResult($message, ValidationResult::WARNING, $fix), $group);
}
/**
* Submit a failed validation result.
*
2021-09-08 23:35:56 +02:00
* @param string $message
2022-04-14 11:22:40 -05:00
* @param string|null $fix
* @param string|null $group manually specify the group, otherwise this will be inferred from the callers class name
2017-10-26 01:56:09 -05:00
*/
2022-04-14 11:22:40 -05:00
public function fail(string $message, string $fix = null, string $group = null): void
2017-10-26 01:56:09 -05:00
{
$this->result(new ValidationResult($message, ValidationResult::FAILURE, $fix), $group);
}
/**
* Submit an informational validation result.
*
2021-09-08 23:35:56 +02:00
* @param string $message
2022-04-14 11:22:40 -05:00
* @param string|null $group manually specify the group, otherwise this will be inferred from the callers class name
*/
2022-04-14 11:22:40 -05:00
public function info(string $message, string $group = null): void
{
$this->result(new ValidationResult($message, ValidationResult::INFO), $group);
}
2017-10-26 01:56:09 -05:00
/**
* Execute a command, but don't run it as root. If we are root, run as the LibreNMS user.
* Arguments match exec()
*
2021-09-08 23:35:56 +02:00
* @param string $command the command to run
2022-04-14 11:22:40 -05:00
* @param array|null $output will hold the output of the command
* @param int|null $code will hold the return code from the command
2017-10-26 01:56:09 -05:00
*/
2022-04-14 11:22:40 -05:00
public function execAsUser(string $command, array &$output = null, int &$code = null): void
2017-10-26 01:56:09 -05:00
{
if (self::getUsername() === 'root') {
2020-06-27 22:24:54 -05:00
$command = 'su ' . \config('librenms.user') . ' -s /bin/sh -c "' . $command . '"';
2017-10-26 01:56:09 -05:00
}
exec($command, $output, $code);
}
/**
* Get the username of the user running this and cache it for future requests.
*
* @return string
*/
2022-04-14 11:22:40 -05:00
public function getUsername(): string
2017-10-26 01:56:09 -05:00
{
if (! isset($this->username)) {
if (function_exists('posix_getpwuid')) {
$userinfo = posix_getpwuid(posix_geteuid());
$this->username = $userinfo['name'];
} else {
$this->username = getenv('USERNAME') ?: getenv('USER');
}
}
return $this->username;
}
/**
* Get the base url for this LibreNMS install, this will only work from web pages.
* (unless base_url is set)
*
* @return string the base url without a trailing /
*/
2022-04-14 11:22:40 -05:00
public function getBaseURL(): string
2017-10-26 01:56:09 -05:00
{
2017-10-31 15:32:56 -05:00
$url = function_exists('get_url') ? get_url() : Config::get('base_url');
2020-09-21 14:54:51 +02:00
2017-10-31 15:32:56 -05:00
return rtrim(str_replace('validate', '', $url), '/'); // get base_url from current url
2017-10-26 01:56:09 -05:00
}
2022-04-14 11:22:40 -05:00
public function getBaseDir(): string
{
return realpath(__DIR__ . '/..');
}
2022-04-14 11:22:40 -05:00
public function getStatusText(int $status): string
{
switch ($status) {
case ValidationResult::SUCCESS:
return 'Ok';
case ValidationResult::FAILURE:
return 'Failure';
case ValidationResult::WARNING:
return 'Warning';
case ValidationResult::INFO:
return 'Info';
default:
return '';
}
}
public function toArray(): array
{
return array_map(function (array $results, string $group) {
$groupStatus = $this->getGroupStatus($group);
return [
'group' => $group,
'name' => ucfirst($group),
'status' => $groupStatus,
'statusText' => $this->getStatusText($groupStatus),
'results' => array_map(function (ValidationResult $result) {
return $result->toArray();
}, $results),
];
}, $this->getAllResults(), array_keys($this->getAllResults()));
}
2017-10-26 01:56:09 -05:00
}