mirror of
https://github.com/librenms/librenms.git
synced 2024-09-21 18:38:25 +00:00
Enable config:set to set variables inside a nested array of settings (#12772)
* Enable config:set to set variables inside a nested array of settings Re-index arrays when forgetting a value from a sequential numerically indexed array * cleanup
This commit is contained in:
parent
7510224ead
commit
ebadcbc8af
@ -4,6 +4,8 @@ namespace App\Console\Commands;
|
||||
|
||||
use App\Console\Commands\Traits\CompletesConfigArgument;
|
||||
use App\Console\LnmsCommand;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Str;
|
||||
use LibreNMS\Config;
|
||||
use LibreNMS\DB\Eloquent;
|
||||
use LibreNMS\Util\DynamicConfig;
|
||||
@ -39,12 +41,16 @@ class SetConfigCommand extends LnmsCommand
|
||||
$setting = $this->argument('setting');
|
||||
$value = $this->argument('value');
|
||||
$force = $this->option('ignore-checks');
|
||||
$parent = null;
|
||||
|
||||
if (! $force && ! $definition->isValidSetting($setting)) {
|
||||
if (! $definition->isValidSetting($setting)) {
|
||||
$parent = $this->findParentSetting($definition, $setting);
|
||||
if (! $force && ! $parent) {
|
||||
$this->error(trans('commands.config:set.errors.invalid'));
|
||||
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
|
||||
if (! Eloquent::isConnected()) {
|
||||
$this->error(trans('commands.config:set.errors.nodb'));
|
||||
@ -53,16 +59,41 @@ class SetConfigCommand extends LnmsCommand
|
||||
}
|
||||
|
||||
if (! $force && ! $value) {
|
||||
if ($this->confirm(trans('commands.config:set.confirm', ['setting' => $setting]))) {
|
||||
Config::erase($setting);
|
||||
$message = $parent
|
||||
? trans('commands.config:set.forget_from', ['path' => $this->getChildPath($setting, $parent), 'parent' => $parent])
|
||||
: trans('commands.config:set.confirm', ['setting' => $setting]);
|
||||
|
||||
return 0;
|
||||
if ($this->confirm($message)) {
|
||||
return $this->erase($setting, $parent) ? 0 : 1;
|
||||
}
|
||||
|
||||
return 3;
|
||||
}
|
||||
|
||||
$value = $this->juggleType($value);
|
||||
|
||||
// handle appending to arrays
|
||||
if (Str::endsWith($setting, '.+')) {
|
||||
$setting = substr($setting, 0, -2);
|
||||
$sub_data = Config::get($setting, []);
|
||||
if (! is_array($sub_data)) {
|
||||
$this->error(trans('commands.config:set.errors.append'));
|
||||
|
||||
return 2;
|
||||
}
|
||||
|
||||
array_push($sub_data, $value);
|
||||
$value = $sub_data;
|
||||
}
|
||||
|
||||
// handle setting value inside multi-dimensional array
|
||||
if ($parent) {
|
||||
$parent_data = Config::get($parent);
|
||||
Arr::set($parent_data, $this->getChildPath($setting, $parent), $value);
|
||||
$value = $parent_data;
|
||||
$setting = $parent;
|
||||
}
|
||||
|
||||
$configItem = $definition->get($setting);
|
||||
if (! $force && ! $configItem->checkValue($value)) {
|
||||
$message = ($configItem->type || $configItem->validate)
|
||||
@ -93,4 +124,65 @@ class SetConfigCommand extends LnmsCommand
|
||||
|
||||
return json_last_error() ? $value : $json;
|
||||
}
|
||||
|
||||
private function findParentSetting(DynamicConfig $definition, $setting): ?string
|
||||
{
|
||||
$parts = explode('.', $setting);
|
||||
array_pop($parts); // looking for parent, not this setting
|
||||
|
||||
while (! empty($parts)) {
|
||||
$name = implode('.', $parts);
|
||||
if ($definition->isValidSetting($name)) {
|
||||
return $name;
|
||||
}
|
||||
array_pop($parts);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private function erase($setting, $parent = null)
|
||||
{
|
||||
if ($parent) {
|
||||
$data = Config::get($parent);
|
||||
|
||||
if (preg_match("/^$parent\.?(?<sub>.+)\\.(?<index>\\d+)\$/", $setting, $matches)) {
|
||||
// nested inside the parent setting, update just the required part
|
||||
$sub_data = Arr::get($data, $matches['sub']);
|
||||
$this->forgetWithIndex($sub_data, $matches['index']);
|
||||
Arr::set($data, $matches['sub'], $sub_data);
|
||||
} else {
|
||||
// not nested, just forget the setting
|
||||
$this->forgetWithIndex($data, $this->getChildPath($setting, $parent));
|
||||
}
|
||||
|
||||
return Config::persist($parent, $data);
|
||||
}
|
||||
|
||||
return Config::erase($setting);
|
||||
}
|
||||
|
||||
private function getChildPath($setting, $parent = null): string
|
||||
{
|
||||
return ltrim(Str::after($setting, $parent), '.');
|
||||
}
|
||||
|
||||
private function hasSequentialIndex($array): bool
|
||||
{
|
||||
if (! is_array($array) || $array === []) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return array_keys($array) === range(0, count($array) - 1);
|
||||
}
|
||||
|
||||
private function forgetWithIndex(&$data, $matches)
|
||||
{
|
||||
// detect sequentially numeric indexed array so we can re-index the array
|
||||
if ($this->hasSequentialIndex($data)) {
|
||||
array_splice($data, (int) $matches, 1);
|
||||
} else {
|
||||
Arr::forget($data, $matches);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -13,14 +13,16 @@ return [
|
||||
'config:set' => [
|
||||
'description' => 'Set configuration value (or unset)',
|
||||
'arguments' => [
|
||||
'setting' => 'setting to set in dot notation (example: snmp.community.0)',
|
||||
'setting' => 'setting to set in dot notation (example: snmp.community.0) To append to an array suffix with .+',
|
||||
'value' => 'value to set, unset setting if this is omitted',
|
||||
],
|
||||
'options' => [
|
||||
'ignore-checks' => 'Ignore all safety checks',
|
||||
],
|
||||
'confirm' => 'Reset :setting to the default?',
|
||||
'forget_from' => 'Forget :path from :parent?',
|
||||
'errors' => [
|
||||
'append' => 'Cannot append to non-array setting',
|
||||
'failed' => 'Failed to set :setting',
|
||||
'invalid' => 'This is not a valid setting. Please check your spelling',
|
||||
'nodb' => 'Database is not connected',
|
||||
|
Loading…
Reference in New Issue
Block a user