DbObjectStore: inject into DbObject for related...

...objects in a branch
This commit is contained in:
Thomas Gelf 2021-12-17 11:46:55 +01:00
parent 56d052a804
commit 376344257c
12 changed files with 218 additions and 14 deletions

View File

@ -5,6 +5,7 @@ namespace Icinga\Module\Director\Controllers;
use gipfl\Diff\HtmlRenderer\SideBySideDiff;
use gipfl\Diff\PhpDiff;
use gipfl\IcingaWeb2\Widget\NameValueTable;
use Icinga\Module\Director\Data\Db\DbObjectStore;
use Icinga\Module\Director\Data\Db\DbObjectTypeRegistry;
use Icinga\Module\Director\Db\Branch\BranchActivity;
use Icinga\Module\Director\IcingaConfig\IcingaConfig;
@ -19,6 +20,12 @@ class BranchController extends ActionController
{
use BranchHelper;
public function init()
{
parent::init();
IcingaObject::setDbObjectStore(new DbObjectStore($this->db(), $this->getBranch()));
}
protected function checkDirectorPermissions()
{
}

View File

@ -7,6 +7,7 @@ use Icinga\Module\Director\Data\Db\DbObjectStore;
use Icinga\Module\Director\Db\Branch\UuidLookup;
use Icinga\Module\Director\Forms\IcingaServiceForm;
use Icinga\Module\Director\Monitoring;
use Icinga\Module\Director\Objects\IcingaObject;
use Icinga\Module\Director\Web\Controller\ObjectController;
use Icinga\Module\Director\Objects\IcingaService;
use Icinga\Module\Director\Objects\IcingaHost;
@ -49,11 +50,13 @@ class ServiceController extends ObjectController
{
$host = $this->params->get('host', $this->params->get('host_id'));
if ($host === null && $this->object) {
if ($host = $this->object->get('host_id')) {
$host = (int) $host;
} else {
$host = $this->object->get('host');
// We reach this when accessing Service Template Fields
if (null === $host = $this->object->getUnresolvedRelated('host')) {
if ($host = $this->object->get('host_id')) {
$host = (int) $host;
} else {
$host = $this->object->get('host');
// We reach this when accessing Service Template Fields
}
}
}

View File

@ -153,7 +153,11 @@ class IcingaAddServiceForm extends DirectorObjectForm
public function onSuccess()
{
if ($this->host !== null) {
$this->object->set('host_id', $this->host->get('id'));
if ($id = $this->host->get('id')) {
$this->object->set('host_id', $this->host->get('id'));
} else {
$this->object->set('host', $this->host->getObjectName());
}
parent::onSuccess();
return;
}

View File

@ -5,6 +5,7 @@ namespace Icinga\Module\Director\Data\Db;
use Icinga\Exception\NotFoundError;
use Icinga\Module\Director\Data\InvalidDataException;
use Icinga\Module\Director\Db;
use Icinga\Module\Director\Db\Branch\UuidLookup;
use Icinga\Module\Director\Exception\DuplicateKeyException;
use InvalidArgumentException;
use LogicException;
@ -91,6 +92,9 @@ abstract class DbObject
protected static $prefetchStats = array();
/** @var ?DbObjectStore */
protected static $dbObjectStore;
/**
* Constructor is not accessible and should not be overridden
*/
@ -225,6 +229,11 @@ abstract class DbObject
return $this;
}
public static function setDbObjectStore(DbObjectStore $store)
{
self::$dbObjectStore = $store;
}
/**
* Getter
*
@ -992,6 +1001,12 @@ abstract class DbObject
public function createWhere()
{
if ($this->hasUuidColumn() && $this->properties[$this->uuidColumn] !== null) {
return $this->db->quoteInto(
sprintf('%s = ?', $this->getUuidColumn()),
$this->connection->quoteBinary($this->getUniqueId()->getBytes())
);
}
if ($id = $this->getAutoincId()) {
if ($originalId = $this->getOriginalProperty($this->autoincKeyName)) {
return $this->db->quoteInto(
@ -1220,8 +1235,15 @@ abstract class DbObject
return $prefetched;
}
/** @var DbObject $obj */
$obj = new static;
if (self::$dbObjectStore !== null && $obj->hasUuidColumn()) {
$table = $obj->getTableName();
assert($connection instanceof Db);
$uuid = UuidLookup::findUuidForKey($id, $table, $connection, self::$dbObjectStore->getBranch());
return self::$dbObjectStore->load($table, $uuid);
}
$obj->setConnection($connection)
->set($obj->autoincKeyName, $id)
->loadFromDb();
@ -1240,9 +1262,17 @@ abstract class DbObject
if ($prefetched = static::getPrefetched($id)) {
return $prefetched;
}
/** @var DbObject $obj */
$obj = new static;
if (self::$dbObjectStore !== null) {
$table = $obj->getTableName();
assert($connection instanceof Db);
$uuid = UuidLookup::findUuidForKey($id, $table, $connection, self::$dbObjectStore->getBranch());
return self::$dbObjectStore->load($table, $uuid);
}
$obj->setConnection($connection)->setKey($id)->loadFromDb();
return $obj;
@ -1341,6 +1371,14 @@ abstract class DbObject
/** @var DbObject $obj */
$obj = new static;
if (self::$dbObjectStore !== null) {
$table = $obj->getTableName();
assert($connection instanceof Db);
$uuid = UuidLookup::findUuidForKey($id, $table, $connection, self::$dbObjectStore->getBranch());
return self::$dbObjectStore->exists($table, $uuid);
}
$obj->setConnection($connection)->setKey($id);
return $obj->existsInDb();
}
@ -1376,6 +1414,13 @@ abstract class DbObject
{
$db = $connection->getDbAdapter();
$obj = new static;
if (self::$dbObjectStore !== null) {
$table = $obj->getTableName();
assert($connection instanceof Db);
return self::$dbObjectStore->load($table, $uuid);
}
$query = $db->select()
->from($obj->getTableName())
->where($obj->getUuidColumn() . ' = ?', $connection->quoteBinary($uuid->getBytes()));

View File

@ -6,6 +6,7 @@ use Icinga\Module\Director\Db;
use Icinga\Module\Director\Db\Branch\Branch;
use Icinga\Module\Director\Db\Branch\BranchActivity;
use Icinga\Module\Director\Db\Branch\BranchedObject;
use Ramsey\Uuid\UuidInterface;
/**
* Loader for Icinga/DbObjects
@ -28,6 +29,30 @@ class DbObjectStore
$this->branch = $branch;
}
/**
* @param $tableName
* @param UuidInterface $uuid
* @return DbObject|null
* @throws \Icinga\Exception\NotFoundError
*/
public function load($tableName, UuidInterface $uuid)
{
$branchedObject = BranchedObject::load($this->connection, $tableName, $uuid, $this->branch);
$object = $branchedObject->getBranchedDbObject($this->connection);
if ($object === null) {
return null;
}
$object->setBeingLoadedFromDb();
return $object;
}
public function exists($tableName, UuidInterface $uuid)
{
return BranchedObject::exists($this->connection, $tableName, $uuid, $this->branch->getUuid());
}
public function store(DbObject $object)
{
if ($this->branch && $this->branch->isBranch()) {
@ -61,4 +86,9 @@ class DbObjectStore
return $object->delete();
}
public function getBranch()
{
return $this->branch;
}
}

View File

@ -344,6 +344,23 @@ class BranchedObject
return $self;
}
public static function exists(
Db $connection,
$table,
UuidInterface $uuid,
UuidInterface $branchUuid = null
) {
if (static::optionalTableRowByUuid($connection, $table, $uuid)) {
return true;
}
if ($branchUuid && static::optionalBranchedTableRowByUuid($connection, $table, $uuid, $branchUuid)) {
return true;
}
return false;
}
/**
* @param Db $connection
* @param string $table

View File

@ -31,7 +31,7 @@ class UuidLookup
) {
$db = $connection->getDbAdapter();
$query = $db->select()->from('icinga_service', 'uuid')->where('object_type = ?', $objectType);
$query = self::addKeyToQuery($query, $key);
$query = self::addKeyToQuery($connection, $query, $key);
if ($host) {
$query->add('host_id = ?', $host->get('id'));
}
@ -43,7 +43,7 @@ class UuidLookup
if ($uuid === null && $branch->isBranch()) {
// TODO: use different tables?
$query = $db->select()->from('branched_icinga_service', 'uuid')->where('object_type = ?', $objectType);
$query = self::addKeyToQuery($query, $key);
$query = self::addKeyToQuery($connection, $query, $key);
if ($host) {
// TODO: uuid?
$query->add('host = ?', $host->getObjectName());
@ -60,7 +60,7 @@ class UuidLookup
public static function findUuidForKey($key, $table, Db $connection, Branch $branch)
{
$db = $connection->getDbAdapter();
$query = self::addKeyToQuery($db->select()->from($table, 'uuid'), $key);
$query = self::addKeyToQuery($connection, $db->select()->from($table, 'uuid'), $key);
$uuid = self::fetchOptionalUuid($connection, $query);
if ($uuid === null && $branch->isBranch()) {
$query = $db->select()->from("branched_$table", 'uuid')->where('object_name = ?', $key);
@ -70,14 +70,16 @@ class UuidLookup
return $uuid;
}
protected static function addKeyToQuery($query, $key)
protected static function addKeyToQuery(Db $connection, $query, $key)
{
if (is_int($key)) {
$query->where('id = ?', $key);
} elseif (is_string($key)) {
$query->where('object_name = ?', $key);
} else {
throw new RuntimeException('Cannot deal with non-int/string keys for UUID fallback');
foreach ($key as $k => $v) {
$query->where($connection->getDbAdapter()->quoteIdentifier($k) . ' = ?', $v);
}
}
return $query;

View File

@ -9,6 +9,7 @@ use Icinga\Data\Filter\FilterExpression;
use Icinga\Exception\NotFoundError;
use Icinga\Module\Director\CustomVariable\CustomVariables;
use Icinga\Module\Director\Data\Db\DbDataFormatter;
use Icinga\Module\Director\Data\Db\DbObjectStore;
use Icinga\Module\Director\Data\Db\DbObjectTypeRegistry;
use Icinga\Module\Director\IcingaConfig\AssignRenderer;
use Icinga\Module\Director\Data\Db\DbObject;
@ -530,6 +531,25 @@ abstract class IcingaObject extends DbObject implements IcingaConfigRenderer
return $this;
}
public function getUnresolvedRelated($property)
{
if ($this->hasRelation($property)) {
$property .= '_id';
if (isset($this->unresolvedRelatedProperties[$property])) {
return $this->unresolvedRelatedProperties[$property];
}
return null;
}
throw new RuntimeException(sprintf(
'%s "%s" has no %s reference',
$this->getShortTableName(),
$this->getObjectName(),
$property
));
}
/**
* @param $name
*/
@ -1695,11 +1715,16 @@ abstract class IcingaObject extends DbObject implements IcingaConfigRenderer
try {
$object->renderToConfig($config);
} catch (Exception $e) {
$message = $e->getMessage();
$showTrace = false;
if ($showTrace) {
$message .= "\n" . $e->getTraceAsString();
}
$config->configFile(
'failed-to-render'
)->prepend(
"/** Failed to render this object **/\n"
. '/* ' . $e->getMessage() . ' */'
. '/* ' . $message . ' */'
);
}
if ($wasExternal) {

View File

@ -473,6 +473,10 @@ class IcingaService extends IcingaObject implements ExportInterface
*/
public function hasBeenAssignedToHostTemplate()
{
// Branches would fail
if ($this->properties['host_id'] === null) {
return null;
}
$hostId = $this->get('host_id');
return $hostId && $this->getRelatedObject(

View File

@ -2,8 +2,11 @@
namespace Icinga\Module\Director\Web\Controller;
use Icinga\Module\Director\Data\Db\DbObjectStore;
use Icinga\Module\Director\Data\Db\DbObjectTypeRegistry;
use Icinga\Module\Director\Db\Branch\Branch;
use Icinga\Module\Director\Db\Branch\BranchStore;
use Icinga\Module\Director\Objects\IcingaObject;
use Icinga\Module\Director\Web\Widget\NotInBranchedHint;
trait BranchHelper
@ -14,6 +17,23 @@ trait BranchHelper
/** @var BranchStore */
protected $branchStore;
protected static $banchedTables = [
'icinga_apiuser',
'icinga_command',
'icinga_dependency',
'icinga_endpoint',
'icinga_host',
'icinga_hostgroup',
'icinga_notification',
'icinga_scheduled_downtime',
'icinga_service',
'icinga_servicegroup',
'icinga_timeperiod',
'icinga_user',
'icinga_usergroup',
'icinga_zone',
];
/**
* @return false|\Ramsey\Uuid\UuidInterface
*/
@ -49,6 +69,18 @@ trait BranchHelper
return $this->getBranchUuid() !== null;
}
protected function tableHasBranchSupport($table)
{
return in_array($table, self::$banchedTables, true);
}
protected function enableStaticObjectLoader($table)
{
if ($this->tableHasBranchSupport($table)) {
IcingaObject::setDbObjectStore(new DbObjectStore($this->db(), $this->getBranch()));
}
}
/**
* @param string $subject
* @return bool

View File

@ -62,6 +62,7 @@ abstract class ObjectController extends ActionController
public function init()
{
parent::init();
$this->enableStaticObjectLoader($this->getTableName());
if ($this->getRequest()->isApiRequest()) {
$handler = new IcingaObjectHandler($this->getRequest(), $this->getResponse(), $this->db());

View File

@ -343,4 +343,38 @@ class ObjectsTable extends ZfQueryBasedTable
return $query;
}
protected static function branchifyQuery(Db $connection, $query, $table, UuidInterface $branchUuid)
{
$right = clone($query);
/** @var Db $conn */
$conn = $connection;
$db = $connection->getDbAdapter();
$query->joinLeft(
['bo' => "branched_$table"],
// TODO: PgHexFunc
$db->quoteInto(
'bo.uuid = o.uuid AND bo.branch_uuid = ?',
$conn->quoteBinary($branchUuid->getBytes())
),
[]
)->where("(bo.branch_deleted IS NULL OR bo.branch_deleted = 'n')");
$this->applyObjectTypeFilter($query, $right);
$right->joinRight(
['bo' => "branched_$table"],
'bo.uuid = o.uuid',
[]
)
->where('o.uuid IS NULL')
->where('bo.branch_uuid = ?', $conn->quoteBinary($branchUuid->getBytes()));
$this->leftSubQuery = $query;
$this->rightSubQuery = $right;
$query = $db->select()->union([
'l' => new DbSelectParenthesis($query),
'r' => new DbSelectParenthesis($right),
]);
$query = $db->select()->from(['u' => $query]);
$query->order('object_name')->limit(100);
}
}