icingaweb2-module-director/library/Director/DirectorObject/ObjectPurgeHelper.php

150 lines
4.4 KiB
PHP
Raw Normal View History

<?php
namespace Icinga\Module\Director\DirectorObject;
use Icinga\Module\Director\Db;
use Icinga\Module\Director\Objects\IcingaObject;
use InvalidArgumentException;
class ObjectPurgeHelper
{
protected $db;
protected $force = false;
public function __construct(Db $db)
{
$this->db = $db;
}
public function force($force = true)
{
$this->force = $force;
return $this;
}
public function purge(array $keep, $class, $objectType = null)
{
if (empty($keep) && ! $this->force) {
throw new InvalidArgumentException('I will NOT purge all object unless being forced to do so');
}
$db = $this->db->getDbAdapter();
/** @var IcingaObject $class cheating, it's a class name, not an object */
$dummy = $class::create();
assert($dummy instanceof IcingaObject);
$keyCols = (array) $dummy->getKeyName();
if ($objectType !== null) {
$keyCols[] = 'object_type';
}
$keepKeys = [];
foreach ($keep as $object) {
if ($object instanceof \stdClass) {
$properties = (array) $object;
// TODO: this is object-specific and to be found in the ::import() function!
unset($properties['fields']);
$object = $class::fromPlainObject($properties);
} elseif (\get_class($object) !== $class) {
throw new InvalidArgumentException(
'Can keep only matching objects, expected "%s", got "%s',
$class,
\get_class($keep)
);
}
$key = [];
foreach ($keyCols as $col) {
$key[$col] = $object->get($col);
}
$keepKeys[$this->makeRowKey($key)] = true;
}
$query = $db->select()->from(['o' => $dummy->getTableName()], $keyCols);
if ($objectType !== null) {
$query->where('object_type = ?', $objectType);
}
$allExisting = [];
foreach ($db->fetchAll($query) as $row) {
$allExisting[$this->makeRowKey($row)] = $row;
}
$remove = [];
foreach ($allExisting as $key => $keyProperties) {
if (! isset($keepKeys[$key])) {
$remove[] = $keyProperties;
}
}
$db->beginTransaction();
foreach ($remove as $keyProperties) {
$keyColumn = $class::getKeyColumnName();
if (is_array($keyColumn)) {
$object = $class::load((array) $keyProperties, $this->db);
} else {
$object = $class::load($keyProperties->$keyColumn, $this->db);
}
$object->delete();
}
$db->commit();
}
public static function listObjectTypesAvailableForPurge()
{
return [
'Basket',
'Command',
'CommandTemplate',
'Dependency',
'DirectorJob',
'ExternalCommand',
'HostGroup',
'HostTemplate',
'IcingaTemplateChoiceHost',
'IcingaTemplateChoiceService',
'ImportSource',
'Notification',
'NotificationTemplate',
'ServiceGroup',
'ServiceSet',
'ServiceTemplate',
'SyncRule',
'TimePeriod',
];
}
public static function objectTypeIsEligibleForPurge($type)
{
return in_array($type, static::listObjectTypesAvailableForPurge(), true);
}
public static function assertObjectTypesAreEligibleForPurge($types)
{
$invalid = [];
foreach ($types as $type) {
if (! static::objectTypeIsEligibleForPurge($type)) {
$invalid[] = $type;
}
}
if (empty($invalid)) {
return;
}
if (count($invalid) === 1) {
$message = sprintf('"%s" is not eligible for purge', $invalid[0]);
} else {
$message = 'The following types are not eligible for purge: '
. implode(', ', $invalid);
}
throw new InvalidArgumentException(
"$message. Valid types: "
. implode(', ', static::listObjectTypesAvailableForPurge())
);
}
protected function makeRowKey($row)
{
$row = (array) $row;
ksort($row);
return json_encode($row, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
}
}