489 lines
12 KiB
PHP
489 lines
12 KiB
PHP
<?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();
|
|
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 = '';
|
|
|
|
ksort($this->vars);
|
|
foreach ($this->vars 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();
|
|
}
|
|
}
|
|
}
|