diff --git a/library/Director/Objects/HostApplyMatches.php b/library/Director/Objects/HostApplyMatches.php index cd505467..5feaee79 100644 --- a/library/Director/Objects/HostApplyMatches.php +++ b/library/Director/Objects/HostApplyMatches.php @@ -2,158 +2,7 @@ namespace Icinga\Module\Director\Objects; -use Icinga\Application\Benchmark; -use Icinga\Data\Filter\Filter; -use Icinga\Data\Filter\FilterExpression; -use Icinga\Module\Director\Db; -use Icinga\Module\Director\Db\Cache\PrefetchCache; - -class HostApplyMatches +class HostApplyMatches extends ObjectApplyMatches { - protected static $flatObjects; - - protected static $columnMap = array( - 'name' => 'object_name' - ); - - protected $host; - - protected $flatHost; - - public static function prepare(IcingaHost $host) - { - return new static($host); - } - - public function matchesFilter(Filter $filter) - { - $filter = clone($filter); - static::fixFilterColumns($filter); - return $filter->matches($this->flatHost); - } - - /** - * @param Filter $filter - * @param Db $db - * - * @return array - */ - public static function forFilter(Filter $filter, Db $db) - { - $result = array(); - Benchmark::measure(sprintf('Starting Filter %s', $filter)); - $filter = clone($filter); - static::fixFilterColumns($filter); - foreach (static::flatObjects($db) as $object) { - if ($filter->matches($object)) { - $name = $object->object_name; - $result[] = $name; - } - } - Benchmark::measure(sprintf('Got %d results for %s', count($result), $filter)); - - return array_values($result); - } - - protected static function flatObjects(Db $db) - { - if (self::$flatObjects === null) { - self::$flatObjects = static::fetchFlatObjects($db); - } - - return self::$flatObjects; - } - - protected static function raiseLimits() - { - // Raise limits. TODO: do this in a failsafe way, and only if necessary - // Note: IcingaConfig also raises the limit for generation, **but** we need the higher limit for preview. - if ((string) ini_get('memory_limit') !== '-1') { - ini_set('memory_limit', '1024M'); - } - } - - protected static function fetchFlatObjects(Db $db) - { - self::raiseLimits(); - - Benchmark::measure('HostApplyMatches: prefetching'); - PrefetchCache::initialize($db); - $all = IcingaHost::prefetchAll($db); - IcingaHostGroup::prefetchAll($db); - IcingaZone::prefetchAll($db); - IcingaCommand::prefetchAll($db); - Benchmark::measure('HostApplyMatches: preparing flat objects'); - - $objects = array(); - foreach ($all as $host) { - if ($host->isTemplate()) { - continue; - } - - $object = $host->toPlainObject(true, false); - static::flattenVars($object); - $objects[$host->getObjectName()] = $object; - } - Benchmark::measure('HostApplyMatches: cache ready'); - - return $objects; - } - - protected static function fixFilterColumns(Filter $filter) - { - if ($filter->isExpression()) { - static::fixFilterExpressionColumn($filter); - } else { - foreach ($filter->filters() as $sub) { - static::fixFilterColumns($sub); - } - } - } - - protected static function fixFilterExpressionColumn(FilterExpression $filter) - { - if (static::columnIsJson($filter)) { - $column = $filter->getExpression(); - $filter->setExpression($filter->getColumn()); - $filter->setColumn($column); - } - - /** @var FilterExpression $filter */ - $col = $filter->getColumn(); - if (substr($col, 0, 5) === 'host.') { - $filter->setColumn($col = substr($col, 5)); - } - - if (array_key_exists($col, self::$columnMap)) { - $filter->setColumn(self::$columnMap[$col]); - } - $filter->setExpression(json_decode($filter->getExpression())); - } - - protected static function columnIsJson(FilterExpression $filter) - { - $col = $filter->getColumn(); - return strlen($col) && $col[0] === '"'; - } - - protected static function flattenVars(& $object, $key = 'vars') - { - if (property_exists($object, 'vars')) { - foreach ($object->vars as $k => $v) { - if (is_object($v)) { - static::flattenVars($v, $k); - } - $object->{$key . '.' . $k} = $v; - } - unset($object->vars); - } - } - - protected function __construct(IcingaHost $host) - { - $this->host = $host; - $this->flatHost = $host->toPlainObject(true, false); - static::flattenVars($this->flatHost); - } + protected static $type = 'host'; } diff --git a/library/Director/Objects/ObjectApplyMatches.php b/library/Director/Objects/ObjectApplyMatches.php new file mode 100644 index 00000000..db2c7b18 --- /dev/null +++ b/library/Director/Objects/ObjectApplyMatches.php @@ -0,0 +1,194 @@ + 'object_name' + ); + + protected $object; + + protected $flatObject; + + protected static $type; + + public static function prepare(IcingaObject $object) + { + return new static($object); + } + + public function matchesFilter(Filter $filter) + { + $filter = clone($filter); + static::fixFilterColumns($filter); + return $filter->matches($this->flatObject); + } + + /** + * @param Filter $filter + * @param Db $db + * + * @return array + */ + public static function forFilter(Filter $filter, Db $db) + { + $result = array(); + Benchmark::measure(sprintf('Starting Filter %s', $filter)); + $filter = clone($filter); + static::fixFilterColumns($filter); + foreach (static::flatObjects($db) as $object) { + if ($filter->matches($object)) { + $name = $object->object_name; + $result[] = $name; + } + } + Benchmark::measure(sprintf('Got %d results for %s', count($result), $filter)); + + return array_values($result); + } + + protected static function getType() + { + if (static::$type === null) { + throw new ProgrammingError( + 'Implementations of %s need ::$type to be defined, %s has not', + __CLASS__, + get_called_class() + ); + } + + return static::$type; + } + + protected static function flatObjects(Db $db) + { + if (self::$flatObjects === null) { + self::$flatObjects = static::fetchFlatObjects($db); + } + + return self::$flatObjects; + } + + protected static function raiseLimits() + { + // Raise limits. TODO: do this in a failsafe way, and only if necessary + // Note: IcingaConfig also raises the limit for generation, **but** we need the higher limit for preview. + if ((string) ini_get('memory_limit') !== '-1') { + ini_set('memory_limit', '1024M'); + } + } + + protected static function fetchFlatObjects(Db $db) + { + return static::fetchFlatObjectsByType($db, static::getType()); + } + + protected static function fetchFlatObjectsByType(Db $db, $type) + { + self::raiseLimits(); + + Benchmark::measure("ObjectApplyMatches: prefetching $type"); + PrefetchCache::initialize($db); + /** @var IcingaObject $class */ + $class = IcingaObject::classByType($type); + $all = $class::prefetchAll($db); + Benchmark::measure("ObjectApplyMatches: related objects for $type"); + $class::prefetchAllRelationsByType($type, $db); + Benchmark::measure("ObjectApplyMatches: preparing flat $type objects"); + + $objects = array(); + foreach ($all as $object) { + if ($object->isTemplate()) { + continue; + } + + $flat = $object->toPlainObject(true, false); + static::flattenVars($object); + $objects[$object->getObjectName()] = $flat; + } + Benchmark::measure("ObjectApplyMatches: $type cache ready"); + + return $objects; + } + + protected static function fixFilterColumns(Filter $filter) + { + if ($filter->isExpression()) { + static::fixFilterExpressionColumn($filter); + } else { + foreach ($filter->filters() as $sub) { + static::fixFilterColumns($sub); + } + } + } + + protected static function fixFilterExpressionColumn(FilterExpression $filter) + { + if (static::columnIsJson($filter)) { + $column = $filter->getExpression(); + $filter->setExpression($filter->getColumn()); + $filter->setColumn($column); + } + + /** @var FilterExpression $filter */ + $col = $filter->getColumn(); + $type = static::$type; + + if (substr($col, 0, strlen($type) + 1) === "${type}.") { + $filter->setColumn($col = substr($col, 5)); + } + + if (array_key_exists($col, self::$columnMap)) { + $filter->setColumn(self::$columnMap[$col]); + } + + $filter->setExpression(json_decode($filter->getExpression())); + } + + protected static function columnIsJson(FilterExpression $filter) + { + $col = $filter->getColumn(); + return strlen($col) && $col[0] === '"'; + } + + /** + * Helper, flattens all vars of a given object + * + * The object itself will be modified, and the 'vars' property will be + * replaced with corresponding 'vars.whatever' properties + * + * @param $object + * @param string $key + */ + protected static function flattenVars(stdClass $object, $key = 'vars') + { + if (property_exists($object, 'vars')) { + foreach ($object->vars as $k => $v) { + if (is_object($v)) { + static::flattenVars($v, $k); + } + $object->{$key . '.' . $k} = $v; + } + unset($object->vars); + } + } + + protected function __construct(IcingaObject $object) + { + $this->object = $object; + $this->flatObject = $object->toPlainObject(true, false); + static::flattenVars($this->flatObject); + } +}