From 2d1b93e287707e338e4b64840840041e5744d530 Mon Sep 17 00:00:00 2001 From: raviks789 Date: Thu, 22 Aug 2024 13:04:18 +0200 Subject: [PATCH] WIP --- .../Icingadb/Hook/CustomVarEnricherHook.php | 71 ++++++ .../Icingadb/CustomVarEnricher.php | 179 ++++++++++++++ .../ProvidedHook/Icingadb/CustomVarItem.php | 24 ++ .../Icingadb/CustomVarSection.php | 23 ++ .../Icingadb/Widget/Detail/CustomVarTable.php | 36 ++- run.php | 1 + testBasketData.json | 218 ++++++++++++++++++ 7 files changed, 541 insertions(+), 11 deletions(-) create mode 100644 library/Icingadb/Hook/CustomVarEnricherHook.php create mode 100644 library/Icingadb/ProvidedHook/Icingadb/CustomVarEnricher.php create mode 100644 library/Icingadb/ProvidedHook/Icingadb/CustomVarItem.php create mode 100644 library/Icingadb/ProvidedHook/Icingadb/CustomVarSection.php create mode 100644 testBasketData.json diff --git a/library/Icingadb/Hook/CustomVarEnricherHook.php b/library/Icingadb/Hook/CustomVarEnricherHook.php new file mode 100644 index 000000000..3db596274 --- /dev/null +++ b/library/Icingadb/Hook/CustomVarEnricherHook.php @@ -0,0 +1,71 @@ +enrichCustomVars($vars, $object); + $groups[] = $hook->getGroups(); + } catch (Throwable $e) { + Logger::error('Failed to load hook %s:', get_class($hook), $e); + } + } + + $enrichedVars = array_merge([], ...$enrichedVars); + $groups = array_merge([], ...$groups); + foreach ($vars as $key => $var) { + if (array_key_exists($key, $enrichedVars)) { + $label = key($enrichedVars[$key]); + $vars[$label] = $enrichedVars[$key][$label]; + + unset($vars[$key]); + + $key = $label; + } + + foreach ($groups as $group) { + if (array_key_exists($key, $group)) { + unset($vars[$key]); + + break; + } + } + } + + return [$vars, $groups]; + } +} diff --git a/library/Icingadb/ProvidedHook/Icingadb/CustomVarEnricher.php b/library/Icingadb/ProvidedHook/Icingadb/CustomVarEnricher.php new file mode 100644 index 000000000..c7d5c92b9 --- /dev/null +++ b/library/Icingadb/ProvidedHook/Icingadb/CustomVarEnricher.php @@ -0,0 +1,179 @@ +get('db', 'resource')); + if ($object instanceof Host) { + $directorObject = IcingaHost::load($object->name, $connection); + } elseif ($object instanceof Service) { + $directorHost = IcingaHost::load($object->host->name, $connection); + $directorObject = IcingaService::load( + ['object_name' => $object->name, 'host_id' => $directorHost->get('id')], + $connection + ); + } + + $newVars = []; + $this->fieldConfig = (new IcingaObjectFieldLoader($directorObject))->getFields(); + + $this->buildDataListMap($connection); + if ($directorObject) { + foreach ($vars as $varName => $customVar) { + $newVars[$varName] = $this->resolveCustomVarMapping($varName, $customVar, $connection); + } + } else { + $newVars = $vars; + } + + return $newVars; + } + + /** + * Returns the resolved mapping to custom variables in Director + * + * @param string $name + * @param $val + * @param DbConnection $conn + * @param bool $grouping Whether to enable grouping of custom variables into sections + * + * @return array + */ + protected function resolveCustomVarMapping(string $name, $val, DbConnection $conn, bool $grouping = true): array + { + if (isset($this->fieldConfig[$name])) { + /** @var DirectorDatafield $field */ + $field = $this->fieldConfig[$name]; + $dataType = $field->get('datatype'); + + if ($dataType === get_class(new DataTypeDictionary())) { + $label = $field->get('caption'); + $newVarValue = []; + foreach ($val as $nestedVarName => $nestedVarValue) { + if (isset($this->fieldConfig[$nestedVarName]) && is_array($nestedVarValue)) { + $childValues = []; + foreach ($nestedVarValue as $childName => $childValue) { + $childValues[] = $this->resolveCustomVarMapping($childName, $childValue, $conn, false); + } + + $newVarValue[] = [$nestedVarName => array_merge([], ...$childValues)]; + } else { + $newVarValue[] = $this->resolveCustomVarMapping( + $nestedVarName, + $nestedVarValue, + $conn, + false + ); + } + } + + return [$label => array_merge([], ...$newVarValue)]; + } elseif ($dataType === get_class(new DataTypeDatalist())) { + if (isset($this->datalistMaps[$name])) { + $val = $this->datalistMaps[$name][$val]; + } + + $name = $field->get('caption'); + } else { + $name = $field->get('caption'); + } + + if ($grouping && $field->get('category_id') !== null) { + if (! isset($this->groups[$field->getCategoryName()])) { + $this->groups[$field->getCategoryName()] = [$name => $val]; + } else { + $this->groups[$field->getCategoryName()][$name] = $val; + } + } + } elseif (is_array($val)) { + $newValue = []; + foreach ($val as $childName => $childValue) { + $newValue[] = $this->resolveCustomVarMapping($childName, $childValue, $conn, false); + } + + $val = array_merge([], ...$newValue); + } + + return [$name => $val]; + } + + private function buildDataListMap(DbConnection $db) + { + $fieldsWithDataLists = []; + foreach ($this->fieldConfig as $field) { + if ($field->get('datatype') === 'Icinga\Module\Director\DataType\DataTypeDatalist') { + $fieldsWithDataLists[$field->get('id')] = $field; + } + } + + if (! empty($fieldsWithDataLists)) { + $dataListEntries = $db->select()->from( + ['dds' => 'director_datafield_setting'], + [ + 'dds.datafield_id', + 'dde.entry_name', + 'dde.entry_value' + ] + )->join( + ['dde' => 'director_datalist_entry'], + 'CAST(dds.setting_value AS integer) = dde.list_id', + [] + )->where('dds.datafield_id', array_keys($fieldsWithDataLists)) + ->where('dds.setting_name', 'datalist_id'); + + foreach ($dataListEntries as $dataListEntry) { + $field = $fieldsWithDataLists[$dataListEntry->datafield_id]; + $this->datalistMaps[$field->get('varname')][$dataListEntry->entry_name] = $dataListEntry->entry_value; + } + } + } + + public function getGroups(): array + { + return $this->groups; + } +} diff --git a/library/Icingadb/ProvidedHook/Icingadb/CustomVarItem.php b/library/Icingadb/ProvidedHook/Icingadb/CustomVarItem.php new file mode 100644 index 000000000..ddcd38f02 --- /dev/null +++ b/library/Icingadb/ProvidedHook/Icingadb/CustomVarItem.php @@ -0,0 +1,24 @@ +item = $item; + } + + protected function assemble() + { + $this->addHtml($this->item); + } +} diff --git a/library/Icingadb/ProvidedHook/Icingadb/CustomVarSection.php b/library/Icingadb/ProvidedHook/Icingadb/CustomVarSection.php new file mode 100644 index 000000000..171d24913 --- /dev/null +++ b/library/Icingadb/ProvidedHook/Icingadb/CustomVarSection.php @@ -0,0 +1,23 @@ +items[] = $item; + } + + public function assemble() + { + $this->addHtml($this->items); + } +} diff --git a/library/Icingadb/Widget/Detail/CustomVarTable.php b/library/Icingadb/Widget/Detail/CustomVarTable.php index 9d6916b34..6492a3db0 100644 --- a/library/Icingadb/Widget/Detail/CustomVarTable.php +++ b/library/Icingadb/Widget/Detail/CustomVarTable.php @@ -4,6 +4,7 @@ namespace Icinga\Module\Icingadb\Widget\Detail; +use Icinga\Module\Icingadb\Hook\CustomVarEnricherHook; use Icinga\Module\Icingadb\Hook\CustomVarRendererHook; use ipl\Html\Attributes; use ipl\Html\BaseHtmlElement; @@ -11,6 +12,7 @@ use ipl\Html\HtmlDocument; use ipl\Html\HtmlElement; use ipl\Html\Text; +use ipl\Html\ValidHtml; use ipl\Orm\Model; use ipl\Web\Widget\EmptyState; use ipl\Web\Widget\Icon; @@ -101,11 +103,11 @@ protected function addRow($name, $value) protected function renderVar($name, $value) { if ($this->object !== null && $this->level === 0) { - list($name, $value, $group) = call_user_func($this->hookApplier, $name, $value); - if ($group !== null) { - $this->groups[$group][] = [$name, $value]; - return; - } +// list($name, $value, $group) = call_user_func($this->hookApplier, $name, $value); +// if ($group !== null) { +// $this->groups[$group][] = [$name, $value]; +// return; +// } } $isArray = is_array($value); @@ -116,6 +118,8 @@ protected function renderVar($name, $value) case $isArray: $this->renderObject($name, $value); break; + case $value instanceof ValidHtml: + // Todo: Needs new implementation default: $this->renderScalar($name, $value); } @@ -213,9 +217,9 @@ protected function renderGroup(string $name, $entries) protected function assemble() { - if ($this->object !== null) { - $this->hookApplier = CustomVarRendererHook::prepareForObject($this->object); - } +// if ($this->object !== null) { +// $this->hookApplier = CustomVarRendererHook::prepareForObject($this->object); +// } if ($this->headerTitle !== null) { $this->getAttributes() @@ -248,7 +252,17 @@ protected function assemble() ksort($this->data); } - foreach ($this->data as $name => $value) { + $groups = []; + if ($this->object !== null) { + list($enrichedCustomVars, $groups) = CustomVarEnricherHook::prepareEnrichedCustomVars( + (array) $this->data, + $this->object + ); + } else { + $enrichedCustomVars = $this->data; + } + + foreach ($enrichedCustomVars as $name => $value) { $this->renderVar($name, $value); } @@ -256,12 +270,12 @@ protected function assemble() // Hooks can return objects as replacement for keys, hence a generator is needed for group entries $genGenerator = function ($entries) { - foreach ($entries as list($key, $value)) { + foreach ($entries as $key => $value) { yield $key => $value; } }; - foreach ($this->groups as $group => $entries) { + foreach ($groups as $group => $entries) { $this->renderGroup($group, $genGenerator($entries)); } } diff --git a/run.php b/run.php index b4c803222..ea7df5e25 100644 --- a/run.php +++ b/run.php @@ -6,6 +6,7 @@ $this->provideHook('ApplicationState'); $this->provideHook('X509/Sni'); +$this->provideHook('Icingadb/CustomVarEnricher'); $this->provideHook('health', 'IcingaHealth'); $this->provideHook('health', 'RedisHealth'); $this->provideHook('Reporting/Report', 'Reporting/HostSlaReport'); diff --git a/testBasketData.json b/testBasketData.json new file mode 100644 index 000000000..f1aef7a95 --- /dev/null +++ b/testBasketData.json @@ -0,0 +1,218 @@ +{ + "ServiceTemplate": { + "scadvfda": { + "check_command": "disk", + "fields": [ + { + "datafield_id": 1, + "is_required": "n", + "var_filter": null + }, + { + "datafield_id": 2, + "is_required": "n", + "var_filter": null + } + ], + "object_name": "scadvfda", + "object_type": "template", + "uuid": "e8d4e283-4741-42e0-a4c1-ea6efd740477" + }, + "asfdsfbdg": { + "fields": [ + { + "datafield_id": 1, + "is_required": "n", + "var_filter": null + }, + { + "datafield_id": 2, + "is_required": "n", + "var_filter": null + } + ], + "object_name": "asfdsfbdg", + "object_type": "template", + "uuid": "53b68378-ca77-438a-824d-ce43d5261a9d" + }, + "st1": { + "check_command": "dummy", + "fields": [ + { + "datafield_id": 5, + "is_required": "n", + "var_filter": null + }, + { + "datafield_id": 6, + "is_required": "y", + "var_filter": null + }, + { + "datafield_id": 10, + "is_required": "n", + "var_filter": null + }, + { + "datafield_id": 9, + "is_required": "n", + "var_filter": null + }, + { + "datafield_id": 12, + "is_required": "n", + "var_filter": null + }, + { + "datafield_id": 13, + "is_required": "n", + "var_filter": null + }, + { + "datafield_id": 2, + "is_required": "n", + "var_filter": null + } + ], + "groups": [ + "foo" + ], + "object_name": "st1", + "object_type": "template", + "uuid": "bf099d80-ee7c-4a31-84a5-6916d1543410" + } + }, + "DataList": { + "cities": { + "entries": [ + { + "entry_name": "firt", + "entry_value": "foo", + "format": "string", + "allowed_roles": [ + "admin" + ] + }, + { + "entry_name": "second", + "entry_value": "Second", + "format": "string", + "allowed_roles": null + } + ], + "list_name": "cities", + "owner": "icingaadmin", + "uuid": "5795dbf7-79ae-43b9-a659-255dd2f2fcab" + } + }, + "Datafield": { + "1": { + "uuid": "7097ac28-8055-472f-b60b-2cd18f45ba6b", + "varname": "group", + "caption": "Host group", + "description": null, + "datatype": "Icinga\\Module\\Director\\DataType\\DataTypeString", + "format": null, + "settings": { + "visibility": "visible" + }, + "category": null + }, + "2": { + "uuid": "6a6f90bd-c601-4e0d-8083-46af977850e4", + "varname": "Test string", + "caption": "String to test", + "description": null, + "datatype": "Icinga\\Module\\Director\\DataType\\DataTypeString", + "format": null, + "settings": { + "visibility": "visible" + }, + "category": null + }, + "5": { + "uuid": "79ef7150-9f8c-4808-890c-b9d82c5ba2a5", + "varname": "branch", + "caption": "Company branch", + "description": "something", + "datatype": "Icinga\\Module\\Director\\DataType\\DataTypeDatalist", + "format": null, + "settings": { + "behavior": "strict", + "data_type": "string", + "rename_vars": "y", + "datalist": "cities" + }, + "category": "location" + }, + "6": { + "uuid": "9ddd34fa-79ac-4e48-b468-e30c69d26a48", + "varname": "dict", + "caption": "Dictionary", + "description": null, + "datatype": "Icinga\\Module\\Director\\DataType\\DataTypeDictionary", + "format": null, + "settings": { + "template_name": "st1", + "template_object_type": "service" + }, + "category": null + }, + "10": { + "uuid": "a9c6e74f-3fd1-403e-9813-f61614732d85", + "varname": "dict2", + "caption": "Child dictionary", + "description": null, + "datatype": "Icinga\\Module\\Director\\DataType\\DataTypeDictionary", + "format": null, + "settings": { + "template_name": "st1", + "template_object_type": "service" + }, + "category": null + }, + "9": { + "uuid": "d030fd44-be9b-4f4f-9d4c-53fc46db7b95", + "varname": "foo", + "caption": "Dummy Variable", + "description": null, + "datatype": "Icinga\\Module\\Director\\DataType\\DataTypeString", + "format": null, + "settings": { + "visibility": "visible" + }, + "category": null + }, + "12": { + "uuid": "c8050590-44fe-4cb3-ae87-19e0693d9b88", + "varname": "locations", + "caption": "Locations", + "description": "Locations belonging to domain", + "datatype": "Icinga\\Module\\Director\\DataType\\DataTypeArray", + "format": null, + "settings": {}, + "category": null + }, + "13": { + "uuid": "d63601ee-2637-47bd-9cf4-6e6df5bfb3f3", + "varname": "related_hosts", + "caption": "Related Hosts", + "description": null, + "datatype": "Icinga\\Module\\Director\\DataType\\DataTypeSqlQuery", + "format": null, + "settings": { + "data_type": "array", + "query": "select name, display_name from host where name like 'dummy%' order by name limit 5;", + "resource": "icingadb_postgres" + }, + "category": null + } + }, + "DatafieldCategory": { + "location": { + "category_name": "location", + "description": null, + "originalId": "1" + } + } +} \ No newline at end of file