mirror of
https://github.com/Icinga/icingaweb2-module-director.git
synced 2025-09-25 10:57:41 +02:00
Check for existence of custom variable in the object before storing it
This commit is contained in:
parent
6a7194475f
commit
d3d4bfd0bd
@ -3058,6 +3058,11 @@ abstract class IcingaObject extends DbObject implements IcingaConfigRenderer
|
||||
return $this->templateTree()->listAncestorIdsFor($this);
|
||||
}
|
||||
|
||||
public function listAncestorUuIds()
|
||||
{
|
||||
return $this->templateTree()->listAncestorUuIdsFor($this);
|
||||
}
|
||||
|
||||
protected function templateTree()
|
||||
{
|
||||
return $this->templates()->tree();
|
||||
|
@ -17,6 +17,8 @@ class TemplateTree
|
||||
|
||||
protected $parents;
|
||||
|
||||
protected $parentsUuids;
|
||||
|
||||
protected $children;
|
||||
|
||||
protected $rootNodes;
|
||||
@ -29,6 +31,8 @@ class TemplateTree
|
||||
|
||||
protected $names;
|
||||
|
||||
protected $uuids;
|
||||
|
||||
protected $templateNameToId;
|
||||
|
||||
public function __construct($type, Db $connection)
|
||||
@ -111,6 +115,11 @@ class TemplateTree
|
||||
return array_keys($this->getAncestorsFor($object));
|
||||
}
|
||||
|
||||
public function listAncestorUuIdsFor(IcingaObject $object)
|
||||
{
|
||||
return $this->getAncestorsUuidsFor($object);
|
||||
}
|
||||
|
||||
public function listChildIdsFor(IcingaObject $object)
|
||||
{
|
||||
return array_keys($this->getChildrenFor($object));
|
||||
@ -148,6 +157,19 @@ class TemplateTree
|
||||
}
|
||||
}
|
||||
|
||||
public function getAncestorsUuidsFor(IcingaObject $object)
|
||||
{
|
||||
if (
|
||||
$object->hasBeenModified()
|
||||
&& $object->gotImports()
|
||||
&& $object->imports()->hasBeenModified()
|
||||
) {
|
||||
return $this->getAncestorsUuidsForUnstoredObject($object);
|
||||
} else {
|
||||
return $this->getParentsUuidsById($object->getProperty('id'));
|
||||
}
|
||||
}
|
||||
|
||||
protected function getAncestorsForUnstoredObject(IcingaObject $object)
|
||||
{
|
||||
$this->requireTree();
|
||||
@ -183,6 +205,36 @@ class TemplateTree
|
||||
return $ancestors;
|
||||
}
|
||||
|
||||
protected function getAncestorsUuidsForUnstoredObject(IcingaObject $object)
|
||||
{
|
||||
$this->requireTree();
|
||||
$ancestors = [];
|
||||
foreach ($object->imports() as $import) {
|
||||
$name = $import->get('uuid');
|
||||
if ($import->hasBeenLoadedFromDb()) {
|
||||
$pid = (int) $import->get('id');
|
||||
} else {
|
||||
if (! array_key_exists($name, $this->templateNameToId)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$pid = $this->templateNameToId[$name];
|
||||
}
|
||||
|
||||
$this->getAncestorsUuidsById($pid, $ancestors);
|
||||
|
||||
$uuid = $import->get('uuid');
|
||||
// Hint: inheritance order matters
|
||||
if (false !== ($key = array_search($uuid, $ancestors))) {
|
||||
unset($ancestors[$key]);
|
||||
}
|
||||
|
||||
$ancestors[$pid] = $uuid;
|
||||
}
|
||||
|
||||
return $ancestors;
|
||||
}
|
||||
|
||||
protected function requireObjectMaps()
|
||||
{
|
||||
if ($this->objectMaps === null) {
|
||||
@ -211,6 +263,27 @@ class TemplateTree
|
||||
}
|
||||
}
|
||||
|
||||
public function getParentsUuidsById($id)
|
||||
{
|
||||
$this->requireTree();
|
||||
|
||||
if (array_key_exists($id, $this->parentsUuids)) {
|
||||
return $this->parentsUuids[$id];
|
||||
}
|
||||
|
||||
$this->requireObjectMaps();
|
||||
if (array_key_exists($id, $this->objectMaps)) {
|
||||
$parentsUuids = [];
|
||||
foreach ($this->objectMaps[$id] as $pid) {
|
||||
$parentsUuids[$pid] = $this->uuids[$pid];
|
||||
}
|
||||
|
||||
return $parentsUuids;
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $id
|
||||
* @param $list
|
||||
@ -287,6 +360,38 @@ class TemplateTree
|
||||
return $ancestors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the ancestorUuids for the given object ID
|
||||
*
|
||||
* @param $id
|
||||
* @param array $ancestors
|
||||
* @param array $path
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getAncestorsUuidsById($id, &$ancestors = [], $path = [])
|
||||
{
|
||||
$path[$id] = true;
|
||||
foreach ($this->getParentsUuidsById($id) as $pid => $uuid) {
|
||||
$this->assertNotInList($pid, $path);
|
||||
$path[$pid] = true;
|
||||
|
||||
$this->getAncestorsUuidsById($pid, $ancestors, $path);
|
||||
unset($path[$pid]);
|
||||
|
||||
// Hint: inheritance order matters
|
||||
if (false !== ($key = array_search($uuid, $ancestors))) {
|
||||
unset($ancestors[$key]);
|
||||
}
|
||||
|
||||
$ancestors[$pid] = $uuid;
|
||||
}
|
||||
|
||||
unset($path[$id]);
|
||||
|
||||
return $ancestors;
|
||||
}
|
||||
|
||||
public function getChildrenFor(IcingaObject $object)
|
||||
{
|
||||
// can not use hasBeenLoadedFromDb() when in onStore()
|
||||
@ -386,6 +491,8 @@ class TemplateTree
|
||||
$rootNodes = [];
|
||||
$children = [];
|
||||
$names = [];
|
||||
$parentsUuids = [];
|
||||
$uuids = [];
|
||||
foreach ($templates as $row) {
|
||||
$id = (int) $row->id;
|
||||
$pid = (int) $row->parent_id;
|
||||
@ -403,7 +510,9 @@ class TemplateTree
|
||||
}
|
||||
|
||||
$names[$pid] = $row->parent_name;
|
||||
$uuids[$pid] = $row->parent_uuid;
|
||||
$parents[$id][$pid] = $row->parent_name;
|
||||
$parentsUuids[$id][$pid] = $row->parent_uuid;
|
||||
|
||||
if (! array_key_exists($pid, $children)) {
|
||||
$children[$pid] = [];
|
||||
@ -413,9 +522,11 @@ class TemplateTree
|
||||
}
|
||||
|
||||
$this->parents = $parents;
|
||||
$this->parentsUuids = $parentsUuids;
|
||||
$this->children = $children;
|
||||
$this->rootNodes = $rootNodes;
|
||||
$this->names = $names;
|
||||
$this->uuids = $uuids;
|
||||
$this->templateNameToId = array_flip($names);
|
||||
Benchmark::measure(sprintf('"%s" Template Tree ready', $this->type));
|
||||
}
|
||||
@ -458,6 +569,7 @@ class TemplateTree
|
||||
'object_type' => 'o.object_type',
|
||||
'parent_id' => 'p.id',
|
||||
'parent_name' => 'p.object_name',
|
||||
'parent_uuid' => 'p.uuid'
|
||||
]
|
||||
)->joinLeft(
|
||||
['i' => $table . '_inheritance'],
|
||||
|
@ -14,6 +14,7 @@ use Icinga\Module\Director\Objects\IcingaHost;
|
||||
use Icinga\Module\Director\Objects\IcingaObject;
|
||||
use Icinga\Module\Director\Resolver\OverrideHelper;
|
||||
use InvalidArgumentException;
|
||||
use PDO;
|
||||
use RuntimeException;
|
||||
|
||||
class IcingaObjectHandler extends RequestHandler
|
||||
@ -96,6 +97,45 @@ class IcingaObjectHandler extends RequestHandler
|
||||
}
|
||||
}
|
||||
|
||||
public function getCustomProperties(IcingaObject $object): array
|
||||
{
|
||||
if ($object->get('uuid') === null) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$type = $object->getShortTableName();
|
||||
$db = $object->getConnection();
|
||||
$uuids = $object->listAncestorUuIds();
|
||||
$query = $db->getDbAdapter()
|
||||
->select()
|
||||
->from(
|
||||
['dp' => 'director_property'],
|
||||
[
|
||||
'key_name' => 'dp.key_name',
|
||||
'uuid' => 'dp.uuid',
|
||||
'value_type' => 'dp.value_type',
|
||||
'label' => 'dp.label',
|
||||
'instantiable' => 'dp.instantiable',
|
||||
'required' => 'iop.required',
|
||||
'children' => 'COUNT(cdp.uuid)'
|
||||
]
|
||||
)
|
||||
->join(['iop' => "icinga_$type" . '_property'], 'dp.uuid = iop.property_uuid', [])
|
||||
->joinLeft(['cdp' => 'director_property'], 'cdp.parent_uuid = dp.uuid', [])
|
||||
->where('iop.' . $type . '_uuid IN (?)', $uuids)
|
||||
->group(['dp.uuid', 'dp.key_name', 'dp.value_type', 'dp.label', 'dp.instantiable', 'iop.required'])
|
||||
->order('children')
|
||||
->order('instantiable')
|
||||
->order('key_name');
|
||||
|
||||
$result = [];
|
||||
foreach ($db->getDbAdapter()->fetchAll($query, fetchMode: PDO::FETCH_ASSOC) as $row) {
|
||||
$result[$row['key_name']] = $row;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
protected function handleApiRequest()
|
||||
{
|
||||
$request = $this->request;
|
||||
@ -129,10 +169,30 @@ class IcingaObjectHandler extends RequestHandler
|
||||
$params = $this->request->getUrl()->getParams();
|
||||
$allowsOverrides = $params->get('allowOverrides');
|
||||
$type = $this->getType();
|
||||
if ($object = $this->loadOptionalObject()) {
|
||||
if ($this->request->getActionName() === 'variables') {
|
||||
$data = ['vars' => $data];
|
||||
$object = $this->loadOptionalObject();
|
||||
$customProperties = $this->getCustomProperties($object);
|
||||
$overridenCustomVars = $this->getCustomVarsFromData($data);
|
||||
if (! empty($overridenCustomVars)) {
|
||||
$diff = array_diff(array_keys($data), array_keys($customProperties));
|
||||
if (! empty($diff)) {
|
||||
throw new Exception(sprintf(
|
||||
"The custom properties (%s) are not supported by this object",
|
||||
implode(", ", $diff)
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
if ($object) {
|
||||
if ($this->request->getActionName() === 'variables') {
|
||||
$diff = array_diff(array_keys($data), array_keys($customProperties));
|
||||
if (! empty($diff)) {
|
||||
throw new Exception(sprintf(
|
||||
"The custom properties %s are not supported by this object",
|
||||
implode(", ", $diff)
|
||||
));
|
||||
}
|
||||
|
||||
$data = ['vars' => $data];
|
||||
$object->setProperties($data);
|
||||
} elseif ($request->getMethod() === 'POST') {
|
||||
$object->setProperties($data);
|
||||
@ -197,4 +257,21 @@ class IcingaObjectHandler extends RequestHandler
|
||||
throw new RuntimeException('Found a single service, which should have been found (and dealt with) before');
|
||||
}
|
||||
}
|
||||
|
||||
private function getCustomVarsFromData(array $data): array
|
||||
{
|
||||
$customVars = [];
|
||||
|
||||
foreach ($data as $key => $value) {
|
||||
if ($key === 'vars') {
|
||||
$customVars = $value;
|
||||
}
|
||||
|
||||
if (substr($key, 0, 5) === 'vars.') {
|
||||
$customVars[substr($key, 5)] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
return $customVars;
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user