diff --git a/library/Director/Objects/HostApplyMatches.php b/library/Director/Objects/HostApplyMatches.php new file mode 100644 index 00000000..9c90d56c --- /dev/null +++ b/library/Director/Objects/HostApplyMatches.php @@ -0,0 +1,109 @@ + 'object_name' + ); + + /** + * @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 fetchFlatObjects(Db $db) + { + Benchmark::measure('HostApplyMatches: prefetching'); + PrefetchCache::initialize($db); + $all = IcingaHost::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); + static::flattenVars($object); + $objects[$host->getObjectName()] = $object; + } + Benchmark::measure('HostApplyMatches: cache ready'); + + return $objects; + } + + protected static function fixFilterColumns(Filter $filter) + { + if ($filter->isExpression()) { + /** @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())); + } else { + foreach ($filter->filters() as $sub) { + static::fixFilterColumns($sub); + } + } + } + + 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() + { + } +} \ No newline at end of file diff --git a/library/Director/Objects/IcingaService.php b/library/Director/Objects/IcingaService.php index 64c5f228..f86003e0 100644 --- a/library/Director/Objects/IcingaService.php +++ b/library/Director/Objects/IcingaService.php @@ -2,6 +2,7 @@ namespace Icinga\Module\Director\Objects; +use Icinga\Data\Filter\Filter; use Icinga\Exception\ProgrammingError; use Icinga\Module\Director\Db; use Icinga\Module\Director\Data\PropertiesFilter; @@ -174,8 +175,47 @@ class IcingaService extends IcingaObject return $this->renderRelationProperty('host', $this->host_id, 'host_name'); } + public function toLegacyConfigString() + { + if ($this->get('assign_filter')) { + return $this->renderLegacyResolvedAssignFilter(); + } + + $str = parent::toLegacyConfigString(); + + if (! $this->isDisabled() && $this->host_id && $this->getRelated('host')->isDisabled()) { + return + "# --- This services host has been disabled ---\n" + . preg_replace('~^~m', '# ', trim($str)) + . "\n\n"; + } else { + return $str; + } + } + + protected function renderLegacyResolvedAssignFilter() + { + $str = ''; + $hosts = HostApplyMatches::forFilter( + Filter::fromQueryString($this->get('assign_filter')), + $this->getConnection() + ); + $this->object_type = 'object'; + $this->assign_filter = null; + + foreach ($hosts as $hostname) { + $this->host = $hostname; + $str .= $this->toLegacyConfigString(); + } + + return $str; + } + public function toConfigString() { + if ($this->get('service_set_id')) { + return ''; + } $str = parent::toConfigString(); if (! $this->isDisabled() && $this->host_id && $this->getRelated('host')->isDisabled()) { @@ -282,6 +322,11 @@ class IcingaService extends IcingaObject return ''; } + public function renderService_set() + { + return ''; + } + public function renderUse_var_overrides() { return ''; diff --git a/library/Director/Objects/IcingaServiceSet.php b/library/Director/Objects/IcingaServiceSet.php index 4e996794..1f115a20 100644 --- a/library/Director/Objects/IcingaServiceSet.php +++ b/library/Director/Objects/IcingaServiceSet.php @@ -2,6 +2,7 @@ namespace Icinga\Module\Director\Objects; +use Icinga\Data\Filter\Filter; use Icinga\Exception\ProgrammingError; use Icinga\Module\Director\IcingaConfig\IcingaConfig; use Icinga\Module\Director\IcingaConfig\IcingaConfigHelper as c; @@ -45,11 +46,11 @@ class IcingaServiceSet extends IcingaObject } /** - * @return IcingaService + * @return IcingaService[] */ public function getServiceObjects() { - if ($this->host_id) { + if ($this->get('host_id')) { $imports = $this->imports()->getObjects(); if (empty($imports)) { return array(); @@ -62,7 +63,7 @@ class IcingaServiceSet extends IcingaObject protected function getServiceObjectsForSet(IcingaServiceSet $set) { - if ($set->id === null) { + if ($set->get('id') === null) { return array(); } @@ -70,7 +71,7 @@ class IcingaServiceSet extends IcingaObject $db = $this->getDb(); $ids = $db->fetchCol( $db->select()->from('icinga_service', 'id') - ->where('service_set_id = ?', $set->id) + ->where('service_set_id = ?', $set->get('id')) ); $services = array(); @@ -80,7 +81,7 @@ class IcingaServiceSet extends IcingaObject 'object_type' => 'template' ), $connection); - $services[$service->object_name] = $service; + $services[$service->getObjectName()] = $service; } return $services; @@ -88,12 +89,13 @@ class IcingaServiceSet extends IcingaObject public function renderToConfig(IcingaConfig $config) { - if ($this->assign_filter === null && $this->isTemplate()) { + if ($this->get('assign_filter') === null && $this->isTemplate()) { return; } if ($config->isLegacy()) { - return $this->renderToLegacyConfig($config); + $this->renderToLegacyConfig($config); + return; } $file = $this->getConfigFileWithHeader($config); @@ -103,19 +105,18 @@ class IcingaServiceSet extends IcingaObject // eventually clone them beforehand to not get into trouble with caches // figure out whether we might need a zone property foreach ($this->getServiceObjects() as $service) { - // TODO: make them REAL applies - if ($this->assign_filter) { - $service->object_type = 'apply'; - $service->assign_filter = $this->assign_filter; + if ($filter = $this->get('assign_filter')) { + $service->set('object_type', 'apply'); + $service->set('assign_filter', $filter); + } elseif ($hostId = $this->get('host_id')) { + $service->set('object_type', 'object'); + $service->host_id = $this->host_id; } else { - $service->object_type = $this->object_type; - if ($this->isApplyRule()) { - $service->assign_filter = $this->assign_filter; - } + // Service set template without assign filter or host + continue; } - $service->vars = $this->vars; - $service->host_id = $this->host_id; + $this->copyVarsToService($service); $file->addObject($service); } } @@ -141,20 +142,26 @@ class IcingaServiceSet extends IcingaObject return sprintf($comment, $this->object_name); } - public function renderToLegacyConfig(IcingaConfig $config) + protected function copyVarsToService(IcingaService $service) { - if ($this->isTemplate()) { - return; + $serviceVars = $service->vars(); + + foreach ($this->vars() as $k => $var) { + $serviceVars->$k = $var; } - if ($this->isApplyRule()) { - // Not yet + return $this; + } + + public function renderToLegacyConfig(IcingaConfig $config) + { + if ($this->assign_filter === null && $this->isTemplate()) { return; } // evaluate my assign rules once, get related hosts // Loop over all services belonging to this set - // generate every service with host_name host1,host2... + // generate every service with host_name host1,host2... -> not yet. And Zones? $file = $config->configFile( // TODO: zones.d? @@ -162,21 +169,39 @@ class IcingaServiceSet extends IcingaObject ); $file->prepend($this->getConfigHeaderComment($config)); + $conn = $this->getConnection(); - foreach ($this->getServiceObjects() as $service) { - $service->object_type = 'object'; - $service->host_id = $this->host_id; - $service->vars = $this->vars; - $file->addLegacyObject($service); + // Delegating this to the service would look, but this way it's faster + if ($filter = $this->get('assign_filter')) { + $filter = Filter::fromQueryString($filter); + $hosts = HostApplyMatches::forFilter($filter, $conn); + foreach ($this->getServiceObjects() as $service) { + $service->set('object_type', 'object'); + $this->copyVarsToService($service); + + foreach ($hosts as $hostname) { + $service->host = $hostname; + $file->addLegacyObject($service); + } + } + } else { + foreach ($this->getServiceObjects() as $service) { + $service->set('object_type', 'object'); + $service->set('host_id', $this->get('host_id')); + foreach ($this->vars() as $k => $var) { + $service->$k = $var; + } + $file->addLegacyObject($service); + } } } public function getRenderingZone(IcingaConfig $config = null) { - if ($this->host_id === null) { + if ($this->get('host_id') === null) { return $this->connection->getDefaultGlobalZoneName(); } else { - $host = $this->getRelatedObject('host', $this->host_id); + $host = $this->getRelatedObject('host', $this->get('host_id')); return $host->getRenderingZone($config); } return $zone;