diff options
Diffstat (limited to 'library/Director/Objects')
31 files changed, 506 insertions, 1206 deletions
diff --git a/library/Director/Objects/DirectorActivityLog.php b/library/Director/Objects/DirectorActivityLog.php index cb041b6..2cecc2e 100644 --- a/library/Director/Objects/DirectorActivityLog.php +++ b/library/Director/Objects/DirectorActivityLog.php @@ -7,6 +7,7 @@ use Icinga\Module\Director\Db; use Icinga\Authentication\Auth; use Icinga\Application\Icinga; use Icinga\Application\Logger; +use stdClass; class DirectorActivityLog extends DbObject { @@ -176,7 +177,19 @@ class DirectorActivityLog extends DbObject { $name = $object->getObjectName(); $type = $object->getTableName(); - $oldProps = json_encode($object->getPlainUnmodifiedObject()); + /** @var stdClass $plainUnmodifiedObject */ + $plainUnmodifiedObject = $object->getPlainUnmodifiedObject(); + + if ($object instanceof IcingaServiceSet) { + $services = []; + foreach ($object->getCachedServices() as $service) { + $services[$service->getObjectName()] = $service->toPlainObject(); + } + + $plainUnmodifiedObject->services = $services; + } + + $oldProps = json_encode($plainUnmodifiedObject); $data = [ 'object_name' => $name, diff --git a/library/Director/Objects/DirectorDatafield.php b/library/Director/Objects/DirectorDatafield.php index 84db068..ced6218 100644 --- a/library/Director/Objects/DirectorDatafield.php +++ b/library/Director/Objects/DirectorDatafield.php @@ -2,28 +2,30 @@ namespace Icinga\Module\Director\Objects; -use Icinga\Module\Director\Core\Json; use Icinga\Module\Director\Data\Db\DbObjectWithSettings; use Icinga\Module\Director\Db; +use Icinga\Module\Director\DirectorObject\Automation\BasketSnapshotFieldResolver; use Icinga\Module\Director\DirectorObject\Automation\CompareBasketObject; -use Icinga\Module\Director\Exception\DuplicateKeyException; use Icinga\Module\Director\Forms\IcingaServiceForm; use Icinga\Module\Director\Hook\DataTypeHook; use Icinga\Module\Director\Resolver\OverriddenVarsResolver; use Icinga\Module\Director\Web\Form\DirectorObjectForm; -use InvalidArgumentException; +use Ramsey\Uuid\Uuid; +use stdClass; use Zend_Form_Element as ZfElement; class DirectorDatafield extends DbObjectWithSettings { protected $table = 'director_datafield'; - protected $keyName = 'id'; - protected $autoincKeyName = 'id'; + protected $uuidColumn = 'uuid'; + protected $settingsTable = 'director_datafield_setting'; + protected $settingsRemoteId = 'datafield_id'; protected $defaultProperties = [ 'id' => null, + 'uuid' => null, 'category_id' => null, 'varname' => null, 'caption' => null, @@ -33,16 +35,11 @@ class DirectorDatafield extends DbObjectWithSettings ]; protected $relations = [ - 'category' => 'DirectorDatafieldCategory' + 'category' => 'DirectorDatafieldCategory' ]; - protected $settingsTable = 'director_datafield_setting'; - - protected $settingsRemoteId = 'datafield_id'; - - /** @var DirectorDatafieldCategory|null */ + /** @var ?DirectorDatafieldCategory */ private $category; - private $object; public static function fromDbRow($row, Db $connection) @@ -70,10 +67,9 @@ class DirectorDatafield extends DbObjectWithSettings } /** - * @return DirectorDatafieldCategory|null * @throws \Icinga\Exception\NotFoundError */ - public function getCategory() + public function getCategory(): ?DirectorDatafieldCategory { if ($this->category) { return $this->category; @@ -84,7 +80,7 @@ class DirectorDatafield extends DbObjectWithSettings } } - public function getCategoryName() + public function getCategoryName(): ?string { $category = $this->getCategory(); if ($category === null) { @@ -105,27 +101,26 @@ class DirectorDatafield extends DbObjectWithSettings } $this->category = $category; } else { - if (DirectorDatafieldCategory::exists($category, $this->getConnection())) { - $this->setCategory(DirectorDatafieldCategory::load($category, $this->getConnection())); + if ($category = DirectorDatafieldCategory::loadOptional($category, $this->getConnection())) { + $this->setCategory($category); } else { $this->setCategory(DirectorDatafieldCategory::create([ 'category_name' => $category ], $this->getConnection())); } } - - return $this; } /** - * @return object * @throws \Icinga\Exception\NotFoundError */ - public function export() + public function export(): stdClass { $plain = (object) $this->getProperties(); - $plain->originalId = $plain->id; unset($plain->id); + if ($uuid = $this->get('uuid')) { + $plain->uuid = Uuid::fromBytes($uuid)->toString(); + } $plain->settings = (object) $this->getSettings(); if (property_exists($plain->settings, 'datalist_id')) { @@ -144,63 +139,35 @@ class DirectorDatafield extends DbObjectWithSettings } /** - * @param $plain - * @param Db $db - * @param bool $replace - * @return DirectorDatafield * @throws \Icinga\Exception\NotFoundError */ - public static function import($plain, Db $db, $replace = false) + public static function import(stdClass $plain, Db $db): DirectorDatafield { - $properties = (array) $plain; - if (isset($properties['originalId'])) { - $id = $properties['originalId']; - unset($properties['originalId']); - } else { - $id = null; - } - - if (isset($properties['settings']->datalist)) { - // Just try to load the list, import should fail if missing - $list = DirectorDatalist::load( - $properties['settings']->datalist, - $db - ); - } else { - $list = null; - } - - $compare = Json::decode(Json::encode($properties)); - if ($id && static::exists($id, $db)) { - $existing = static::loadWithAutoIncId($id, $db); - $existingProperties = (array) $existing->export(); - unset($existingProperties['originalId']); - if (CompareBasketObject::equals((object) $compare, (object) $existingProperties)) { - return $existing; + $dba = $db->getDbAdapter(); + if ($uuid = $plain->uuid ?? null) { + $uuid = Uuid::fromString($uuid); + if ($candidate = DirectorDatafield::loadWithUniqueId($uuid, $db)) { + BasketSnapshotFieldResolver::fixOptionalDatalistReference($plain, $db); + assert($candidate instanceof DirectorDatafield); + $candidate->setProperties((array) $plain); + return $candidate; } } - - if ($list) { - unset($properties['settings']->datalist); - $properties['settings']->datalist_id = $list->get('id'); - } - - $dba = $db->getDbAdapter(); - $query = $dba->select() - ->from('director_datafield') - ->where('varname = ?', $plain->varname); + $query = $dba->select()->from('director_datafield')->where('varname = ?', $plain->varname); $candidates = DirectorDatafield::loadAll($db, $query); foreach ($candidates as $candidate) { $export = $candidate->export(); - unset($export->originalId); CompareBasketObject::normalize($export); - if (CompareBasketObject::equals($export, $compare)) { + unset($export->uuid); + unset($plain->originalId); + if (CompareBasketObject::equals($export, $plain)) { return $candidate; } } + BasketSnapshotFieldResolver::fixOptionalDatalistReference($plain, $db); - return static::create($properties, $db); + return static::create((array) $plain, $db); } protected function beforeStore() @@ -223,7 +190,7 @@ class DirectorDatafield extends DbObjectWithSettings return $this->object; } - public function getFormElement(DirectorObjectForm $form, $name = null) + public function getFormElement(DirectorObjectForm $form, $name = null): ?ZfElement { $className = $this->get('datatype'); @@ -305,7 +272,7 @@ class DirectorDatafield extends DbObjectWithSettings } } - protected function eventuallyGetResolvedCommandVar(IcingaObject $object, $varName) + protected function eventuallyGetResolvedCommandVar(IcingaObject $object, $varName): ?array { if (! $object->hasRelation('check_command')) { return null; diff --git a/library/Director/Objects/DirectorDatalist.php b/library/Director/Objects/DirectorDatalist.php index ae5c983..1bb821b 100644 --- a/library/Director/Objects/DirectorDatalist.php +++ b/library/Director/Objects/DirectorDatalist.php @@ -4,68 +4,35 @@ namespace Icinga\Module\Director\Objects; use Exception; use Icinga\Module\Director\Data\Db\DbObject; -use Icinga\Module\Director\Db; +use Icinga\Module\Director\DataType\DataTypeDatalist; use Icinga\Module\Director\DirectorObject\Automation\ExportInterface; use Icinga\Module\Director\Exception\DuplicateKeyException; class DirectorDatalist extends DbObject implements ExportInterface { protected $table = 'director_datalist'; - protected $keyName = 'list_name'; - protected $autoincKeyName = 'id'; + protected $uuidColumn = 'uuid'; - protected $defaultProperties = array( + protected $defaultProperties = [ 'id' => null, + 'uuid' => null, 'list_name' => null, 'owner' => null - ); + ]; /** @var DirectorDatalistEntry[] */ - protected $storedEntries; + protected $entries; public function getUniqueIdentifier() { return $this->get('list_name'); } - /** - * @param $plain - * @param Db $db - * @param bool $replace - * @return static - * @throws \Icinga\Exception\NotFoundError - * @throws DuplicateKeyException - */ - public static function import($plain, Db $db, $replace = false) - { - $properties = (array) $plain; - if (isset($properties['originalId'])) { - unset($properties['originalId']); - } else { - $id = null; - } - $name = $properties['list_name']; - - if ($replace && static::exists($name, $db)) { - $object = static::load($name, $db); - } elseif (static::exists($name, $db)) { - throw new DuplicateKeyException( - 'Data List %s already exists', - $name - ); - } else { - $object = static::create([], $db); - } - $object->setProperties($properties); - - return $object; - } - - public function setEntries($entries) + public function setEntries($entries): void { - $existing = $this->getStoredEntries(); + $existing = $this->getEntries(); $new = []; $seen = []; @@ -101,15 +68,13 @@ class DirectorDatalist extends DbObject implements ExportInterface $this->hasBeenModified = true; } - $this->storedEntries = $existing; - ksort($this->storedEntries); - - return $this; + $this->entries = $existing; + ksort($this->entries); } protected function beforeDelete() { - if ($this->hasBeenUsed()) { + if ($this->isInUse()) { throw new Exception( sprintf( "Cannot delete '%s', as the datalist '%s' is currently being used.", @@ -120,9 +85,13 @@ class DirectorDatalist extends DbObject implements ExportInterface } } - protected function hasBeenUsed() + protected function isInUse(): bool { - $datalistType = 'Icinga\\Module\\Director\\DataType\\DataTypeDatalist'; + $id = $this->get('id'); + if ($id === null) { + return false; + } + $db = $this->getDb(); $dataFieldsCheck = $db->select() @@ -137,8 +106,8 @@ class DirectorDatalist extends DbObject implements ExportInterface 'l.id = dfs.setting_value', [] ) - ->where('datatype = ?', $datalistType) - ->where('setting_value = ?', $this->get('id')); + ->where('datatype = ?', DataTypeDatalist::class) + ->where('setting_value = ?', $id); if ($db->fetchOne($dataFieldsCheck)) { return true; @@ -147,7 +116,7 @@ class DirectorDatalist extends DbObject implements ExportInterface $syncCheck = $db->select() ->from(['sp' =>'sync_property'], ['source_expression']) ->where('sp.destination_field = ?', 'list_id') - ->where('sp.source_expression = ?', $this->get('id')); + ->where('sp.source_expression = ?', $id); if ($db->fetchOne($syncCheck)) { return true; @@ -161,65 +130,38 @@ class DirectorDatalist extends DbObject implements ExportInterface */ public function onStore() { - if ($this->storedEntries) { + if ($this->entries) { $db = $this->getConnection(); $removedKeys = []; $myId = $this->get('id'); - foreach ($this->storedEntries as $key => $entry) { + foreach ($this->entries as $key => $entry) { if ($entry->shouldBeRemoved()) { $entry->delete(); $removedKeys[] = $key; } else { - if (! $entry->hasBeenLoadedFromDb()) { - $entry->set('list_id', $myId); - } $entry->set('list_id', $myId); $entry->store($db); } } foreach ($removedKeys as $key) { - unset($this->storedEntries[$key]); - } - } - } - - /** - * @deprecated please use \Icinga\Module\Director\Data\Exporter - * @return object - */ - public function export() - { - $plain = (object) $this->getProperties(); - $plain->originalId = $plain->id; - unset($plain->id); - - $plain->entries = []; - foreach ($this->getStoredEntries() as $key => $entry) { - if ($entry->shouldBeRemoved()) { - continue; + unset($this->entries[$key]); } - $plainEntry = (object) $entry->getProperties(); - unset($plainEntry->list_id); - - $plain->entries[] = $plainEntry; } - - return $plain; } - protected function getStoredEntries() + public function getEntries(): array { - if ($this->storedEntries === null) { - if ($id = $this->get('id')) { - $this->storedEntries = DirectorDatalistEntry::loadAllForList($this); - ksort($this->storedEntries); + if ($this->entries === null) { + if ($this->get('id')) { + $this->entries = DirectorDatalistEntry::loadAllForList($this); + ksort($this->entries); } else { - $this->storedEntries = []; + $this->entries = []; } } - return $this->storedEntries; + return $this->entries; } } diff --git a/library/Director/Objects/DirectorDatalistEntry.php b/library/Director/Objects/DirectorDatalistEntry.php index 086686a..278de97 100644 --- a/library/Director/Objects/DirectorDatalistEntry.php +++ b/library/Director/Objects/DirectorDatalistEntry.php @@ -51,7 +51,7 @@ class DirectorDatalistEntry extends DbObject } else { throw new RuntimeException( 'Expected array or null for allowed_roles, got %s', - var_export($roles, 1) + var_export($roles, true) ); } } diff --git a/library/Director/Objects/DirectorDeploymentLog.php b/library/Director/Objects/DirectorDeploymentLog.php index 0794a3c..e9b07ce 100644 --- a/library/Director/Objects/DirectorDeploymentLog.php +++ b/library/Director/Objects/DirectorDeploymentLog.php @@ -46,7 +46,11 @@ class DirectorDeploymentLog extends DbObject public function getConfigHexChecksum() { - return bin2hex($this->config_checksum); + $checksum = $this->get('config_checksum'); + if ($checksum === null) { + return null; + } + return bin2hex($checksum); } public function getConfig() diff --git a/library/Director/Objects/DirectorJob.php b/library/Director/Objects/DirectorJob.php index 361f764..2a18d52 100644 --- a/library/Director/Objects/DirectorJob.php +++ b/library/Director/Objects/DirectorJob.php @@ -122,8 +122,12 @@ class DirectorJob extends DbObjectWithSettings implements ExportInterface, Insta return false; } + if ($this->get('ts_last_attempt') === null) { + return true; + } + return ( - strtotime((int) $this->get('ts_last_attempt')) + $this->get('run_interval') * 2 + strtotime($this->get('ts_last_attempt')) + $this->get('run_interval') * 2 ) < time(); } @@ -194,87 +198,6 @@ class DirectorJob extends DbObjectWithSettings implements ExportInterface, Insta } /** - * @return object - * @deprecated please use \Icinga\Module\Director\Data\Exporter - * @throws \Icinga\Exception\NotFoundError - */ - public function export() - { - $plain = (object) $this->getProperties(); - $plain->originalId = $plain->id; - unset($plain->id); - unset($plain->timeperiod_id); - if ($this->hasTimeperiod()) { - $plain->timeperiod = $this->timeperiod()->getObjectName(); - } - - foreach ($this->stateProperties as $key) { - unset($plain->$key); - } - $plain->settings = $this->getInstance()->exportSettings(); - - return $plain; - } - - /** - * @param $plain - * @param Db $db - * @param bool $replace - * @return DirectorJob - * @throws DuplicateKeyException - * @throws NotFoundError - */ - public static function import($plain, Db $db, $replace = false) - { - $dummy = new static; - $idCol = $dummy->autoincKeyName; - $keyCol = $dummy->keyName; - $properties = (array) $plain; - if (isset($properties['originalId'])) { - $id = $properties['originalId']; - unset($properties['originalId']); - } else { - $id = null; - } - $name = $properties[$keyCol]; - - if ($replace && $id && static::existsWithNameAndId($name, $id, $db)) { - $object = static::loadWithAutoIncId($id, $db); - } elseif ($replace && static::exists($name, $db)) { - $object = static::load($name, $db); - } elseif (static::exists($name, $db)) { - throw new DuplicateKeyException( - 'Director Job "%s" already exists', - $name - ); - } else { - $object = static::create([], $db); - } - - $settings = (array) $properties['settings']; - - if (array_key_exists('source', $settings) && ! (array_key_exists('source_id', $settings))) { - $val = ImportSource::load($settings['source'], $db)->get('id'); - $settings['source_id'] = $val; - unset($settings['source']); - } - - if (array_key_exists('rule', $settings) && ! (array_key_exists('rule_id', $settings))) { - $val = SyncRule::load($settings['rule'], $db)->get('id'); - $settings['rule_id'] = $val; - unset($settings['rule']); - } - - $properties['settings'] = (object) $settings; - $object->setProperties($properties); - if ($id !== null) { - $object->reallySet($idCol, $id); - } - - return $object; - } - - /** * @param string $name * @param int $id * @param Db $connection diff --git a/library/Director/Objects/GroupMembershipResolver.php b/library/Director/Objects/GroupMembershipResolver.php index f5ef418..7266de9 100644 --- a/library/Director/Objects/GroupMembershipResolver.php +++ b/library/Director/Objects/GroupMembershipResolver.php @@ -211,7 +211,7 @@ abstract class GroupMembershipResolver $query, $object, 'o', - Db\IcingaObjectFilterHelper::INHERIT_DIRECT_OR_INDIRECT + IcingaObjectFilterHelper::INHERIT_DIRECT_OR_INDIRECT ); foreach ($object::loadAll($this->connection, $query) as $child) { @@ -352,7 +352,7 @@ abstract class GroupMembershipResolver $type = $this->type; if ($this->groupMap === null) { $this->groupMap = $this->db->fetchPairs( - $this->db->select()->from("icinga_${type}group", ['object_name', 'id']) + $this->db->select()->from("icinga_{$type}group", ['object_name', 'id']) ); } @@ -390,9 +390,9 @@ abstract class GroupMembershipResolver $db->delete( $this->getResolvedTableName(), sprintf( - "(${type}group_id = %d AND ${type}_id = %d)", - $row["${type}group_id"], - $row["${type}_id"] + "({$type}group_id = %d AND {$type}_id = %d)", + $row["{$type}group_id"], + $row["{$type}_id"] ) ); } @@ -416,16 +416,16 @@ abstract class GroupMembershipResolver foreach ($objectIds as $objectId) { if (! array_key_exists($objectId, $right[$groupId])) { $diff[] = array( - "${type}group_id" => $groupId, - "${type}_id" => $objectId, + "{$type}group_id" => $groupId, + "{$type}_id" => $objectId, ); } } } else { foreach ($objectIds as $objectId) { $diff[] = array( - "${type}group_id" => $groupId, - "${type}_id" => $objectId, + "{$type}group_id" => $groupId, + "{$type}_id" => $objectId, ); } } @@ -445,16 +445,16 @@ abstract class GroupMembershipResolver $query = $this->db->select()->from( array('hgh' => $this->getResolvedTableName()), array( - 'group_id' => "${type}group_id", - 'object_id' => "${type}_id", + 'group_id' => "{$type}group_id", + 'object_id' => "{$type}_id", ) ); - $this->addMembershipWhere($query, "${type}_id", $this->objects); - $this->addMembershipWhere($query, "${type}group_id", $this->groups); + $this->addMembershipWhere($query, "{$type}_id", $this->objects); + $this->addMembershipWhere($query, "{$type}group_id", $this->groups); if (! empty($this->groups)) { // load staticGroups (we touched here) additionally, so we can compare changes - $this->addMembershipWhere($query, "${type}group_id", $this->staticGroups); + $this->addMembershipWhere($query, "{$type}group_id", $this->staticGroups); } foreach ($this->db->fetchAll($query) as $row) { @@ -602,7 +602,7 @@ abstract class GroupMembershipResolver { $type = $this->getType(); $query = $this->db->select()->from( - array('hg' => "icinga_${type}group"), + array('hg' => "icinga_{$type}group"), array( 'id', 'assign_filter', @@ -629,7 +629,7 @@ abstract class GroupMembershipResolver protected function getTableName() { $type = $this->getType(); - return "icinga_${type}group_${type}"; + return "icinga_{$type}group_{$type}"; } protected function getResolvedTableName() diff --git a/library/Director/Objects/IcingaArguments.php b/library/Director/Objects/IcingaArguments.php index e788da8..22bf914 100644 --- a/library/Director/Objects/IcingaArguments.php +++ b/library/Director/Objects/IcingaArguments.php @@ -155,7 +155,9 @@ class IcingaArguments implements Iterator, Countable, IcingaConfigRenderer if (property_exists($value, 'type')) { // argument is directly set as function, no further properties if ($value->type === 'Function') { - $attrs['argument_value'] = self::COMMENT_DSL_UNSUPPORTED; + $attrs['argument_value'] = property_exists($value, 'body') + ? $value->body + : self::COMMENT_DSL_UNSUPPORTED; $attrs['argument_format'] = 'expression'; } } elseif (property_exists($value, 'value')) { @@ -296,6 +298,7 @@ class IcingaArguments implements Iterator, Countable, IcingaConfigRenderer $this->arguments = IcingaCommandArgument::loadAll($connection, $query, 'argument_name'); $this->cloneStored(); $this->refreshIndex(); + $this->modified = false; return $this; } @@ -360,6 +363,7 @@ class IcingaArguments implements Iterator, Countable, IcingaConfigRenderer } $this->refreshIndex(); $this->cloneStored(); + $this->modified = false; } /** @@ -393,7 +397,9 @@ class IcingaArguments implements Iterator, Countable, IcingaConfigRenderer unset($this->arguments[$key]); } + $this->refreshIndex(); $this->cloneStored(); + $this->modified = false; return $this; } diff --git a/library/Director/Objects/IcingaCommand.php b/library/Director/Objects/IcingaCommand.php index 35f38a4..8c5aed0 100644 --- a/library/Director/Objects/IcingaCommand.php +++ b/library/Director/Objects/IcingaCommand.php @@ -2,9 +2,7 @@ namespace Icinga\Module\Director\Objects; -use Icinga\Module\Director\Db; use Icinga\Module\Director\DirectorObject\Automation\ExportInterface; -use Icinga\Module\Director\Exception\DuplicateKeyException; use Icinga\Module\Director\IcingaConfig\IcingaConfigHelper as c; use Icinga\Module\Director\IcingaConfig\IcingaLegacyConfigHelper as c1; use Icinga\Module\Director\Objects\Extension\Arguments; @@ -129,10 +127,8 @@ class IcingaCommand extends IcingaObject implements ObjectWithArguments, ExportI // return $value; } - if (self::$pluginDir !== null) { - if (($pos = strpos($value, self::$pluginDir)) === 0) { - $value = substr($value, strlen(self::$pluginDir) + 1); - } + if (isset($value, self::$pluginDir) && strpos($value, self::$pluginDir) === 0) { + $value = substr($value, strlen(self::$pluginDir) + 1); } return $value; @@ -212,89 +208,6 @@ class IcingaCommand extends IcingaObject implements ObjectWithArguments, ExportI return $this->getObjectName(); } - /** - * @return object - * @deprecated please use \Icinga\Module\Director\Data\Exporter - * @throws \Icinga\Exception\NotFoundError - */ - public function export() - { - $props = (array) $this->toPlainObject(); - if (isset($props['arguments'])) { - foreach ($props['arguments'] as $key => $argument) { - if (property_exists($argument, 'command_id')) { - unset($props['arguments'][$key]->command_id); - } - } - } - $props['fields'] = $this->loadFieldReferences(); - ksort($props); - - return (object) $props; - } - - /** - * @param $plain - * @param Db $db - * @param bool $replace - * @return IcingaCommand - * @throws DuplicateKeyException - * @throws \Icinga\Exception\NotFoundError - */ - public static function import($plain, Db $db, $replace = false) - { - $properties = (array) $plain; - $name = $properties['object_name']; - $key = $name; - - if ($replace && static::exists($key, $db)) { - $object = static::load($key, $db); - } elseif (static::exists($key, $db)) { - throw new DuplicateKeyException( - 'Command "%s" already exists', - $name - ); - } else { - $object = static::create([], $db); - } - - unset($properties['fields']); - $object->setProperties($properties); - - return $object; - } - - /** - * @deprecated please use \Icinga\Module\Director\Data\FieldReferenceLoader - * @return array - */ - protected function loadFieldReferences() - { - $db = $this->getDb(); - - $res = $db->fetchAll( - $db->select()->from([ - 'cf' => 'icinga_command_field' - ], [ - 'cf.datafield_id', - 'cf.is_required', - 'cf.var_filter', - ])->join(['df' => 'director_datafield'], 'df.id = cf.datafield_id', []) - ->where('command_id = ?', $this->get('id')) - ->order('varname ASC') - ); - - if (empty($res)) { - return []; - } else { - foreach ($res as $field) { - $field->datafield_id = (int) $field->datafield_id; - } - - return $res; - } - } - protected function renderCommand() { $command = $this->get('command'); diff --git a/library/Director/Objects/IcingaDependency.php b/library/Director/Objects/IcingaDependency.php index c9d9b89..abd92e6 100644 --- a/library/Director/Objects/IcingaDependency.php +++ b/library/Director/Objects/IcingaDependency.php @@ -3,9 +3,7 @@ namespace Icinga\Module\Director\Objects; use Icinga\Exception\ConfigurationError; -use Icinga\Module\Director\Db; use Icinga\Module\Director\DirectorObject\Automation\ExportInterface; -use Icinga\Module\Director\Exception\DuplicateKeyException; use Icinga\Module\Director\IcingaConfig\IcingaConfigHelper as c; use Icinga\Exception\NotFoundError; use Icinga\Data\Filter\Filter; @@ -80,52 +78,21 @@ class IcingaDependency extends IcingaObject implements ExportInterface return $this->getObjectName(); } - /** - * @return object - * @deprecated please use \Icinga\Module\Director\Data\Exporter - * @throws \Icinga\Exception\NotFoundError - */ - public function export() + public function parentHostIsVar() { - $props = (array) $this->toPlainObject(); - ksort($props); - - return (object) $props; + return $this->get('parent_host_var') !== null; } /** - * @param $plain - * @param Db $db - * @param bool $replace - * @return static - * @throws DuplicateKeyException - * @throws \Icinga\Exception\NotFoundError + * Check if the given string is a custom variable + * + * @param $string string + * + * @return false|int */ - public static function import($plain, Db $db, $replace = false) + protected function isCustomVar(string $string) { - $properties = (array) $plain; - $name = $properties['object_name']; - $key = $name; - - if ($replace && static::exists($key, $db)) { - $object = static::load($key, $db); - } elseif (static::exists($key, $db)) { - throw new DuplicateKeyException( - 'Dependency "%s" already exists', - $name - ); - } else { - $object = static::create([], $db); - } - - $object->setProperties($properties); - - return $object; - } - - public function parentHostIsVar() - { - return $this->get('parent_host_var') !== null; + return preg_match('/^(?:host|service)\.vars\..+$/', $string); } /** @@ -485,9 +452,16 @@ class IcingaDependency extends IcingaObject implements ExportInterface public function renderParent_service_by_name() { // @codingStandardsIgnoreEnd + $var = $this->get('parent_service_by_name'); + if ($this->isCustomVar($var)) { + return c::renderKeyValue( + 'parent_service_name', + $var + ); + } return c::renderKeyValue( 'parent_service_name', - c::renderString($this->get('parent_service_by_name')) + c::renderString($var) ); } @@ -506,7 +480,7 @@ class IcingaDependency extends IcingaObject implements ExportInterface protected function resolveUnresolvedRelatedProperty($name) { $short = substr($name, 0, -3); - /** @var IcingaObject $class */ + /** @var IcingaObject|string $class */ $class = $this->getRelationClass($short); $objKey = $this->unresolvedRelatedProperties[$name]; @@ -589,7 +563,25 @@ class IcingaDependency extends IcingaObject implements ExportInterface $this->reallySet($name, $object->get('id')); unset($this->unresolvedRelatedProperties[$name]); } else { - throw new NotFoundError('Unable to resolve related property: "%s"', $name); + // Depend on a single service on a single host. Rare case, as usually you want to + // depend on a service on the very same host - and leave the Host field empty. The + // latter is already being handled above. This duplicates some code, but I'll leave + // it this way for now. There might have been a reason for the parent_host_id = null + // check in that code. + if ($name === 'parent_service_id' && $this->get('object_type') === 'apply') { + $this->reallySet( + 'parent_service_by_name', + $this->unresolvedRelatedProperties[$name] + ); + $this->reallySet('parent_service_id', null); + unset($this->unresolvedRelatedProperties[$name]); + return; + } + throw new NotFoundError(sprintf( + 'Unable to resolve related property: %s "%s"', + $name, + $this->unresolvedRelatedProperties[$name] + )); } } @@ -620,8 +612,12 @@ class IcingaDependency extends IcingaObject implements ExportInterface $related = parent::getRelatedProperty($key); // handle special case for plain string parent service on Dependency // Apply rules - if ($related === null && $key === 'parent_service' - && null !== $this->get('parent_service_by_name') + if ($related === null + && $key === 'parent_service' + && ( + $this->get('parent_service_by_name') + && ! $this->isCustomVar($this->get('parent_service_by_name')) + ) ) { return $this->get('parent_service_by_name'); } diff --git a/library/Director/Objects/IcingaHost.php b/library/Director/Objects/IcingaHost.php index 2731f4a..7859324 100644 --- a/library/Director/Objects/IcingaHost.php +++ b/library/Director/Objects/IcingaHost.php @@ -7,7 +7,6 @@ use Icinga\Exception\NotFoundError; use Icinga\Module\Director\Data\PropertiesFilter; use Icinga\Module\Director\Db; use Icinga\Module\Director\DirectorObject\Automation\ExportInterface; -use Icinga\Module\Director\Exception\DuplicateKeyException; use Icinga\Module\Director\IcingaConfig\IcingaConfig; use Icinga\Module\Director\IcingaConfig\IcingaConfigHelper as c; use Icinga\Module\Director\IcingaConfig\IcingaLegacyConfigHelper as c1; @@ -310,88 +309,18 @@ class IcingaHost extends IcingaObject implements ExportInterface } } - /** - * @return object - * @deprecated please use \Icinga\Module\Director\Data\Exporter - * @throws \Icinga\Exception\NotFoundError - */ - public function export() + protected function rendersConditionalTemplate(): bool { - // TODO: ksort in toPlainObject? - $props = (array) $this->toPlainObject(); - $props['fields'] = $this->loadFieldReferences(); - ksort($props); - - return (object) $props; - } - - /** - * @param $plain - * @param Db $db - * @param bool $replace - * @return IcingaHost - * @throws DuplicateKeyException - * @throws \Icinga\Exception\NotFoundError - */ - public static function import($plain, Db $db, $replace = false) - { - $properties = (array) $plain; - $name = $properties['object_name']; - if ($properties['object_type'] !== 'template') { - throw new InvalidArgumentException(sprintf( - 'Can import only Templates, got "%s" for "%s"', - $properties['object_type'], - $name - )); - } - $key = $name; - - if ($replace && static::exists($key, $db)) { - $object = static::load($key, $db); - } elseif (static::exists($key, $db)) { - throw new DuplicateKeyException( - 'Service Template "%s" already exists', - $name - ); - } else { - $object = static::create([], $db); - } - - // $object->newFields = $properties['fields']; - unset($properties['fields']); - $object->setProperties($properties); - - return $object; + return $this->getRenderingZone() === self::ALL_NON_GLOBAL_ZONES; } - /** - * @deprecated please use \Icinga\Module\Director\Data\FieldReferenceLoader - * @return array - */ - protected function loadFieldReferences() + protected function getDefaultZone(IcingaConfig $config = null) { - $db = $this->getDb(); - - $res = $db->fetchAll( - $db->select()->from([ - 'hf' => 'icinga_host_field' - ], [ - 'hf.datafield_id', - 'hf.is_required', - 'hf.var_filter', - ])->join(['df' => 'director_datafield'], 'df.id = hf.datafield_id', []) - ->where('host_id = ?', $this->get('id')) - ->order('varname ASC') - ); - - if (empty($res)) { - return []; - } else { - foreach ($res as $field) { - $field->datafield_id = (int) $field->datafield_id; - } - return $res; + if ($this->isTemplate()) { + return self::ALL_NON_GLOBAL_ZONES; } + + return parent::getDefaultZone(); } public function beforeDelete() diff --git a/library/Director/Objects/IcingaHostGroup.php b/library/Director/Objects/IcingaHostGroup.php index e11f672..e78e931 100644 --- a/library/Director/Objects/IcingaHostGroup.php +++ b/library/Director/Objects/IcingaHostGroup.php @@ -31,12 +31,8 @@ class IcingaHostGroup extends IcingaObjectGroup return $this; } - protected function notifyResolvers() + protected function getMemberShipResolver() { - $resolver = $this->getHostGroupMembershipResolver(); - $resolver->addGroup($this); - $resolver->refreshDb(); - - return $this; + return $this->getHostGroupMembershipResolver(); } } diff --git a/library/Director/Objects/IcingaNotification.php b/library/Director/Objects/IcingaNotification.php index 9c5d08d..4768704 100644 --- a/library/Director/Objects/IcingaNotification.php +++ b/library/Director/Objects/IcingaNotification.php @@ -2,9 +2,8 @@ namespace Icinga\Module\Director\Objects; -use Icinga\Module\Director\Db; +use Icinga\Module\Director\CustomVariable\CustomVariables; use Icinga\Module\Director\DirectorObject\Automation\ExportInterface; -use Icinga\Module\Director\Exception\DuplicateKeyException; use Icinga\Module\Director\IcingaConfig\IcingaConfigHelper as c; use RuntimeException; @@ -29,6 +28,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, ]; @@ -85,92 +86,76 @@ class IcingaNotification extends IcingaObject implements ExportInterface * @codingStandardsIgnoreStart * @return string */ - protected function renderTimes_end() + protected function renderUsers_var() { // @codingStandardsIgnoreEnd - return c::renderKeyValue('times.end', c::renderInterval($this->get('times_end'))); - } - - public function getUniqueIdentifier() - { - return $this->getObjectName(); + return ''; } /** - * @return \stdClass - * @deprecated please use \Icinga\Module\Director\Data\Exporter - * @throws \Icinga\Exception\NotFoundError + * @codingStandardsIgnoreStart + * @return string */ - public function export() + protected function renderUser_groups_var() { - // TODO: ksort in toPlainObject? - $props = (array) $this->toPlainObject(); - $props['fields'] = $this->loadFieldReferences(); - ksort($props); - - return (object) $props; + // @codingStandardsIgnoreEnd + return ''; } - /** - * @param $plain - * @param Db $db - * @param bool $replace - * @return static - * @throws DuplicateKeyException - * @throws \Icinga\Exception\NotFoundError - */ - public static function import($plain, Db $db, $replace = false) + protected function renderUserVarsSuffixFor($property) { - $properties = (array) $plain; - $name = $properties['object_name']; - $key = $name; - - if ($replace && static::exists($key, $db)) { - $object = static::load($key, $db); - } elseif (static::exists($key, $db)) { - throw new DuplicateKeyException( - 'Notification "%s" already exists', - $name - ); - } else { - $object = static::create([], $db); + $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 . ' ') + . "$indent if (host.vars$varSuffix) { " + . c::renderKeyOperatorValue($property, '+=', "host.vars$varSuffix }", '') + . "$indent}\n"; + } elseif ($objectType === 'host') { + return $this->getHostSnippet() + . "{$indent}if (host.vars$varSuffix) { " + . c::renderKeyOperatorValue($property, '+=', "host.vars$varSuffix }"); } - // $object->newFields = $properties['fields']; - unset($properties['fields']); - $object->setProperties($properties); + return ''; + } + + protected function getHostSnippet($indent = ' ') + { + return "{$indent}if (! host) {\n" + . "$indent var host = get_host(host_name)\n" + . "$indent}\n"; + } - return $object; + protected function renderSuffix() + { + return $this->renderUserVarsSuffixFor('users') + . $this->renderUserVarsSuffixFor('user_groups') + . parent::renderSuffix(); } /** - * @deprecated please use \Icinga\Module\Director\Data\FieldReferenceLoader - * @return array + * @codingStandardsIgnoreStart + * @return string */ - protected function loadFieldReferences() + protected function renderTimes_end() { - $db = $this->getDb(); - - $res = $db->fetchAll( - $db->select()->from([ - 'nf' => 'icinga_notification_field' - ], [ - 'nf.datafield_id', - 'nf.is_required', - 'nf.var_filter', - ])->join(['df' => 'director_datafield'], 'df.id = nf.datafield_id', []) - ->where('notification_id = ?', $this->get('id')) - ->order('varname ASC') - ); - - if (empty($res)) { - return []; - } else { - foreach ($res as $field) { - $field->datafield_id = (int) $field->datafield_id; - } - return $res; - } + // @codingStandardsIgnoreEnd + return c::renderKeyValue('times.end', c::renderInterval($this->get('times_end'))); + } + + public function getUniqueIdentifier() + { + return $this->getObjectName(); } /** diff --git a/library/Director/Objects/IcingaObject.php b/library/Director/Objects/IcingaObject.php index 04ae32b..3b6236d 100644 --- a/library/Director/Objects/IcingaObject.php +++ b/library/Director/Objects/IcingaObject.php @@ -27,6 +27,7 @@ use RuntimeException; abstract class IcingaObject extends DbObject implements IcingaConfigRenderer { const RESOLVE_ERROR = '(unable to resolve)'; + const ALL_NON_GLOBAL_ZONES = '(all non-global zones)'; protected $keyName = 'object_name'; @@ -63,9 +64,6 @@ abstract class IcingaObject extends DbObject implements IcingaConfigRenderer protected $type; - /* key/value!! */ - protected $booleans = []; - // Property suffixed with _id must exist protected $relations = [ // property => PropertyClass @@ -142,11 +140,6 @@ abstract class IcingaObject extends DbObject implements IcingaConfigRenderer return $this->connection; } - public function propertyIsBoolean($property) - { - return array_key_exists($property, $this->booleans); - } - public function propertyIsInterval($property) { return array_key_exists($property, $this->intervalProperties); @@ -771,10 +764,6 @@ abstract class IcingaObject extends DbObject implements IcingaConfigRenderer return $this; } - if ($this->propertyIsBoolean($key)) { - return parent::set($key, DbDataFormatter::normalizeBoolean($value)); - } - // e.g. zone_id if ($this->propertyIsRelation($key)) { return $this->setRelation($key, $value); @@ -906,20 +895,20 @@ abstract class IcingaObject extends DbObject implements IcingaConfigRenderer $type = strtolower($this->getType()); $query = $this->db->select()->from( - ['gr' => "icinga_${type}group_${type}_resolved"], + ['gr' => "icinga_{$type}group_{$type}_resolved"], ['g.object_name'] )->join( - ['g' => "icinga_${type}group"], - "g.id = gr.${type}group_id", + ['g' => "icinga_{$type}group"], + "g.id = gr.{$type}group_id", [] )->joinLeft( - ['go' => "icinga_${type}group_${type}"], - "go.${type}group_id = gr.${type}group_id AND go.${type}_id = " . (int) $id, + ['go' => "icinga_{$type}group_{$type}"], + "go.{$type}group_id = gr.{$type}group_id AND go.{$type}_id = " . (int) $id, [] )->where( - "gr.${type}_id = ?", + "gr.{$type}_id = ?", (int) $id - )->where("go.${type}_id IS NULL")->order('g.object_name'); + )->where("go.{$type}_id IS NULL")->order('g.object_name'); return $this->db->fetchCol($query); } @@ -1812,9 +1801,21 @@ abstract class IcingaObject extends DbObject implements IcingaConfigRenderer return; } - $config->configFile( - 'zones.d/' . $this->getRenderingZone($config) . '/' . $this->getRenderingFilename() - )->addObject($this); + foreach ($this->getRenderingZones($config) as $zone) { + $config->configFile( + 'zones.d/' . $zone . '/' . $this->getRenderingFilename() + )->addObject($this); + } + } + + protected function getRenderingZones(IcingaConfig $config): array + { + $zone = $this->getRenderingZone($config); + if ($zone === self::ALL_NON_GLOBAL_ZONES) { + return $config->listNonGlobalZones(); + } + + return [$zone]; } public function getRenderingFilename() @@ -2193,7 +2194,12 @@ abstract class IcingaObject extends DbObject implements IcingaConfigRenderer protected function renderSuffix() { - return "}\n\n"; + $prefix = ''; + if ($this->rendersConditionalTemplate()) { + $prefix = '} '; + } + + return "$prefix}\n\n"; } protected function renderLegacySuffix() @@ -2418,14 +2424,25 @@ abstract class IcingaObject extends DbObject implements IcingaConfigRenderer protected function renderObjectHeader() { + $prefix = ''; + $renderedName = c::renderString($this->getObjectName()); + if ($this->rendersConditionalTemplate()) { + $prefix = sprintf('if (! get_template(%s, %s)) { ', $this->getType(), $renderedName); + } return sprintf( - "%s %s %s {\n", + "%s%s %s %s {\n", + $prefix, $this->getObjectTypeName(), $this->getType(), - c::renderString($this->getObjectName()) + $renderedName ); } + protected function rendersConditionalTemplate(): bool + { + return false; + } + public function getLegacyObjectType() { return strtolower($this->getType()); @@ -2674,16 +2691,16 @@ abstract class IcingaObject extends DbObject implements IcingaConfigRenderer /** @var DbObject $class */ $class = DbObjectTypeRegistry::classByType($type); + if ($keyColumn === null && is_array($class::create()->getKeyName())) { + return $class::loadAll($db, $query); + } + if ($keyColumn === null) { if (method_exists($class, 'getKeyColumnName')) { $keyColumn = $class::getKeyColumnName(); } } - if (is_array($class::create()->getKeyName())) { - return $class::loadAll($db, $query); - } - if (PrefetchCache::shouldBeUsed() && $query === null && $keyColumn === static::getKeyColumnName() @@ -2890,11 +2907,14 @@ abstract class IcingaObject extends DbObject implements IcingaConfigRenderer } } } + if ($this->propertyIsInterval($k) && is_string($v) && ctype_digit($v)) { + $v = (int) $v; + } // TODO: Do not ship null properties based on flag? if (!$skipDefaults || $this->differsFromDefaultValue($k, $v)) { if ($k === 'disabled' || $this->propertyIsBoolean($k)) { - $props[$k] = $this->booleanForDbValue($v); + $props[$k] = DbDataFormatter::booleanForDbValue($v); } else { $props[$k] = $v; } @@ -3005,18 +3025,6 @@ abstract class IcingaObject extends DbObject implements IcingaConfigRenderer return (object) $props; } - protected function booleanForDbValue($value) - { - if ($value === 'y') { - return true; - } - if ($value === 'n') { - return false; - } - - return $value; // let this fail elsewhere, if not null - } - public function listImportNames() { if ($this->gotImports()) { @@ -3161,7 +3169,7 @@ abstract class IcingaObject extends DbObject implements IcingaConfigRenderer if ($this->differsFromDefaultValue($k, $v)) { if ($k === 'disabled' || $this->propertyIsBoolean($k)) { - $props[$k] = $this->booleanForDbValue($v); + $props[$k] = DbDataFormatter::booleanForDbValue($v); } else { $props[$k] = $v; } diff --git a/library/Director/Objects/IcingaObjectGroup.php b/library/Director/Objects/IcingaObjectGroup.php index c0bec54..c076f52 100644 --- a/library/Director/Objects/IcingaObjectGroup.php +++ b/library/Director/Objects/IcingaObjectGroup.php @@ -2,9 +2,7 @@ namespace Icinga\Module\Director\Objects; -use Icinga\Module\Director\Db; use Icinga\Module\Director\DirectorObject\Automation\ExportInterface; -use Icinga\Module\Director\Exception\DuplicateKeyException; abstract class IcingaObjectGroup extends IcingaObject implements ExportInterface { @@ -24,53 +22,51 @@ abstract class IcingaObjectGroup extends IcingaObject implements ExportInterface 'assign_filter' => null, ]; + protected $memberShipShouldBeRefreshed = false; + public function getUniqueIdentifier() { return $this->getObjectName(); } - /** - * @return object - * @deprecated please use \Icinga\Module\Director\Data\Exporter - * @throws \Icinga\Exception\NotFoundError - */ - public function export() + protected function prefersGlobalZone() { - return $this->toPlainObject(); + return true; } - /** - * @param $plain - * @param Db $db - * @param bool $replace - * @return IcingaObjectGroup - * @throws DuplicateKeyException - * @throws \Icinga\Exception\NotFoundError - */ - public static function import($plain, Db $db, $replace = false) + protected function beforeStore() { - $properties = (array) $plain; - $name = $properties['object_name']; - $key = $name; - - if ($replace && static::exists($key, $db)) { - $object = static::load($key, $db); - } elseif (static::exists($key, $db)) { - throw new DuplicateKeyException( - 'Group "%s" already exists', - $name - ); + parent::beforeStore(); + if ($this->hasBeenLoadedFromDb()) { + if (!array_key_exists('assign_filter', $this->getModifiedProperties())) { + $this->memberShipShouldBeRefreshed = false; + return; + } } else { - $object = static::create([], $db); + if ($this->hasProperty('assign_filter') && $this->get('assign_filter') === null) { + $this->memberShipShouldBeRefreshed = false; + return; + } } - $object->setProperties($properties); + if ($this->hasProperty('assign_filter')) { + $this->memberShipShouldBeRefreshed = true; + } + } - return $object; + protected function notifyResolvers() + { + if ($this->memberShipShouldBeRefreshed) { + $resolver = $this->getMemberShipResolver(); + $resolver->addGroup($this); + $resolver->refreshDb(); + } + + return $this; } - protected function prefersGlobalZone() + protected function getMemberShipResolver() { - return true; + return null; } } diff --git a/library/Director/Objects/IcingaObjectGroups.php b/library/Director/Objects/IcingaObjectGroups.php index 8bef1b1..8683c77 100644 --- a/library/Director/Objects/IcingaObjectGroups.php +++ b/library/Director/Objects/IcingaObjectGroups.php @@ -183,6 +183,10 @@ class IcingaObjectGroups implements Iterator, Countable, IcingaConfigRenderer return $this; } + if (is_int($group)) { + $group = (string) $group; + } + /** @var IcingaObjectGroup $class */ $class = $this->getGroupClass(); @@ -224,7 +228,7 @@ class IcingaObjectGroups implements Iterator, Countable, IcingaConfigRenderer } else { throw new RuntimeException( 'Invalid group object: %s', - var_export($group, 1) + var_export($group, true) ); } diff --git a/library/Director/Objects/IcingaObjectLegacyAssignments.php b/library/Director/Objects/IcingaObjectLegacyAssignments.php index 6ab75c8..2db29b4 100644 --- a/library/Director/Objects/IcingaObjectLegacyAssignments.php +++ b/library/Director/Objects/IcingaObjectLegacyAssignments.php @@ -36,7 +36,7 @@ class IcingaObjectLegacyAssignments $assigns = array(); $ignores = array(); foreach ($values as $type => $value) { - if (strpos($value, '|') !== false || strpos($value, '&' !== false)) { + if (strpos($value, '|') !== false || strpos($value, '&') !== false) { $value = '(' . $value . ')'; } diff --git a/library/Director/Objects/IcingaObjectMultiRelations.php b/library/Director/Objects/IcingaObjectMultiRelations.php index a1ec9a2..5931595 100644 --- a/library/Director/Objects/IcingaObjectMultiRelations.php +++ b/library/Director/Objects/IcingaObjectMultiRelations.php @@ -239,7 +239,7 @@ class IcingaObjectMultiRelations implements Iterator, Countable, IcingaConfigRen } else { throw new ProgrammingError( 'Invalid related object: %s', - var_export($relation, 1) + var_export($relation, true) ); } diff --git a/library/Director/Objects/IcingaRelatedObject.php b/library/Director/Objects/IcingaRelatedObject.php index d35bcb0..ca33688 100644 --- a/library/Director/Objects/IcingaRelatedObject.php +++ b/library/Director/Objects/IcingaRelatedObject.php @@ -180,7 +180,7 @@ class IcingaRelatedObject } else { throw new ProgrammingError( 'Related object can be name or object, got: %s', - var_export($related, 1) + var_export($related, true) ); } diff --git a/library/Director/Objects/IcingaService.php b/library/Director/Objects/IcingaService.php index 9479ef7..cbf9ca5 100644 --- a/library/Director/Objects/IcingaService.php +++ b/library/Director/Objects/IcingaService.php @@ -8,7 +8,6 @@ use Icinga\Module\Director\Data\PropertiesFilter; use Icinga\Module\Director\Db; use Icinga\Module\Director\Db\Cache\PrefetchCache; use Icinga\Module\Director\DirectorObject\Automation\ExportInterface; -use Icinga\Module\Director\Exception\DuplicateKeyException; use Icinga\Module\Director\IcingaConfig\IcingaConfig; use Icinga\Module\Director\IcingaConfig\IcingaConfigHelper as c; use Icinga\Module\Director\IcingaConfig\IcingaLegacyConfigHelper as c1; @@ -170,94 +169,6 @@ class IcingaService extends IcingaObject implements ExportInterface } /** - * @return object - * @deprecated please use \Icinga\Module\Director\Data\Exporter - * @throws \Icinga\Exception\NotFoundError - */ - public function export() - { - // TODO: ksort in toPlainObject? - $props = (array) $this->toPlainObject(); - $props['fields'] = $this->loadFieldReferences(); - ksort($props); - - return (object) $props; - } - - /** - * @param $plain - * @param Db $db - * @param bool $replace - * @return IcingaService - * @throws DuplicateKeyException - * @throws \Icinga\Exception\NotFoundError - */ - public static function import($plain, Db $db, $replace = false) - { - $properties = (array) $plain; - $name = $properties['object_name']; - if ($properties['object_type'] !== 'template') { - throw new InvalidArgumentException(sprintf( - 'Can import only Templates, got "%s" for "%s"', - $properties['object_type'], - $name - )); - } - $key = [ - 'object_type' => 'template', - 'object_name' => $name - ]; - - if ($replace && static::exists($key, $db)) { - $object = static::load($key, $db); - } elseif (static::exists($key, $db)) { - throw new DuplicateKeyException( - 'Service Template "%s" already exists', - $name - ); - } else { - $object = static::create([], $db); - } - - // $object->newFields = $properties['fields']; - unset($properties['fields']); - $object->setProperties($properties); - - return $object; - } - - /** - * @deprecated please use \Icinga\Module\Director\Data\FieldReferenceLoader - * @return array - */ - protected function loadFieldReferences() - { - $db = $this->getDb(); - - $res = $db->fetchAll( - $db->select()->from([ - 'sf' => 'icinga_service_field' - ], [ - 'sf.datafield_id', - 'sf.is_required', - 'sf.var_filter', - ])->join(['df' => 'director_datafield'], 'df.id = sf.datafield_id', []) - ->where('service_id = ?', $this->get('id')) - ->order('varname ASC') - ); - - if (empty($res)) { - return []; - } else { - foreach ($res as $field) { - $field->datafield_id = (int) $field->datafield_id; - } - - return $res; - } - } - - /** * @param string $key * @return $this */ @@ -473,6 +384,11 @@ class IcingaService extends IcingaObject implements ExportInterface } } + protected function rendersConditionalTemplate(): bool + { + return $this->getRenderingZone() === self::ALL_NON_GLOBAL_ZONES; + } + /** * @return bool */ @@ -667,11 +583,22 @@ class IcingaService extends IcingaObject implements ExportInterface protected function getDefaultZone(IcingaConfig $config = null) { + // Hint: this isn't possible yet, as we're unable to render dependent apply rules to multiple zones as well + // if ($this->isTemplate()) { + // return self::ALL_NON_GLOBAL_ZONES; + // } if ($this->get('host_id') === null) { return parent::getDefaultZone(); } else { - return $this->getRelatedObject('host', $this->get('host_id')) + $zone = $this->getRelatedObject('host', $this->get('host_id')) ->getRenderingZone($config); + + // Hint: this avoids problems with host templates rendered to all non-global zones + if ($zone === self::ALL_NON_GLOBAL_ZONES) { + $zone = $this->connection->getDefaultGlobalZoneName(); + } + + return $zone; } } diff --git a/library/Director/Objects/IcingaServiceGroup.php b/library/Director/Objects/IcingaServiceGroup.php index ae43ff3..92fb1cb 100644 --- a/library/Director/Objects/IcingaServiceGroup.php +++ b/library/Director/Objects/IcingaServiceGroup.php @@ -31,12 +31,8 @@ class IcingaServiceGroup extends IcingaObjectGroup return $this; } - protected function notifyResolvers() + protected function getMemberShipResolver() { - $resolver = $this->getServiceGroupMembershipResolver(); - $resolver->addGroup($this); - $resolver->refreshDb(); - - return $this; + return $this->getServiceGroupMembershipResolver(); } } diff --git a/library/Director/Objects/IcingaServiceSet.php b/library/Director/Objects/IcingaServiceSet.php index 8217a59..252c52a 100644 --- a/library/Director/Objects/IcingaServiceSet.php +++ b/library/Director/Objects/IcingaServiceSet.php @@ -5,16 +5,14 @@ namespace Icinga\Module\Director\Objects; use Exception; use Icinga\Data\Filter\Filter; use Icinga\Module\Director\Data\Db\ServiceSetQueryBuilder; -use Icinga\Module\Director\Db; use Icinga\Module\Director\Db\Cache\PrefetchCache; -use Icinga\Module\Director\Db\DbUtil; use Icinga\Module\Director\DirectorObject\Automation\ExportInterface; use Icinga\Module\Director\Exception\DuplicateKeyException; use Icinga\Module\Director\IcingaConfig\IcingaConfig; use Icinga\Module\Director\Resolver\HostServiceBlacklist; use InvalidArgumentException; use Ramsey\Uuid\Uuid; -use RuntimeException; +use stdClass; class IcingaServiceSet extends IcingaObject implements ExportInterface { @@ -46,6 +44,33 @@ class IcingaServiceSet extends IcingaObject implements ExportInterface 'host' => 'IcingaHost', ); + /** @var IcingaService[] Cached services */ + protected $cachedServices = []; + + /** @var IcingaService[]|null */ + private $services; + + /** + * Set the services to be cached + * + * @param $services IcingaService[] + * @return void + */ + public function setCachedServices($services) + { + $this->cachedServices = $services; + } + + /** + * Get the cached services + * + * @return IcingaService[] + */ + public function getCachedServices() + { + return $this->cachedServices; + } + public function isDisabled() { return false; @@ -79,6 +104,61 @@ class IcingaServiceSet extends IcingaObject implements ExportInterface } /** + * @param stdClass[] $services + * @return void + */ + public function setServices(array $services) + { + $existing = $this->getServices(); + $uuidMap = []; + foreach ($existing as $service) { + $uuidMap[$service->getUniqueId()->getBytes()] = $service; + } + $this->services = []; + foreach ($services as $service) { + if (isset($service->uuid)) { + $uuid = Uuid::fromString($service->uuid)->getBytes(); + $current = $uuidMap[$uuid] ?? IcingaService::create([], $this->connection); + } else { + if (! is_object($service)) { + var_dump($service); + exit; + } + $current = $existing[$service->object_name] ?? IcingaService::create([], $this->connection); + } + $current->setProperties((array) $service); + $this->services[] = $current; + } + } + + protected function storeRelatedServices() + { + if ($this->services === null) { + $cachedServices = $this->getCachedServices(); + if ($cachedServices) { + $this->services = $cachedServices; + } else { + return; + } + } + + $seen = []; + /** @var IcingaService $service */ + foreach ($this->services as $service) { + $seen[$service->getUniqueId()->getBytes()] = true; + $service->set('service_set_id', $this->get('id')); + $service->store(); + } + + foreach ($this->fetchServices() as $service) { + if (!isset($seen[$service->getUniqueId()->getBytes()])) { + $service->delete(); + } + } + } + + /** + * @deprecated * @return IcingaService[] * @throws \Icinga\Exception\NotFoundError */ @@ -126,129 +206,9 @@ class IcingaServiceSet extends IcingaObject implements ExportInterface return $this->getObjectName(); } - /** - * @return object - * @deprecated please use \Icinga\Module\Director\Data\Exporter - * @throws \Icinga\Exception\NotFoundError - */ - public function export() - { - if ($this->get('host_id')) { - $result = $this->exportSetOnHost(); - } else { - $result = $this->exportTemplate(); - } - - unset($result->uuid); - return $result; - } - - protected function exportSetOnHost() - { - // TODO. - throw new RuntimeException('Not yet'); - } - - /** - * @return object - * @deprecated - * @throws \Icinga\Exception\NotFoundError - */ - protected function exportTemplate() - { - $props = $this->getProperties(); - unset($props['id'], $props['host_id']); - $props['services'] = []; - foreach ($this->getServiceObjects() as $serviceObject) { - $props['services'][$serviceObject->getObjectName()] = $serviceObject->export(); - } - ksort($props); - - return (object) $props; - } - - /** - * @param $plain - * @param Db $db - * @param bool $replace - * @return IcingaServiceSet - * @throws DuplicateKeyException - * @throws \Icinga\Exception\NotFoundError - */ - public static function import($plain, Db $db, $replace = false) - { - $properties = (array) $plain; - $name = $properties['object_name']; - if (isset($properties['services'])) { - $services = $properties['services']; - unset($properties['services']); - } else { - $services = []; - } - - if ($properties['object_type'] !== 'template') { - throw new InvalidArgumentException(sprintf( - 'Can import only Templates, got "%s" for "%s"', - $properties['object_type'], - $name - )); - } - if ($replace && static::exists($name, $db)) { - $object = static::load($name, $db); - } elseif (static::exists($name, $db)) { - throw new DuplicateKeyException( - 'Service Set "%s" already exists', - $name - ); - } else { - $object = static::create([], $db); - } - - $object->setProperties($properties); - - // This is not how other imports work, but here we need an ID - if (! $object->hasBeenLoadedFromDb()) { - $object->store(); - } - - $setId = $object->get('id'); - $sQuery = $db->getDbAdapter()->select()->from( - ['s' => 'icinga_service'], - 's.*' - )->where('service_set_id = ?', $setId); - $existingServices = IcingaService::loadAll($db, $sQuery, 'object_name'); - $serviceNames = []; - foreach ($services as $service) { - if (isset($service->fields)) { - unset($service->fields); - } - $name = $service->object_name; - $serviceNames[] = $name; - if (isset($existingServices[$name])) { - $existing = $existingServices[$name]; - $existing->setProperties((array) $service); - $existing->set('service_set_id', $setId); - if ($existing->hasBeenModified()) { - $existing->store(); - } - } else { - $new = IcingaService::create((array) $service, $db); - $new->set('service_set_id', $setId); - $new->store(); - } - } - - foreach ($existingServices as $existing) { - if (!in_array($existing->getObjectName(), $serviceNames)) { - $existing->delete(); - } - } - - return $object; - } - public function beforeDelete() { + $this->setCachedServices($this->getServices()); // check if this is a template, or directly assigned to a host if ($this->get('host_id') === null) { // find all host sets and delete them @@ -295,8 +255,8 @@ class IcingaServiceSet extends IcingaObject implements ExportInterface */ public function renderToConfig(IcingaConfig $config) { - // always print the header, so you have minimal info present - $file = $this->getConfigFileWithHeader($config); + $files = []; + $zone = $this->getRenderingZone($config) ; if ($this->get('assign_filter') === null && $this->isTemplate()) { return; @@ -334,7 +294,15 @@ class IcingaServiceSet extends IcingaObject implements ExportInterface } $this->copyVarsToService($service); - $file->addObject($service); + foreach ($service->getRenderingZones($config) as $serviceZone) { + $file = $this->getConfigFileWithHeader($config, $serviceZone, $files); + $file->addObject($service); + } + } + + if (empty($files)) { + // always print the header, so you have minimal info present + $this->getConfigFileWithHeader($config, $zone, $files); } } @@ -355,14 +323,18 @@ class IcingaServiceSet extends IcingaObject implements ExportInterface return $lookup->getBlacklistedHostnamesForService($service); } - protected function getConfigFileWithHeader(IcingaConfig $config) + protected function getConfigFileWithHeader(IcingaConfig $config, $zone, &$files = []) { - $file = $config->configFile( - 'zones.d/' . $this->getRenderingZone($config) . '/servicesets' - ); + if (!isset($files[$zone])) { + $file = $config->configFile( + 'zones.d/' . $zone . '/servicesets' + ); - $file->addContent($this->getConfigHeaderComment($config)); - return $file; + $file->addContent($this->getConfigHeaderComment($config)); + $files[$zone] = $file; + } + + return $files[$zone]; } protected function getConfigHeaderComment(IcingaConfig $config) @@ -372,13 +344,13 @@ class IcingaServiceSet extends IcingaObject implements ExportInterface if ($config->isLegacy()) { if ($assign !== null) { - return "## applied Service Set '${name}'\n\n"; + return "## applied Service Set '{$name}'\n\n"; } else { - return "## Service Set '${name}' on this host\n\n"; + return "## Service Set '{$name}' on this host\n\n"; } } else { $comment = [ - "Service Set: ${name}", + "Service Set: {$name}", ]; if (($host = $this->get('host')) !== null) { @@ -505,7 +477,23 @@ class IcingaServiceSet extends IcingaObject implements ExportInterface /** * @return IcingaService[] */ - public function fetchServices() + public function getServices(): array + { + if ($this->services !== null) { + return $this->services; + } + + if ($this->hasBeenLoadedFromDb()) { + return $this->fetchServices(); + } + + return []; + } + + /** + * @return IcingaService[] + */ + public function fetchServices(): array { if ($store = self::$dbObjectStore) { $uuid = $store->getBranch()->getUuid(); @@ -569,6 +557,24 @@ class IcingaServiceSet extends IcingaObject implements ExportInterface } } + public function onStore() + { + $this->storeRelatedServices(); + } + + public function hasBeenModified() + { + if ($this->services !== null) { + foreach ($this->services as $service) { + if ($service->hasBeenModified()) { + return true; + } + } + } + + return parent::hasBeenModified(); + } + public function toSingleIcingaConfig() { $config = parent::toSingleIcingaConfig(); diff --git a/library/Director/Objects/IcingaTemplateChoice.php b/library/Director/Objects/IcingaTemplateChoice.php index 1a1be90..a2be07a 100644 --- a/library/Director/Objects/IcingaTemplateChoice.php +++ b/library/Director/Objects/IcingaTemplateChoice.php @@ -3,9 +3,7 @@ namespace Icinga\Module\Director\Objects; use Icinga\Exception\ProgrammingError; -use Icinga\Module\Director\Db; use Icinga\Module\Director\DirectorObject\Automation\ExportInterface; -use Icinga\Module\Director\Exception\DuplicateKeyException; use Icinga\Module\Director\Web\Form\QuickForm; class IcingaTemplateChoice extends IcingaObject implements ExportInterface @@ -36,63 +34,6 @@ class IcingaTemplateChoice extends IcingaObject implements ExportInterface return $this->getObjectName(); } - /** - * @param $plain - * @param Db $db - * @param bool $replace - * @return IcingaTemplateChoice - * @throws DuplicateKeyException - * @throws \Icinga\Exception\NotFoundError - */ - public static function import($plain, Db $db, $replace = false) - { - $properties = (array) $plain; - if (isset($properties['originalId'])) { - unset($properties['originalId']); - } - $name = $properties['object_name']; - $key = $name; - - if ($replace && static::exists($key, $db)) { - $object = static::load($key, $db); - } elseif (static::exists($key, $db)) { - throw new DuplicateKeyException( - 'Template Choice "%s" already exists', - $name - ); - } else { - $object = static::create([], $db); - } - - $object->setProperties($properties); - - return $object; - } - - /** - * @deprecated please use \Icinga\Module\Director\Data\Exporter - * @return array|object|\stdClass - */ - public function export() - { - $plain = (object) $this->getProperties(); - $plain->originalId = $plain->id; - unset($plain->id); - $requiredId = $plain->required_template_id; - unset($plain->required_template_id); - if ($requiredId) { - $db = $this->getDb(); - $query = $db->select() - ->from(['o' => $this->getObjectTableName()], 'o.object_name')->where("o.object_type = 'template'") - ->where('o.id = ?', $this->get('id')); - $plain->required_template = $db->fetchOne($query); - } - - $plain->members = array_values($this->getMembers()); - - return $plain; - } - public function isMainChoice() { return $this->hasBeenLoadedFromDb() @@ -155,7 +96,7 @@ class IcingaTemplateChoice extends IcingaObject implements ExportInterface public function hasBeenModified() { - if ($this->newChoices !== null && $this->choices !== $this->newChoices) { + if ($this->newChoices !== null && ($this->choices ?? $this->fetchChoices()) !== $this->newChoices) { return true; } @@ -284,7 +225,7 @@ class IcingaTemplateChoice extends IcingaObject implements ExportInterface } else { throw new ProgrammingError( 'Expected array or null for allowed_roles, got %s', - var_export($roles, 1) + var_export($roles, true) ); } } diff --git a/library/Director/Objects/IcingaTemplateResolver.php b/library/Director/Objects/IcingaTemplateResolver.php index 61122a0..7f14967 100644 --- a/library/Director/Objects/IcingaTemplateResolver.php +++ b/library/Director/Objects/IcingaTemplateResolver.php @@ -45,7 +45,6 @@ class IcingaTemplateResolver { $this->object = $object; $this->type = $object->getShortTableName(); - $this->table = $object->getTableName(); $this->connection = $object->getConnection(); $this->db = $this->connection->getDbAdapter(); diff --git a/library/Director/Objects/IcingaTimePeriod.php b/library/Director/Objects/IcingaTimePeriod.php index 1232366..081cdd9 100644 --- a/library/Director/Objects/IcingaTimePeriod.php +++ b/library/Director/Objects/IcingaTimePeriod.php @@ -2,9 +2,7 @@ namespace Icinga\Module\Director\Objects; -use Icinga\Module\Director\Db; use Icinga\Module\Director\DirectorObject\Automation\ExportInterface; -use Icinga\Module\Director\Exception\DuplicateKeyException; class IcingaTimePeriod extends IcingaObject implements ExportInterface { @@ -56,48 +54,6 @@ class IcingaTimePeriod extends IcingaObject implements ExportInterface } /** - * @deprecated please use \Icinga\Module\Director\Data\Exporter - * @return object - * @throws \Icinga\Exception\NotFoundError - */ - public function export() - { - $props = (array) $this->toPlainObject(); - ksort($props); - - return (object) $props; - } - - /** - * @param $plain - * @param Db $db - * @param bool $replace - * @return static - * @throws DuplicateKeyException - * @throws \Icinga\Exception\NotFoundError - */ - public static function import($plain, Db $db, $replace = false) - { - $properties = (array) $plain; - $name = $properties['object_name']; - $key = $name; - - if ($replace && static::exists($key, $db)) { - $object = static::load($key, $db); - } elseif (static::exists($key, $db)) { - throw new DuplicateKeyException( - 'Time Period "%s" already exists', - $name - ); - } else { - $object = static::create([], $db); - } - $object->setProperties($properties); - - return $object; - } - - /** * Render update property * * Avoid complaints for method names with underscore: diff --git a/library/Director/Objects/IcingaUser.php b/library/Director/Objects/IcingaUser.php index 394e849..4100245 100644 --- a/library/Director/Objects/IcingaUser.php +++ b/library/Director/Objects/IcingaUser.php @@ -48,43 +48,6 @@ class IcingaUser extends IcingaObject implements ExportInterface 'zone' => 'IcingaZone', ); - public function export() - { - return ImportExportHelper::simpleExport($this); - } - - /** - * @param $plain - * @param Db $db - * @param bool $replace - * @return IcingaUser - * @throws DuplicateKeyException - * @throws \Icinga\Exception\NotFoundError - */ - public static function import($plain, Db $db, $replace = false) - { - $properties = (array) $plain; - $key = $properties['object_name']; - - if ($replace && static::exists($key, $db)) { - $object = static::load($key, $db); - } elseif (static::exists($key, $db)) { - throw new DuplicateKeyException( - 'Cannot import, %s "%s" already exists', - static::create([])->getShortTableName(), - $key - ); - } else { - $object = static::create([], $db); - } - - // $object->newFields = $properties['fields']; - unset($properties['fields']); - $object->setProperties($properties); - - return $object; - } - public function getUniqueIdentifier() { return $this->getObjectName(); diff --git a/library/Director/Objects/ImportExportHelper.php b/library/Director/Objects/ImportExportHelper.php deleted file mode 100644 index 98d34c6..0000000 --- a/library/Director/Objects/ImportExportHelper.php +++ /dev/null @@ -1,68 +0,0 @@ -<?php - -namespace Icinga\Module\Director\Objects; - -use Icinga\Module\Director\Db; - -/** - * Helper class, allows to reduce duplicate code. Might be moved elsewhere - * afterwards - */ -class ImportExportHelper -{ - /** - * Does not support every type out of the box - * - * @param IcingaObject $object - * @return object - * @throws \Icinga\Exception\NotFoundError - */ - public static function simpleExport(IcingaObject $object) - { - $props = (array) $object->toPlainObject(); - $props['fields'] = static::fetchFields($object); - ksort($props); // TODO: ksort in toPlainObject? - - return (object) $props; - } - - public static function fetchFields(IcingaObject $object) - { - return static::loadFieldReferences( - $object->getConnection(), - $object->getShortTableName(), - $object->get('id') - ); - } - - /** - * @param Db $connection - * @param string $type Warning: this will not be validated. - * @param int $id - * @return array - */ - public static function loadFieldReferences(Db $connection, $type, $id) - { - $db = $connection->getDbAdapter(); - $res = $db->fetchAll( - $db->select()->from([ - 'f' => "icinga_${type}_field" - ], [ - 'f.datafield_id', - 'f.is_required', - 'f.var_filter', - ])->join(['df' => 'director_datafield'], 'df.id = f.datafield_id', []) - ->where("${type}_id = ?", $id) - ->order('varname ASC') - ); - - if (empty($res)) { - return []; - } - - foreach ($res as $field) { - $field->datafield_id = (int) $field->datafield_id; - } - return $res; - } -} diff --git a/library/Director/Objects/ImportRowModifier.php b/library/Director/Objects/ImportRowModifier.php index 76982c2..afebbad 100644 --- a/library/Director/Objects/ImportRowModifier.php +++ b/library/Director/Objects/ImportRowModifier.php @@ -18,13 +18,14 @@ class ImportRowModifier extends DbObjectWithSettings implements InstantiatedViaH protected $autoincKeyName = 'id'; protected $defaultProperties = [ - 'id' => null, - 'source_id' => null, - 'property_name' => null, - 'provider_class' => null, - 'target_property' => null, - 'priority' => null, - 'description' => null, + 'id' => null, + 'source_id' => null, + 'property_name' => null, + 'provider_class' => null, + 'target_property' => null, + 'filter_expression' => null, + 'priority' => null, + 'description' => null, ]; protected $settingsTable = 'import_row_modifier_setting'; diff --git a/library/Director/Objects/ImportSource.php b/library/Director/Objects/ImportSource.php index fd892ef..7477472 100644 --- a/library/Director/Objects/ImportSource.php +++ b/library/Director/Objects/ImportSource.php @@ -3,12 +3,14 @@ namespace Icinga\Module\Director\Objects; use Icinga\Application\Benchmark; +use Icinga\Data\Filter\Filter; use Icinga\Exception\NotFoundError; use Icinga\Module\Director\Application\MemoryLimit; use Icinga\Module\Director\Data\Db\DbObjectWithSettings; use Icinga\Module\Director\Db; use Icinga\Module\Director\DirectorObject\Automation\ExportInterface; use Icinga\Module\Director\Exception\DuplicateKeyException; +use Icinga\Module\Director\Filter\FilterEnrichment; use Icinga\Module\Director\Hook\PropertyModifierHook; use Icinga\Module\Director\Import\Import; use Icinga\Module\Director\Import\SyncUtils; @@ -52,68 +54,6 @@ class ImportSource extends DbObjectWithSettings implements ExportInterface private $newRowModifiers; - /** - * @deprecated please use \Icinga\Module\Director\Data\FieldReferenceLoader - * @return \stdClass - */ - public function export() - { - $plain = $this->getProperties(); - $plain['originalId'] = $plain['id']; - unset($plain['id']); - - foreach ($this->stateProperties as $key) { - unset($plain[$key]); - } - - $plain['settings'] = (object) $this->getSettings(); - $plain['modifiers'] = $this->exportRowModifiers(); - ksort($plain); - - return (object) $plain; - } - - /** - * @param $plain - * @param Db $db - * @param bool $replace - * @return ImportSource - * @throws DuplicateKeyException - * @throws NotFoundError - */ - public static function import($plain, Db $db, $replace = false) - { - $properties = (array) $plain; - if (isset($properties['originalId'])) { - $id = $properties['originalId']; - unset($properties['originalId']); - } else { - $id = null; - } - $name = $properties['source_name']; - - if ($replace && $id && static::existsWithNameAndId($name, $id, $db)) { - $object = static::loadWithAutoIncId($id, $db); - } elseif ($replace && static::exists($name, $db)) { - $object = static::load($name, $db); - } elseif (static::existsWithName($name, $db)) { - throw new DuplicateKeyException( - 'Import Source %s already exists', - $name - ); - } else { - $object = static::create([], $db); - } - - if (! isset($properties['modifiers'])) { - $properties['modifiers'] = []; - } - - $object->setProperties($properties); - - return $object; - } - public function setModifiers(array $modifiers) { if ($this->loadedRowModifiers === null && $this->hasBeenLoadedFromDb()) { @@ -314,10 +254,14 @@ class ImportSource extends DbObjectWithSettings implements ExportInterface foreach ($modifiers as $modPair) { /** @var PropertyModifierHook $modifier */ - list($property, $modifier) = $modPair; + /** @var ?Filter $filter */ + list($property, $modifier, $filter) = $modPair; $rejected = []; $newRows = []; foreach ($data as $key => $row) { + if ($filter && ! $filter->matches($row)) { + continue; + } $this->applyPropertyModifierToRow($modifier, $property, $row); if ($modifier->rejectsRow()) { $rejected[] = $key; @@ -434,7 +378,12 @@ class ImportSource extends DbObjectWithSettings implements ExportInterface { $mods = []; foreach ($this->fetchRowModifiers() as $mod) { - $mods[] = [$mod->get('property_name'), $mod->getInstance()]; + if ($filterExpression = $mod->get('filter_expression')) { + $filter = FilterEnrichment::enrichFilter(Filter::fromQueryString($filterExpression)); + } else { + $filter = null; + } + $mods[] = [$mod->get('property_name'), $mod->getInstance(), $filter]; } return $mods; @@ -530,7 +479,7 @@ class ImportSource extends DbObjectWithSettings implements ExportInterface protected function raiseLimits() { MemoryLimit::raiseTo('1024M'); - ini_set('max_execution_time', 0); + ini_set('max_execution_time', '0'); return $this; } diff --git a/library/Director/Objects/ObjectApplyMatches.php b/library/Director/Objects/ObjectApplyMatches.php index 018c880..d749d6a 100644 --- a/library/Director/Objects/ObjectApplyMatches.php +++ b/library/Director/Objects/ObjectApplyMatches.php @@ -172,7 +172,7 @@ abstract class ObjectApplyMatches $col = $filter->getColumn(); $type = static::$type; - if ($type && substr($col, 0, strlen($type) + 1) === "${type}.") { + if ($type && substr($col, 0, strlen($type) + 1) === "{$type}.") { $filter->setColumn($col = substr($col, strlen($type) + 1)); } diff --git a/library/Director/Objects/SyncRule.php b/library/Director/Objects/SyncRule.php index 89f7fd1..270a882 100644 --- a/library/Director/Objects/SyncRule.php +++ b/library/Director/Objects/SyncRule.php @@ -43,6 +43,10 @@ class SyncRule extends DbObject implements ExportInterface 'last_attempt', ]; + protected $booleans = [ + 'purge_existing' => 'purge_existing', + ]; + private $sync; private $purgeStrategy; @@ -60,8 +64,6 @@ class SyncRule extends DbObject implements ExportInterface private $newSyncProperties; - private $originalId; - public function listInvolvedSourceIds() { if (! $this->hasBeenLoadedFromDb()) { @@ -257,61 +259,13 @@ class SyncRule extends DbObject implements ExportInterface } /** - * @deprecated please use \Icinga\Module\Director\Data\Exporter - * @return object - */ - public function export() - { - $plain = $this->getProperties(); - $plain['originalId'] = $plain['id']; - unset($plain['id']); - - foreach ($this->stateProperties as $key) { - unset($plain[$key]); - } - $plain['properties'] = $this->exportSyncProperties(); - ksort($plain); - - return (object) $plain; - } - - /** - * @param object $plain - * @param Db $db - * @param bool $replace - * @return static - * @throws DuplicateKeyException - * @throws \Icinga\Exception\NotFoundError + * Flat object has 'properties', but setProperties() is not available in DbObject + * + * @return void */ - public static function import($plain, Db $db, $replace = false) + public function setSyncProperties(?array $value) { - $properties = (array) $plain; - if (isset($properties['originalId'])) { - $id = $properties['originalId']; - unset($properties['originalId']); - } else { - $id = null; - } - $name = $properties['rule_name']; - - if ($replace && $id && static::existsWithNameAndId($name, $id, $db)) { - $object = static::loadWithAutoIncId($id, $db); - } elseif ($replace && static::exists($name, $db)) { - $object = static::load($name, $db); - } elseif (static::existsWithName($name, $db)) { - throw new DuplicateKeyException( - 'Sync Rule %s already exists', - $name - ); - } else { - $object = static::create([], $db); - } - - $object->newSyncProperties = $properties['properties']; - unset($properties['properties']); - $object->setProperties($properties); - - return $object; + $this->newSyncProperties = $value; } public function getUniqueIdentifier() @@ -329,12 +283,6 @@ class SyncRule extends DbObject implements ExportInterface $connection = $this->getConnection(); $db = $connection->getDbAdapter(); $myId = $this->get('id'); - if ($this->originalId === null) { - $originalId = $myId; - } else { - $originalId = $this->originalId; - $this->originalId = null; - } if ($this->hasBeenLoadedFromDb()) { $db->delete( 'sync_property', |