Install: Validate database before migrating (#12867)

* Validate database during install
Needed to remove usages of legacy functions in the validation

* Fix output, restore real versions
This commit is contained in:
Tony Murray 2021-05-13 07:18:54 -05:00 committed by GitHub
parent 500b0ac6fa
commit df5096e449
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 77 additions and 69 deletions

View File

@ -28,6 +28,7 @@ use DB;
use Illuminate\Support\Str;
use LibreNMS\Config;
use LibreNMS\Util\Version;
use PDOException;
use Schema as LaravelSchema;
use Symfony\Component\Yaml\Yaml;
@ -111,6 +112,25 @@ class Schema
return $this->schema;
}
/**
* Get the schema version from the previous schema system
*/
public static function getLegacySchema(): int
{
try {
$db = \LibreNMS\DB\Eloquent::DB();
if ($db) {
return (int) $db->table('dbSchema')
->orderBy('version', 'DESC')
->value('version');
}
} catch (PDOException $e) {
// return default
}
return 0;
}
/**
* Get a list of all tables.
*

View File

@ -170,7 +170,7 @@ abstract class Model
*/
public static function onDelete($model)
{
if (isCli()) {
if (\App::runningInConsole()) {
echo '-';
}
}
@ -180,7 +180,7 @@ abstract class Model
*/
public static function onCreate($model)
{
if (isCli()) {
if (\App::runningInConsole()) {
echo '+';
}
}
@ -190,14 +190,14 @@ abstract class Model
*/
public static function onUpdate($model)
{
if (isCli()) {
if (\App::runningInConsole()) {
echo 'U';
}
}
public static function onNoUpdate()
{
if (isCli()) {
if (\App::runningInConsole()) {
echo '.';
}
}

View File

@ -24,6 +24,7 @@
namespace LibreNMS\Util;
use App;
use LibreNMS\Config;
use LibreNMS\Proc;
@ -60,7 +61,7 @@ class Snmpsim
$cmd = $this->getCmd();
if (isCli()) {
if (App::runningInConsole()) {
echo "Starting snmpsim listening on {$this->ip}:{$this->port}... \n";
d_echo($cmd);
}
@ -71,7 +72,7 @@ class Snmpsim
sleep($wait);
}
if (isCli() && ! $this->proc->isRunning()) {
if (App::runningInConsole() && ! $this->proc->isRunning()) {
// if starting failed, run snmpsim again and output to the console and validate the data
passthru($this->getCmd(false) . ' --validate-data');

View File

@ -46,7 +46,7 @@ class Database extends BaseValidation
public function validate(Validator $validator)
{
if (! dbIsConnected()) {
if (! Eloquent::isConnected()) {
return;
}
@ -56,7 +56,7 @@ class Database extends BaseValidation
$this->checkMysqlEngine($validator);
// check database schema version
$current = get_db_schema();
$current = \LibreNMS\DB\Schema::getLegacySchema();
$latest = 1000;
if ($current === 0 || $current === $latest) {
@ -96,16 +96,16 @@ class Database extends BaseValidation
if (version_compare($version[0], self::MARIADB_MIN_VERSION, '<=')) {
$validator->fail(
'MariaDB version ' . self::MARIADB_MIN_VERSION . ' is the minimum supported version as of ' .
self::MARIADB_MIN_VERSION_DATE . '. We recommend you update MariaDB to a supported version ' .
self::MARIADB_RECOMMENDED_VERSION . ' suggested). Failure to update MariaDB will eventually cause issues.'
self::MARIADB_MIN_VERSION_DATE . '. Update MariaDB to a supported version ' .
self::MARIADB_RECOMMENDED_VERSION . ' suggested).'
);
}
} else {
if (version_compare($version[0], self::MYSQL_MIN_VERSION, '<=')) {
$validator->fail(
'MySQL version ' . self::MYSQL_MIN_VERSION . ' is the minimum supported version as of ' .
self::MYSQL_MIN_VERSION_DATE . '. We recommend you update MySQL to a supported version (' .
self::MYSQL_RECOMMENDED_VERSION . ' suggested). Failure to update MySQL will eventually cause issues.'
self::MYSQL_MIN_VERSION_DATE . '. Update MySQL to a supported version (' .
self::MYSQL_RECOMMENDED_VERSION . ' suggested).'
);
}
}
@ -131,7 +131,7 @@ class Database extends BaseValidation
private function checkMode(Validator $validator)
{
// Test for lower case table name support
$lc_mode = dbFetchCell('SELECT @@global.lower_case_table_names');
$lc_mode = Eloquent::DB()->selectOne('SELECT @@global.lower_case_table_names as mode')->mode;
if ($lc_mode != 0) {
$validator->fail(
'You have lower_case_table_names set to 1 or true in mysql config.',
@ -144,7 +144,7 @@ class Database extends BaseValidation
{
$db = \config('database.connections.' . \config('database.default') . '.database');
$query = "SELECT `TABLE_NAME` FROM information_schema.tables WHERE `TABLE_SCHEMA` = '$db' && `ENGINE` != 'InnoDB'";
$tables = dbFetchRows($query);
$tables = Eloquent::DB()->select($query);
if (! empty($tables)) {
$validator->result(
ValidationResult::warn('Some tables are not using the recommended InnoDB engine, this may cause you issues.')
@ -155,14 +155,14 @@ class Database extends BaseValidation
private function checkCollation(Validator $validator)
{
$db_name = dbFetchCell('SELECT DATABASE()');
$db_name = Eloquent::DB()->selectOne('SELECT DATABASE() as name')->name;
// Test for correct character set and collation
$db_collation_sql = "SELECT DEFAULT_CHARACTER_SET_NAME, DEFAULT_COLLATION_NAME
FROM information_schema.SCHEMATA S
WHERE schema_name = '$db_name' AND
( DEFAULT_CHARACTER_SET_NAME != 'utf8mb4' OR DEFAULT_COLLATION_NAME != 'utf8mb4_unicode_ci')";
$collation = dbFetchRows($db_collation_sql);
$collation = Eloquent::DB()->select($db_collation_sql);
if (empty($collation) !== true) {
$validator->fail(
'MySQL Database collation is wrong: ' . implode(' ', $collation[0]),
@ -174,7 +174,7 @@ class Database extends BaseValidation
FROM information_schema.TABLES AS T, information_schema.COLLATION_CHARACTER_SET_APPLICABILITY AS C
WHERE C.collation_name = T.table_collation AND T.table_schema = '$db_name' AND
( C.CHARACTER_SET_NAME != 'utf8mb4' OR C.COLLATION_NAME != 'utf8mb4_unicode_ci' );";
$collation_tables = dbFetchRows($table_collation_sql);
$collation_tables = Eloquent::DB()->select($table_collation_sql);
if (empty($collation_tables) !== true) {
$result = ValidationResult::fail('MySQL tables collation is wrong: ')
->setFix('Check https://community.librenms.org/t/new-default-database-charset-collation/14956 for info on how to fix.')
@ -185,7 +185,7 @@ class Database extends BaseValidation
$column_collation_sql = "SELECT TABLE_NAME, COLUMN_NAME, CHARACTER_SET_NAME, COLLATION_NAME
FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = '$db_name' AND
( CHARACTER_SET_NAME != 'utf8mb4' OR COLLATION_NAME != 'utf8mb4_unicode_ci' );";
$collation_columns = dbFetchRows($column_collation_sql);
$collation_columns = Eloquent::DB()->select($column_collation_sql);
if (empty($collation_columns) !== true) {
$result = ValidationResult::fail('MySQL column collation is wrong: ')
->setFix('Check https://community.librenms.org/t/new-default-database-charset-collation/14956 for info on how to fix.')

View File

@ -24,6 +24,7 @@
namespace LibreNMS\Validations;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Str;
use LibreNMS\Config;
use LibreNMS\Util\EnvHelper;
@ -47,7 +48,7 @@ class User extends BaseValidation
$lnms_groupname = \config('librenms.group');
if (! ($username === 'root' || $username === $lnms_username)) {
if (isCli()) {
if (App::runningInConsole()) {
$validator->fail("You need to run this script as '$lnms_username' or root");
} elseif (function_exists('posix_getgrnam')) {
$lnms_group = posix_getgrnam($lnms_groupname);

View File

@ -24,6 +24,7 @@
namespace LibreNMS;
use App;
use Illuminate\Support\Str;
use LibreNMS\Interfaces\ValidationGroup;
use ReflectionClass;
@ -73,14 +74,14 @@ class Validator
}
if ((empty($validation_groups) && $group->isDefault()) || in_array($group_name, $validation_groups)) {
if ($print_group_status && isCli()) {
if ($print_group_status && App::runningInConsole()) {
echo "Checking $group_name:";
}
/** @var ValidationGroup $group */
$group->validate($this);
if (isCli()) {
if (App::runningInConsole()) {
if ($print_group_status) {
$status = ValidationResult::getStatusText($this->getGroupStatus($group_name));
c_echo(" $status\n");

View File

@ -30,6 +30,8 @@ use Illuminate\Support\Arr;
use LibreNMS\DB\Eloquent;
use LibreNMS\DB\Schema;
use LibreNMS\Interfaces\InstallerStep;
use LibreNMS\ValidationResult;
use LibreNMS\Validator;
use Symfony\Component\HttpFoundation\StreamedResponse;
class DatabaseController extends InstallationController implements InstallerStep
@ -66,17 +68,30 @@ class DatabaseController extends InstallationController implements InstallerStep
session()->forget('install.database'); // reset db complete status
$ok = false;
$message = '';
$messages = [];
try {
$conn = Eloquent::DB('setup');
$ok = $conn && ! is_null($conn->getPdo());
// validate Database
$validator = new Validator();
$validator->validate(['database']);
$results = $validator->getResults('database');
/** @var \LibreNMS\ValidationResult $result */
foreach ($results as $result) {
if ($result->getStatus() == ValidationResult::FAILURE) {
$ok = false;
$messages[] = $result->getMessage();
}
}
} catch (\Exception $e) {
$message = $e->getMessage();
$messages[] = $e->getMessage();
}
return response()->json([
'result' => $ok ? 'ok' : 'fail',
'message' => $message,
'message' => implode('<br />', $messages),
]);
}

View File

@ -12,7 +12,7 @@ use LibreNMS\Config;
$init_modules = ['nodb'];
require __DIR__ . '/includes/init.php';
if (isCli()) {
if (App::runningInConsole()) {
// fill in db variables for legacy external scripts
Config::populateLegacyDbCredentials();

View File

@ -144,18 +144,9 @@ function shorthost($hostname, $len = 12)
return $shorthost;
}
function isCli()
{
if (php_sapi_name() == 'cli' && empty($_SERVER['REMOTE_ADDR'])) {
return true;
} else {
return false;
}
}
function print_error($text)
{
if (isCli()) {
if (App::runningInConsole()) {
c_echo('%r' . $text . "%n\n");
} else {
echo '<div class="alert alert-danger"><i class="fa fa-fw fa-exclamation-circle" aria-hidden="true"></i> ' . $text . '</div>';
@ -164,7 +155,7 @@ function print_error($text)
function print_message($text)
{
if (isCli()) {
if (App::runningInConsole()) {
c_echo('%g' . $text . "%n\n");
} else {
echo '<div class="alert alert-success"><i class="fa fa-fw fa-check-circle" aria-hidden="true"></i> ' . $text . '</div>';
@ -438,7 +429,7 @@ function c_echo($string, $enabled = true)
return;
}
if (isCli()) {
if (App::runningInConsole()) {
global $console_color;
if ($console_color) {
echo $console_color->convert($string);

View File

@ -279,7 +279,7 @@ function renamehost($id, $new, $source = 'console')
function device_discovery_trigger($id)
{
if (isCli() === false) {
if (App::runningInConsole() === false) {
ignore_user_abort(true);
set_time_limit(0);
}
@ -296,7 +296,7 @@ function device_discovery_trigger($id)
function delete_device($id)
{
if (isCli() === false) {
if (App::runningInConsole() === false) {
ignore_user_abort(true);
set_time_limit(0);
}
@ -2017,27 +2017,6 @@ function get_schema_list()
return $files;
}
/**
* Get the current database schema, will return 0 if there is no schema.
*
* @return int
*/
function get_db_schema()
{
try {
$db = \LibreNMS\DB\Eloquent::DB();
if ($db) {
return (int) $db->table('dbSchema')
->orderBy('version', 'DESC')
->value('version');
}
} catch (PDOException $e) {
// return default
}
return 0;
}
/**
* @param $device
* @return int|null

View File

@ -41,7 +41,7 @@ if (config('cache.default') == 'database' && ! \Schema::hasTable('cache_locks'))
$schemaLock = Cache::lock('schema', 86000);
if (! empty($skip_schema_lock) || $schemaLock->get()) {
$db_rev = get_db_schema();
$db_rev = \LibreNMS\DB\Schema::getLegacySchema();
$migrate_opts = ['--force' => true, '--ansi' => true];
@ -93,7 +93,7 @@ if (! empty($skip_schema_lock) || $schemaLock->get()) {
echo "-- Done\n";
// end legacy update
$db_rev = get_db_schema();
$db_rev = \LibreNMS\DB\Schema::getLegacySchema();
}
if ($db_rev == 1000) {

View File

@ -28,7 +28,7 @@ Debug::set(isset($options['d']));
Datastore::init();
// Wait for schema update, as running during update can break update
if (get_db_schema() < 107) {
if (\LibreNMS\DB\Schema::getLegacySchema() < 107) {
logfile('BILLING: Cannot continue until the database schema update to >= 107 is complete');
exit(1);
}

View File

@ -11,12 +11,13 @@ return [
'database' => [
'credentials' => 'Database Credentials',
'host' => 'Host',
'ip_empty' => 'Leave empty if using Host',
'host_placeholder' => 'Use localhost for Unix-Socket',
'name' => 'Database Name',
'password' => 'Password',
'port' => 'Port',
'port_placeholder' => 'Leave empty if using Unix-Socket',
'socket' => 'Unix-Socket',
'socket_empty' => 'Leave empty if using Unix-Socket',
'socket_placeholder' => 'Only use for custom socket path',
'test' => 'Check Credentials',
'title' => 'Configure Database',
'username' => 'User',

View File

@ -28,19 +28,19 @@
<div class="form-row pb-3">
<label for="host" class="col-4 col-form-label text-right">@lang('install.database.host')</label>
<div class="col-6">
<input type="text" class="form-control" name="host" id="host" value="{{ $host ?? 'localhost' }}" placeholder="@lang('install.database.socket_empty')">
<input type="text" class="form-control" name="host" id="host" value="{{ $host ?? 'localhost' }}" placeholder="@lang('install.database.host_placeholder')">
</div>
</div>
<div class="form-row pb-3">
<label for="port" class="col-4 col-form-label text-right">@lang('install.database.port')</label>
<div class="col-6">
<input type="text" class="form-control" name="port" id="port" value="{{ $port ?? 3306 }}" placeholder="@lang('install.database.socket_empty')">
<input type="text" class="form-control" name="port" id="port" value="{{ $port ?? 3306 }}" placeholder="@lang('install.database.port_placeholder')">
</div>
</div>
<div class="form-row pb-3">
<label for="unix_socket" class="col-4 col-form-label text-right">@lang('install.database.socket')</label>
<div class="col-6">
<input type="text" class="form-control" name="unix_socket" id="unix_socket" value="{{ $unix_socket ?? '' }}" placeholder="@lang('install.database.ip_empty')">
<input type="text" class="form-control" name="unix_socket" id="unix_socket" value="{{ $unix_socket ?? '' }}" placeholder="@lang('install.database.socket_placeholder')">
</div>
</div>
<div class="form-row pb-3">

View File

@ -68,7 +68,6 @@ if (! file_exists('vendor/autoload.php')) {
require_once 'vendor/autoload.php';
require_once 'includes/common.php';
require_once 'includes/functions.php';
require_once 'includes/dbFacile.php';
// Buffer output
ob_start();