From 616d329be3c4d2274d940b5abe8b6425cd7b057b Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Thu, 1 Dec 2022 12:03:45 +0100 Subject: [PATCH] IcingaNotification: implement users_var fixes #462 --- application/forms/IcingaNotificationForm.php | 80 +++++++++++++++---- .../CustomVariable/CustomVariables.php | 9 ++- .../Director/Objects/IcingaNotification.php | 61 ++++++++++++++ .../Director/Web/Form/DirectorObjectForm.php | 2 + schema/mysql-migrations/upgrade_183.sql | 9 +++ schema/mysql.sql | 4 +- schema/pgsql-migrations/upgrade_183.sql | 9 +++ schema/pgsql.sql | 4 +- 8 files changed, 158 insertions(+), 20 deletions(-) create mode 100644 schema/mysql-migrations/upgrade_183.sql create mode 100644 schema/pgsql-migrations/upgrade_183.sql diff --git a/application/forms/IcingaNotificationForm.php b/application/forms/IcingaNotificationForm.php index 2b7b4917..7a4bfaae 100644 --- a/application/forms/IcingaNotificationForm.php +++ b/application/forms/IcingaNotificationForm.php @@ -2,6 +2,7 @@ namespace Icinga\Module\Director\Forms; +use Icinga\Module\Director\DataType\DataTypeDirectorObject; use Icinga\Module\Director\Web\Form\DirectorObjectForm; class IcingaNotificationForm extends DirectorObjectForm @@ -121,20 +122,32 @@ class IcingaNotificationForm extends DirectorObjectForm { $users = $this->enumUsers(); if (empty($users)) { - return $this; - } - - $this->addElement( - 'extensibleSet', - 'users', - array( + $this->addElement('select', 'users', [ + 'label' => $this->translate('Users'), + 'description' => $this->translate('No User object has been created yet'), + 'multiOptions' => $this->optionalEnum([]), + ]); + } else { + $this->addElement('extensibleSet', 'users', [ 'label' => $this->translate('Users'), 'description' => $this->translate( 'Users that should be notified by this notifications' ), 'multiOptions' => $this->optionalEnum($users) + ]); + } + + $this->addElement('select', 'users_var', [ + 'label' => $this->translate('Users Custom Variable'), + 'multiOptions' => $this->enumDirectorObjectFields('user'), + 'description' => $this->translate( + 'If defined, Users from this Custom Variable will be combined with single users chosen below. ' + . ' e.g.: when set to notification_contacts, this notification will pick Users from the Array' + . ' service.vars.notification_contacts and fall back to host.vars.notification_contacts, in' + . ' case the former one does not exist.' + . ' Only Array type DirectorObject Fields for User objects are eligible for this feature.' ) - ); + ]); return $this; } @@ -146,24 +159,59 @@ class IcingaNotificationForm extends DirectorObjectForm { $groups = $this->enumUsergroups(); if (empty($groups)) { - return $this; - } - - $this->addElement( - 'extensibleSet', - 'user_groups', - array( + $this->addElement('select', 'user_groups', [ + 'label' => $this->translate('Users'), + 'description' => $this->translate('No UserGroup object has been created yet'), + 'multiOptions' => $this->optionalEnum([]), + ]); + } else { + $this->addElement('extensibleSet', 'user_groups', [ 'label' => $this->translate('User groups'), 'description' => $this->translate( 'User groups that should be notified by this notifications' ), 'multiOptions' => $this->optionalEnum($groups) + ]); + } + + $this->addElement('select', 'user_groups_var', [ + 'label' => $this->translate('User Groups Custom Variable'), + 'multiOptions' => $this->enumDirectorObjectFields('user_group'), + 'description' => $this->translate( + 'If defined, User Groups from this Custom Variable will be combined with single Groups chosen below. ' + . ' e.g.: when set to notification_groups, this notification will pick User Groups from the Array' + . ' service.vars.notification_groups and fall back to host.vars.notification_groups, in' + . ' case the former one does not exist' + . ' Only Array type DirectorObject Fields for User objects are eligible for this feature.' ) - ); + ]); return $this; } + protected function enumDirectorObjectFields($objectType, $dataType = 'array') + { + $db = $this->db->getDbAdapter(); + $query = $db->select() + ->from(['df' => 'director_datafield'], ['k' => 'df.varname', 'v' => 'df.varname']) + ->join( + ['dfs' => 'director_datafield_setting'], + $db->quoteInto('df.id = dfs.datafield_id AND dfs.setting_name = ?', 'icinga_object_type'), + [] + ) + ->join( + ['dft' => 'director_datafield_setting'], + $db->quoteInto('df.id = dft.datafield_id AND dft.setting_name = ?', 'data_type'), + [] + ) + ->where('df.datatype = ?', DataTypeDirectorObject::class) + ->where('dfs.setting_value = ?', $objectType) + ->where('dft.setting_value = ?', $dataType) + ->order('df.varname'); + + return $this->optionalEnum($db->fetchPairs($query)); + } + /** * @return self */ diff --git a/library/Director/CustomVariable/CustomVariables.php b/library/Director/CustomVariable/CustomVariables.php index cdcc4bd7..01227c59 100644 --- a/library/Director/CustomVariable/CustomVariables.php +++ b/library/Director/CustomVariable/CustomVariables.php @@ -413,11 +413,16 @@ class CustomVariables implements Iterator, Countable, IcingaConfigRenderer } protected function renderKeyName($key) + { + return 'vars' . self::renderKeySuffix($key); + } + + public static function renderKeySuffix($key) { if (preg_match('/^[a-z][a-z0-9_]*$/i', $key)) { - return 'vars.' . c::escapeIfReserved($key); + return '.' . c::escapeIfReserved($key); } else { - return 'vars[' . c::renderString($key) . ']'; + return '[' . c::renderString($key) . ']'; } } diff --git a/library/Director/Objects/IcingaNotification.php b/library/Director/Objects/IcingaNotification.php index 9c5d08d3..4e73e54f 100644 --- a/library/Director/Objects/IcingaNotification.php +++ b/library/Director/Objects/IcingaNotification.php @@ -2,6 +2,7 @@ namespace Icinga\Module\Director\Objects; +use Icinga\Module\Director\CustomVariable\CustomVariables; use Icinga\Module\Director\Db; use Icinga\Module\Director\DirectorObject\Automation\ExportInterface; use Icinga\Module\Director\Exception\DuplicateKeyException; @@ -29,6 +30,8 @@ class IcingaNotification extends IcingaObject implements ExportInterface 'notification_interval' => null, 'period_id' => null, 'zone_id' => null, + 'users_var' => null, + 'user_groups_var' => null, 'assign_filter' => null, ]; @@ -81,6 +84,64 @@ class IcingaNotification extends IcingaObject implements ExportInterface return c::renderKeyValue('times.begin', c::renderInterval($this->get('times_begin'))); } + /** + * @codingStandardsIgnoreStart + * @return string + */ + protected function renderUsers_var() + { + // @codingStandardsIgnoreEnd + return ''; + } + + /** + * @codingStandardsIgnoreStart + * @return string + */ + protected function renderUser_groups_var() + { + // @codingStandardsIgnoreEnd + return ''; + } + + protected function renderUserVarsSuffixFor($property) + { + $varName = $this->getResolvedProperty("{$property}_var"); + if ($varName === null) { + return ''; + } + + $varSuffix = CustomVariables::renderKeySuffix($varName); + $indent = ' '; + $objectType = $this->get('apply_to'); + if ($objectType === 'service') { + return "{$indent}if (service.vars$varSuffix) {\n" + . c::renderKeyOperatorValue($property, '+=', "service.vars$varSuffix", $indent . ' ') + . "$indent} else {\n" + . $this->getHostSnippet($indent . ' ') + . c::renderKeyOperatorValue($property, '+=', "host.vars$varSuffix", $indent . ' ') + . "$indent}\n"; + } elseif ($objectType === 'host') { + return $this->getHostSnippet() . c::renderKeyOperatorValue($property, '+=', "host.vars$varSuffix"); + } + + return ''; + } + + protected function getHostSnippet($indent = ' ') + { + return "{$indent}if (! host) {\n" + . "$indent var host = get_host(host_name)\n" + . "$indent}\n"; + } + + protected function renderSuffix() + { + return $this->renderUserVarsSuffixFor('users') + . $this->renderUserVarsSuffixFor('user_groups') + . parent::renderSuffix(); + } + /** * @codingStandardsIgnoreStart * @return string diff --git a/library/Director/Web/Form/DirectorObjectForm.php b/library/Director/Web/Form/DirectorObjectForm.php index b70bd7b9..a7037a78 100644 --- a/library/Director/Web/Form/DirectorObjectForm.php +++ b/library/Director/Web/Form/DirectorObjectForm.php @@ -540,7 +540,9 @@ abstract class DirectorObjectForm extends DirectorForm 'inherited_groups', 'applied_groups', 'users', + 'users_var', 'user_groups', + 'user_groups_var', 'apply_to', 'command_id', // Notification 'notification_interval', diff --git a/schema/mysql-migrations/upgrade_183.sql b/schema/mysql-migrations/upgrade_183.sql new file mode 100644 index 00000000..3e9577d9 --- /dev/null +++ b/schema/mysql-migrations/upgrade_183.sql @@ -0,0 +1,9 @@ +ALTER TABLE icinga_notification + ADD COLUMN users_var VARCHAR(255) DEFAULT NULL AFTER zone_id; + +ALTER TABLE icinga_notification + ADD COLUMN user_groups_var VARCHAR(255) DEFAULT NULL AFTER users_var; + +INSERT INTO director_schema_migration + (schema_version, migration_time) + VALUES (183, NOW()); diff --git a/schema/mysql.sql b/schema/mysql.sql index 02ac5d9f..81cfc2e0 100644 --- a/schema/mysql.sql +++ b/schema/mysql.sql @@ -1260,6 +1260,8 @@ CREATE TABLE icinga_notification ( command_id INT(10) UNSIGNED DEFAULT NULL, period_id INT(10) UNSIGNED DEFAULT NULL, zone_id INT(10) UNSIGNED DEFAULT NULL, + users_var VARCHAR(255) DEFAULT NULL, + user_groups_var VARCHAR(255) DEFAULT NULL, assign_filter TEXT DEFAULT NULL, PRIMARY KEY (id), UNIQUE INDEX uuid (uuid), @@ -2439,4 +2441,4 @@ CREATE TABLE branched_icinga_dependency ( INSERT INTO director_schema_migration (schema_version, migration_time) - VALUES (182, NOW()); + VALUES (183, NOW()); diff --git a/schema/pgsql-migrations/upgrade_183.sql b/schema/pgsql-migrations/upgrade_183.sql new file mode 100644 index 00000000..b9964efa --- /dev/null +++ b/schema/pgsql-migrations/upgrade_183.sql @@ -0,0 +1,9 @@ +ALTER TABLE icinga_notification + ADD COLUMN users_var character varying(255) DEFAULT NULL; + +ALTER TABLE icinga_notification + ADD COLUMN user_groups_var character varying(255) DEFAULT NULL; + +INSERT INTO director_schema_migration + (schema_version, migration_time) + VALUES (183, NOW()); diff --git a/schema/pgsql.sql b/schema/pgsql.sql index b9b2cf8a..a487ae00 100644 --- a/schema/pgsql.sql +++ b/schema/pgsql.sql @@ -1503,6 +1503,8 @@ CREATE TABLE icinga_notification ( command_id integer DEFAULT NULL, period_id integer DEFAULT NULL, zone_id integer DEFAULT NULL, + users_var character varying(255) DEFAULT NULL, + user_groups_var character varying(255) DEFAULT NULL, assign_filter text DEFAULT NULL, PRIMARY KEY (id), CONSTRAINT icinga_notification_host @@ -2778,4 +2780,4 @@ CREATE INDEX branched_dependency_search_object_name ON branched_icinga_dependenc INSERT INTO director_schema_migration (schema_version, migration_time) - VALUES (182, NOW()); + VALUES (183, NOW());