diff options
Diffstat (limited to 'library/Director/CustomVariable/CustomVariables.php')
-rw-r--r-- | library/Director/CustomVariable/CustomVariables.php | 488 |
1 files changed, 488 insertions, 0 deletions
diff --git a/library/Director/CustomVariable/CustomVariables.php b/library/Director/CustomVariable/CustomVariables.php new file mode 100644 index 0000000..cdcc4bd --- /dev/null +++ b/library/Director/CustomVariable/CustomVariables.php @@ -0,0 +1,488 @@ +<?php + +namespace Icinga\Module\Director\CustomVariable; + +use Icinga\Module\Director\Db; +use Icinga\Module\Director\IcingaConfig\IcingaConfigHelper as c; +use Icinga\Module\Director\IcingaConfig\IcingaLegacyConfigHelper as c1; +use Icinga\Module\Director\IcingaConfig\IcingaConfigRenderer; +use Icinga\Module\Director\Objects\IcingaObject; +use Countable; +use Exception; +use Iterator; + +class CustomVariables implements Iterator, Countable, IcingaConfigRenderer +{ + /** @var CustomVariable[] */ + protected $storedVars = array(); + + /** @var CustomVariable[] */ + protected $vars = array(); + + protected $modified = false; + + private $position = 0; + + private $overrideKeyName; + + protected $idx = array(); + + protected static $allTables = array( + 'icinga_command_var', + 'icinga_host_var', + 'icinga_notification_var', + 'icinga_service_set_var', + 'icinga_service_var', + 'icinga_user_var', + ); + + public static function countAll($varname, Db $connection) + { + $db = $connection->getDbAdapter(); + $parts = array(); + $where = $db->quoteInto('varname = ?', $varname); + foreach (static::$allTables as $table) { + $parts[] = "SELECT COUNT(*) as cnt FROM $table WHERE $where"; + } + + $sub = implode(' UNION ALL ', $parts); + $query = "SELECT SUM(sub.cnt) AS cnt FROM ($sub) sub"; + + return (int) $db->fetchOne($query); + } + + public static function deleteAll($varname, Db $connection) + { + $db = $connection->getDbAdapter(); + $where = $db->quoteInto('varname = ?', $varname); + foreach (static::$allTables as $table) { + $db->delete($table, $where); + } + } + + public static function renameAll($oldname, $newname, Db $connection) + { + $db = $connection->getDbAdapter(); + $where = $db->quoteInto('varname = ?', $oldname); + foreach (static::$allTables as $table) { + $db->update($table, ['varname' => $newname], $where); + } + } + + #[\ReturnTypeWillChange] + public function count() + { + $count = 0; + foreach ($this->vars as $var) { + if (! $var->hasBeenDeleted()) { + $count++; + } + } + + return $count; + } + + #[\ReturnTypeWillChange] + public function rewind() + { + $this->position = 0; + } + + #[\ReturnTypeWillChange] + public function current() + { + if (! $this->valid()) { + return null; + } + + return $this->vars[$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); + } + + /** + * Generic setter + * + * @param string $key + * @param mixed $value + * + * @return self + */ + public function set($key, $value) + { + $key = (string) $key; + + if ($value instanceof CustomVariable) { + $value = clone($value); + } else { + if ($value === null) { + $this->__unset($key); + return $this; + } + $value = CustomVariable::create($key, $value); + } + + // Hint: isset($this->$key) wouldn't conflict with protected properties + if ($this->__isset($key)) { + if ($value->equals($this->get($key))) { + return $this; + } else { + if (get_class($this->vars[$key]) === get_class($value)) { + $this->vars[$key]->setValue($value->getValue())->setModified(); + } else { + $this->vars[$key] = $value->setLoadedFromDb()->setModified(); + } + } + } else { + $this->vars[$key] = $value->setModified(); + } + + $this->modified = true; + $this->refreshIndex(); + + return $this; + } + + protected function refreshIndex() + { + $this->idx = array(); + ksort($this->vars); + foreach ($this->vars as $name => $var) { + if (! $var->hasBeenDeleted()) { + $this->idx[] = $name; + } + } + } + + public static function loadForStoredObject(IcingaObject $object) + { + $db = $object->getDb(); + + $query = $db->select()->from( + array('v' => $object->getVarsTableName()), + array( + 'v.varname', + 'v.varvalue', + 'v.format', + ) + )->where(sprintf('v.%s = ?', $object->getVarsIdColumn()), $object->get('id')); + + $vars = new CustomVariables; + foreach ($db->fetchAll($query) as $row) { + $vars->vars[$row->varname] = CustomVariable::fromDbRow($row); + } + $vars->refreshIndex(); + $vars->setBeingLoadedFromDb(); + return $vars; + } + + public static function forStoredRows($rows) + { + $vars = new CustomVariables; + foreach ($rows as $row) { + $vars->vars[$row->varname] = CustomVariable::fromDbRow($row); + } + $vars->refreshIndex(); + $vars->setBeingLoadedFromDb(); + + return $vars; + } + + public function storeToDb(IcingaObject $object) + { + $db = $object->getDb(); + $table = $object->getVarsTableName(); + $foreignColumn = $object->getVarsIdColumn(); + $foreignId = $object->get('id'); + + + foreach ($this->vars as $var) { + if ($var->isNew()) { + $db->insert( + $table, + array( + $foreignColumn => $foreignId, + 'varname' => $var->getKey(), + 'varvalue' => $var->getDbValue(), + 'format' => $var->getDbFormat() + ) + ); + $var->setLoadedFromDb(); + continue; + } + + $where = $db->quoteInto(sprintf('%s = ?', $foreignColumn), (int) $foreignId) + . $db->quoteInto(' AND varname = ?', $var->getKey()); + + if ($var->hasBeenDeleted()) { + $db->delete($table, $where); + } elseif ($var->hasBeenModified()) { + $db->update( + $table, + array( + 'varvalue' => $var->getDbValue(), + 'format' => $var->getDbFormat() + ), + $where + ); + } + } + + $this->setBeingLoadedFromDb(); + } + + public function get($key) + { + if (array_key_exists($key, $this->vars)) { + return $this->vars[$key]; + } + + return null; + } + + public function hasBeenModified() + { + if ($this->modified) { + return true; + } + + foreach ($this->vars as $var) { + if ($var->hasBeenModified()) { + return true; + } + } + + return false; + } + + public function setBeingLoadedFromDb() + { + $this->modified = false; + $this->storedVars = array(); + foreach ($this->vars as $key => $var) { + $this->storedVars[$key] = clone($var); + $var->setUnmodified(); + $var->setLoadedFromDb(); + } + + return $this; + } + + public function restoreStoredVar($key) + { + if (array_key_exists($key, $this->storedVars)) { + $this->vars[$key] = clone($this->storedVars[$key]); + $this->vars[$key]->setUnmodified(); + $this->recheckForModifications(); + $this->refreshIndex(); + } elseif (array_key_exists($key, $this->vars)) { + unset($this->vars[$key]); + $this->recheckForModifications(); + $this->refreshIndex(); + } + } + + protected function recheckForModifications() + { + $this->modified = false; + foreach ($this->vars as $var) { + if ($var->hasBeenModified()) { + $this->modified = true; + + return; + } + } + } + + public function getOriginalVars() + { + return $this->storedVars; + } + + public function flatten() + { + $flat = array(); + foreach ($this->vars as $key => $var) { + $var->flatten($flat, $key); + } + + return $flat; + } + + public function checksum() + { + $sums = array(); + foreach ($this->vars as $key => $var) { + $sums[] = $key . '=' . $var->checksum(); + } + + return sha1(implode('|', $sums), true); + } + + public function setOverrideKeyName($name) + { + $this->overrideKeyName = $name; + return $this; + } + + public function toConfigString($renderExpressions = false) + { + $out = ''; + + foreach ($this as $key => $var) { + // TODO: ctype_alnum + underscore? + $out .= $this->renderSingleVar($key, $var, $renderExpressions); + } + + return $out; + } + + public function toLegacyConfigString() + { + $out = ''; + + ksort($this->vars); + foreach ($this->vars as $key => $var) { + // TODO: ctype_alnum + underscore? + // vars with ARGn will be handled by IcingaObject::renderLegacyCheck_command + if (substr($key, 0, 3) == 'ARG') { + continue; + } + + switch ($type = $var->getType()) { + case 'String': + case 'Number': + # TODO: Make Prefetchable + $out .= c1::renderKeyValue( + '_' . $key, + $var->toLegacyConfigString() + ); + break; + default: + $out .= c1::renderKeyValue( + '# _' . $key, + sprintf('(unsupported: %s)', $type) + ); + } + } + + if ($out !== '') { + $out = "\n".$out; + } + + return $out; + } + + /** + * @param string $key + * @param CustomVariable $var + * @param bool $renderExpressions + * + * @return string + */ + protected function renderSingleVar($key, $var, $renderExpressions = false) + { + if ($key === $this->overrideKeyName) { + return c::renderKeyOperatorValue( + $this->renderKeyName($key), + '+=', + $var->toConfigStringPrefetchable($renderExpressions) + ); + } else { + return c::renderKeyValue( + $this->renderKeyName($key), + $var->toConfigStringPrefetchable($renderExpressions) + ); + } + } + + protected function renderKeyName($key) + { + if (preg_match('/^[a-z][a-z0-9_]*$/i', $key)) { + return 'vars.' . c::escapeIfReserved($key); + } else { + return 'vars[' . c::renderString($key) . ']'; + } + } + + public function __get($key) + { + return $this->get($key); + } + + /** + * Magic setter + * + * @param string $key Key + * @param mixed $val Value + * + * @return void + */ + public function __set($key, $val) + { + $this->set($key, $val); + } + + /** + * Magic isset check + * + * @param string $key + * + * @return boolean + */ + public function __isset($key) + { + return array_key_exists($key, $this->vars); + } + + /** + * Magic unsetter + * + * @param string $key + * + * @return void + */ + public function __unset($key) + { + if (! array_key_exists($key, $this->vars)) { + return; + } + + $this->vars[$key]->delete(); + $this->modified = true; + + $this->refreshIndex(); + } + + public function __toString() + { + try { + return $this->toConfigString(); + } catch (Exception $e) { + trigger_error($e); + $previousHandler = set_exception_handler( + function () { + } + ); + restore_error_handler(); + call_user_func($previousHandler, $e); + die(); + } + } +} |