summaryrefslogtreecommitdiffstats
path: root/library/Director/Objects/IcingaObjectImports.php
diff options
context:
space:
mode:
Diffstat (limited to 'library/Director/Objects/IcingaObjectImports.php')
-rw-r--r--library/Director/Objects/IcingaObjectImports.php439
1 files changed, 439 insertions, 0 deletions
diff --git a/library/Director/Objects/IcingaObjectImports.php b/library/Director/Objects/IcingaObjectImports.php
new file mode 100644
index 0000000..384fa1c
--- /dev/null
+++ b/library/Director/Objects/IcingaObjectImports.php
@@ -0,0 +1,439 @@
+<?php
+
+namespace Icinga\Module\Director\Objects;
+
+use Countable;
+use Exception;
+use Icinga\Exception\NotFoundError;
+use Iterator;
+use Icinga\Module\Director\IcingaConfig\IcingaConfigHelper as c;
+use Icinga\Module\Director\IcingaConfig\IcingaConfigRenderer;
+use Icinga\Module\Director\IcingaConfig\IcingaLegacyConfigHelper as c1;
+use Icinga\Module\Director\Repository\IcingaTemplateRepository;
+use RuntimeException;
+
+class IcingaObjectImports implements Iterator, Countable, IcingaConfigRenderer
+{
+ protected $storedNames = [];
+
+ /** @var array A list of our imports, key and value are the import name */
+ protected $imports = [];
+
+ /** @var IcingaObject[] A list of all objects we have seen, referred by name */
+ protected $objects = [];
+
+ protected $modified = false;
+
+ /** @var IcingaObject The parent object */
+ protected $object;
+
+ private $position = 0;
+
+ protected $idx = [];
+
+ public function __construct(IcingaObject $object)
+ {
+ $this->object = $object;
+ }
+
+ #[\ReturnTypeWillChange]
+ public function count()
+ {
+ return count($this->imports);
+ }
+
+ #[\ReturnTypeWillChange]
+ public function rewind()
+ {
+ $this->position = 0;
+ }
+
+ public function setModified()
+ {
+ $this->modified = true;
+ return $this;
+ }
+
+ public function hasBeenModified()
+ {
+ return $this->modified;
+ }
+
+ /**
+ * @return IcingaObject|null
+ * @throws \Icinga\Exception\NotFoundError
+ */
+ #[\ReturnTypeWillChange]
+ public function current()
+ {
+ if (! $this->valid()) {
+ return null;
+ }
+
+ return $this->getObject(
+ $this->imports[$this->idx[$this->position]]
+ );
+ }
+
+ #[\ReturnTypeWillChange]
+ public function key()
+ {
+ return $this->idx[$this->position];
+ }
+
+ #[\ReturnTypeWillChange]
+ public function next()
+ {
+ ++$this->position;
+ }
+
+ #[\ReturnTypeWillChange]
+ public function valid()
+ {
+ return array_key_exists($this->position, $this->idx);
+ }
+
+ /**
+ * @param $key
+ * @return IcingaObject|null
+ * @throws \Icinga\Exception\NotFoundError
+ */
+ public function get($key)
+ {
+ if (array_key_exists($key, $this->imports)) {
+ return $this->getObject($this->imports[$key]);
+ }
+
+ return null;
+ }
+
+ public function set($import)
+ {
+ if (empty($import)) {
+ if (empty($this->imports)) {
+ return $this;
+ } else {
+ return $this->clear();
+ }
+ }
+
+ if (! is_array($import)) {
+ $import = [$import];
+ }
+
+ $existing = $this->listImportNames();
+ $new = $this->listNamesForGivenImports($import);
+
+ if ($existing === $new) {
+ return $this;
+ }
+
+ $this->imports = [];
+ return $this->add($import);
+ }
+
+ protected function listNamesForGivenImports($imports)
+ {
+ $list = [];
+ $class = $this->getImportClass();
+
+ foreach ($imports as $i) {
+ if ($i instanceof $class) {
+ $list[] = $i->object_name;
+ } else {
+ $list[] = $i;
+ }
+ }
+
+ return $list;
+ }
+
+ /**
+ * Magic isset check
+ *
+ * @param string $import
+ *
+ * @return boolean
+ */
+ public function __isset($import)
+ {
+ return array_key_exists($import, $this->imports);
+ }
+
+ public function clear()
+ {
+ if ($this->imports === []) {
+ return $this;
+ }
+
+ $this->imports = [];
+ $this->modified = true;
+
+ return $this->refreshIndex();
+ }
+
+ public function remove($import)
+ {
+ if (array_key_exists($import, $this->imports)) {
+ unset($this->imports[$import]);
+ }
+
+ $this->modified = true;
+
+ return $this->refreshIndex();
+ }
+
+ protected function refreshIndex()
+ {
+ $this->idx = array_keys($this->imports);
+ // $this->object->templateResolver()->refreshObject($this->object);
+
+ return $this;
+ }
+
+ public function add($import)
+ {
+ $class = $this->getImportClass();
+
+ if (is_array($import)) {
+ foreach ($import as $i) {
+ // Gracefully ignore null members or empty strings
+ if (! $i instanceof $class && ($i === null || strlen($i) === 0)) {
+ continue;
+ }
+
+ $this->add($i);
+ }
+
+ return $this;
+ }
+
+ if ($import instanceof $class) {
+ $name = $import->object_name;
+ if (array_key_exists($name, $this->imports)) {
+ return $this;
+ }
+
+ $this->imports[$name] = $name;
+ $this->objects[$name] = $import;
+ } elseif (is_string($import)) {
+ if (array_key_exists($import, $this->imports)) {
+ return $this;
+ }
+
+ $this->imports[$import] = $import;
+ }
+
+ $this->modified = true;
+ $this->refreshIndex();
+
+ return $this;
+ }
+
+ /**
+ * @return IcingaObject[]
+ * @throws \Icinga\Exception\NotFoundError
+ */
+ public function getObjects()
+ {
+ $list = [];
+ foreach ($this->listImportNames() as $name) {
+ $name = (string) $name;
+ $list[$name] = $this->getObject($name);
+ }
+
+ return $list;
+ }
+
+ /**
+ * @param $name
+ * @return IcingaObject
+ * @throws \Icinga\Exception\NotFoundError
+ */
+ protected function getObject($name)
+ {
+ if (array_key_exists($name, $this->objects)) {
+ return $this->objects[$name];
+ }
+
+ $connection = $this->object->getConnection();
+ /** @var IcingaObject $class */
+ $class = $this->getImportClass();
+ try {
+ if (is_array($this->object->getKeyName())) {
+ // Services only
+ $import = $class::load([
+ 'object_name' => $name,
+ 'object_type' => 'template'
+ ], $connection);
+ } else {
+ $import = $class::load($name, $connection);
+ }
+ } catch (NotFoundError $e) {
+ throw new NotFoundError(sprintf(
+ 'Unable to load parent referenced from %s "%s", %s',
+ $this->object->getShortTableName(),
+ $this->object->getObjectName(),
+ lcfirst($e->getMessage())
+ ), $e->getCode(), $e);
+ }
+
+ return $this->objects[$import->getObjectName()] = $import;
+ }
+
+ protected function getImportTableName()
+ {
+ return $this->object->getTableName() . '_inheritance';
+ }
+
+ public function listImportNames()
+ {
+ return array_keys($this->imports);
+ }
+
+ public function listOriginalImportNames()
+ {
+ return $this->storedNames;
+ }
+
+ public function getType()
+ {
+ return $this->object->getShortTableName();
+ }
+
+ protected function loadFromDb()
+ {
+ // $resolver = $this->object->templateResolver();
+ // $this->objects = $resolver->fetchParents();
+ $this->objects = IcingaTemplateRepository::instanceByObject($this->object)
+ ->getTemplatesIndexedByNameFor($this->object);
+ if (empty($this->objects)) {
+ $this->imports = [];
+ } else {
+ $keys = array_keys($this->objects);
+ $this->imports = array_combine($keys, $keys);
+ }
+
+ $this->setBeingLoadedFromDb();
+ return $this;
+ }
+
+ /**
+ * @return bool
+ * @throws \Zend_Db_Adapter_Exception
+ * @throws \Icinga\Exception\NotFoundError
+ */
+ public function store()
+ {
+ if (! $this->hasBeenModified()) {
+ return true;
+ }
+
+ $objectId = $this->object->get('id');
+ if ($objectId === null) {
+ throw new RuntimeException(
+ 'Cannot store imports for unstored object with no ID'
+ );
+ } else {
+ $objectId = (int) $objectId;
+ }
+
+ $type = $this->getType();
+
+ $objectCol = $type . '_id';
+ $importCol = 'parent_' . $type . '_id';
+ $table = $this->getImportTableName();
+ $db = $this->object->getDb();
+
+ if ($this->object->hasBeenLoadedFromDb()) {
+ $db->delete(
+ $table,
+ $objectCol . ' = ' . $objectId
+ );
+ }
+
+ $weight = 1;
+ foreach ($this->getObjects() as $import) {
+ $db->insert($table, [
+ $objectCol => $objectId,
+ $importCol => $import->get('id'),
+ 'weight' => $weight++
+ ]);
+ }
+
+ $this->setBeingLoadedFromDb();
+
+ return true;
+ }
+
+ public function setBeingLoadedFromDb()
+ {
+ $this->storedNames = $this->listImportNames();
+ $this->modified = false;
+ }
+
+ protected function getImportClass()
+ {
+ return get_class($this->object);
+ }
+
+ public static function loadForStoredObject(IcingaObject $object)
+ {
+ $obj = new static($object);
+ return $obj->loadFromDb();
+ }
+
+ public function toConfigString()
+ {
+ $ret = '';
+
+ foreach ($this->listImportNames() as $name) {
+ $ret .= ' import ' . c::renderString($name) . "\n";
+ }
+
+ if ($ret !== '') {
+ $ret .= "\n";
+ }
+ return $ret;
+ }
+
+ public function toLegacyConfigString()
+ {
+ $ret = '';
+
+ foreach ($this->listImportNames() as $name) {
+ $ret .= c1::renderKeyValue('use', c1::renderString($name)) . "\n";
+ }
+
+ if ($ret !== '') {
+ $ret .= "\n";
+ }
+ return $ret;
+ }
+
+ public function __toString()
+ {
+ try {
+ return $this->toConfigString();
+ } catch (Exception $e) {
+ trigger_error($e);
+ $previousHandler = set_exception_handler(
+ function () {
+ }
+ );
+ restore_error_handler();
+ if ($previousHandler !== null) {
+ call_user_func($previousHandler, $e);
+ die();
+ } else {
+ die($e->getMessage());
+ }
+ }
+ }
+
+ public function __destruct()
+ {
+ unset($this->object);
+ unset($this->objects);
+ }
+}