parent
b4839f6855
commit
c24961f209
|
@ -0,0 +1,166 @@
|
|||
<?php
|
||||
|
||||
namespace Icinga\Module\Director\Data;
|
||||
|
||||
use gipfl\Json\JsonDecodeException;
|
||||
use gipfl\Json\JsonString;
|
||||
use Icinga\Module\Director\Data\Db\DbObject;
|
||||
use Icinga\Module\Director\Db;
|
||||
use Icinga\Module\Director\DirectorObject\Automation\Basket;
|
||||
use Icinga\Module\Director\Objects\DirectorJob;
|
||||
use Icinga\Module\Director\Objects\IcingaHost;
|
||||
use Icinga\Module\Director\Objects\IcingaService;
|
||||
use Icinga\Module\Director\Objects\IcingaServiceSet;
|
||||
use Icinga\Module\Director\Objects\ImportSource;
|
||||
use Icinga\Module\Director\Objects\SyncRule;
|
||||
use InvalidArgumentException;
|
||||
use Ramsey\Uuid\Uuid;
|
||||
use stdClass;
|
||||
|
||||
class ObjectImporter
|
||||
{
|
||||
protected static $templatesOnly = [
|
||||
IcingaHost::class,
|
||||
IcingaService::class,
|
||||
IcingaServiceSet::class,
|
||||
];
|
||||
|
||||
/** @var Db */
|
||||
protected $db;
|
||||
|
||||
public function __construct(Db $db)
|
||||
{
|
||||
$this->db = $db;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param class-string|DbObject $implementation
|
||||
* @param stdClass $plain
|
||||
* @return DbObject
|
||||
* @throws JsonDecodeException
|
||||
*/
|
||||
public function import(string $implementation, stdClass $plain): DbObject
|
||||
{
|
||||
$this->assertTemplate($implementation, $plain);
|
||||
$this->fixRelations($implementation, $plain);
|
||||
$this->applyOtherWorkarounds($implementation, $plain);
|
||||
$this->fixLegacyBaskets($implementation, $plain);
|
||||
$this->fixSubObjects($implementation, $plain);
|
||||
|
||||
$object = $this->loadExistingObject($implementation, $plain);
|
||||
if ($object === null) {
|
||||
$object = $implementation::create([], $this->db);
|
||||
}
|
||||
|
||||
$properties = (array) $plain;
|
||||
unset($properties['fields']);
|
||||
unset($properties['originalId']);
|
||||
if ($implementation === Basket::class) {
|
||||
if (isset($properties['objects']) && is_string($properties['objects'])) {
|
||||
$properties['objects'] = JsonString::decode($properties['objects']);
|
||||
}
|
||||
}
|
||||
$object->setProperties($properties);
|
||||
|
||||
return $object;
|
||||
}
|
||||
|
||||
protected function fixLegacyBaskets(string $implementation, stdClass $plain)
|
||||
{
|
||||
// TODO: Check, whether current export sets modifiers = [] in case there is none
|
||||
if ($implementation == ImportSource::class) {
|
||||
if (!isset($plain->modifiers)) {
|
||||
$plain->modifiers = [];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function applyOtherWorkarounds(string $implementation, stdClass $plain)
|
||||
{
|
||||
if ($implementation === SyncRule::class) {
|
||||
if (isset($plain->properties)) {
|
||||
$plain->syncProperties = $plain->properties;
|
||||
unset($plain->properties);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function fixSubObjects(string $implementation, stdClass $plain)
|
||||
{
|
||||
if ($implementation === IcingaServiceSet::class) {
|
||||
foreach ($plain->services as $service) {
|
||||
unset($service->fields);
|
||||
}
|
||||
// Hint: legacy baskets are carrying service names as object keys, new baskets have arrays
|
||||
$plain->services = array_values((array) $plain->services);
|
||||
}
|
||||
}
|
||||
|
||||
protected function fixRelations(string $implementation, stdClass $plain)
|
||||
{
|
||||
if ($implementation === DirectorJob::class) {
|
||||
$settings = $plain->settings;
|
||||
$source = $settings->source ?? null;
|
||||
if ($source && !isset($settings->source_id)) {
|
||||
$settings->source_id = ImportSource::load($source, $this->db)->get('id');
|
||||
unset($settings->source);
|
||||
}
|
||||
$rule = $settings->rule ?? null;
|
||||
if ($rule && !isset($settings->rule_id)) {
|
||||
$settings->rule_id = SyncRule::load($rule, $this->db)->get('id');
|
||||
unset($settings->rule);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param class-string<DbObject> $implementation
|
||||
* @param stdClass $plain
|
||||
* @return DbObject|null
|
||||
*/
|
||||
protected function loadExistingObject(string $implementation, stdClass $plain): ?DbObject
|
||||
{
|
||||
if (isset($plain->uuid)) {
|
||||
return $implementation::loadWithUniqueId(Uuid::fromString($plain->uuid), $this->db);
|
||||
}
|
||||
|
||||
if ($implementation === IcingaService::class) {
|
||||
$key = [
|
||||
'object_type' => 'template',
|
||||
'object_name' => $plain->object_name
|
||||
];
|
||||
} else {
|
||||
$dummy = $implementation::create();
|
||||
$keyColumn = $dummy->getKeyName();
|
||||
if (is_array($keyColumn)) {
|
||||
if (empty($keyColumn)) {
|
||||
throw new \RuntimeException("$implementation has an empty keyColumn array");
|
||||
}
|
||||
$key = [];
|
||||
foreach ($keyColumn as $column) {
|
||||
if (isset($plain->$column)) {
|
||||
$key[$column] = $plain->$column;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$key = $plain->$keyColumn;
|
||||
}
|
||||
}
|
||||
|
||||
return $implementation::loadOptional($key, $this->db);
|
||||
}
|
||||
|
||||
protected function assertTemplate(string $implementation, stdClass $plain)
|
||||
{
|
||||
if (! in_array($implementation, self::$templatesOnly)) {
|
||||
return;
|
||||
}
|
||||
if ($plain->object_type !== 'template') {
|
||||
throw new InvalidArgumentException(sprintf(
|
||||
'Can import only Templates, got "%s" for "%s"',
|
||||
$plain->object_type,
|
||||
$plain->name
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,10 +2,11 @@
|
|||
|
||||
namespace Icinga\Module\Director\DirectorObject\Automation;
|
||||
|
||||
use gipfl\Json\JsonDecodeException;
|
||||
use gipfl\Json\JsonEncodeException;
|
||||
use gipfl\Json\JsonString;
|
||||
use Icinga\Module\Director\Core\Json;
|
||||
use Icinga\Module\Director\Data\Exporter;
|
||||
use Icinga\Module\Director\Data\ObjectImporter;
|
||||
use Icinga\Module\Director\Db;
|
||||
use Icinga\Module\Director\Data\Db\DbObject;
|
||||
use Icinga\Module\Director\Objects\DirectorDatafield;
|
||||
|
@ -30,6 +31,7 @@ use Icinga\Module\Director\Objects\ImportSource;
|
|||
use Icinga\Module\Director\Objects\SyncRule;
|
||||
use InvalidArgumentException;
|
||||
use RuntimeException;
|
||||
use stdClass;
|
||||
|
||||
class BasketSnapshot extends DbObject
|
||||
{
|
||||
|
@ -240,7 +242,7 @@ class BasketSnapshot extends DbObject
|
|||
'basket_uuid' => $basket->get('uuid')
|
||||
]);
|
||||
$snapshot->objects = [];
|
||||
foreach ((array) Json::decode($string) as $type => $objects) {
|
||||
foreach ((array) JsonString::decode($string) as $type => $objects) {
|
||||
$snapshot->objects[$type] = (array) $objects;
|
||||
}
|
||||
|
||||
|
@ -251,21 +253,19 @@ class BasketSnapshot extends DbObject
|
|||
{
|
||||
$snapshot = new static();
|
||||
$snapshot->restoreObjects(
|
||||
Json::decode($string),
|
||||
JsonString::decode($string),
|
||||
$connection,
|
||||
$replace
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $all
|
||||
* @param Db $connection
|
||||
* @param bool $replace
|
||||
* @throws \Icinga\Module\Director\Exception\DuplicateKeyException
|
||||
* @throws \Zend_Db_Adapter_Exception
|
||||
* @throws \Icinga\Exception\NotFoundError
|
||||
* @throws JsonDecodeException
|
||||
*/
|
||||
protected function restoreObjects($all, Db $connection, $replace = true)
|
||||
protected function restoreObjects(stdClass $all, Db $connection, $replace = true)
|
||||
{
|
||||
$db = $connection->getDbAdapter();
|
||||
$db->beginTransaction();
|
||||
|
@ -280,21 +280,17 @@ class BasketSnapshot extends DbObject
|
|||
}
|
||||
|
||||
/**
|
||||
* @param $all
|
||||
* @param $typeName
|
||||
* @param BasketSnapshotFieldResolver $fieldResolver
|
||||
* @param Db $connection
|
||||
* @param $replace
|
||||
* @throws \Icinga\Exception\NotFoundError
|
||||
* @throws \Icinga\Module\Director\Exception\DuplicateKeyException
|
||||
* @throws \Zend_Db_Adapter_Exception
|
||||
* @throws JsonDecodeException
|
||||
*/
|
||||
public function restoreType(
|
||||
&$all,
|
||||
$typeName,
|
||||
stdClass $all,
|
||||
string $typeName,
|
||||
BasketSnapshotFieldResolver $fieldResolver,
|
||||
Db $connection,
|
||||
$replace
|
||||
bool $replace
|
||||
) {
|
||||
if (isset($all->$typeName)) {
|
||||
$objects = (array) $all->$typeName;
|
||||
|
@ -302,11 +298,10 @@ class BasketSnapshot extends DbObject
|
|||
return;
|
||||
}
|
||||
$class = static::getClassForType($typeName);
|
||||
|
||||
$importer = new ObjectImporter($connection);
|
||||
$changed = [];
|
||||
foreach ($objects as $key => $object) {
|
||||
/** @var DbObject $new */
|
||||
$new = $class::import($object, $connection, $replace);
|
||||
foreach ($objects as $object) {
|
||||
$new = $importer->import($class, $object);
|
||||
if ($new->hasBeenModified()) {
|
||||
if ($new instanceof IcingaObject && $new->supportsImports()) {
|
||||
/** @var ExportInterface $new */
|
||||
|
@ -325,7 +320,6 @@ class BasketSnapshot extends DbObject
|
|||
$fieldResolver->relinkObjectFields($new, $object);
|
||||
}
|
||||
}
|
||||
$allObjects[spl_object_hash($new)] = $object;
|
||||
}
|
||||
|
||||
/** @var IcingaObject $object */
|
||||
|
@ -334,7 +328,7 @@ class BasketSnapshot extends DbObject
|
|||
}
|
||||
foreach ($changed as $key => $new) {
|
||||
// Store related fields. As objects might have formerly been
|
||||
// un-stored, let's to it right here
|
||||
// un-stored, let's do it right here
|
||||
if ($new instanceof IcingaObject) {
|
||||
$fieldResolver->relinkObjectFields($new, $objects[$key]);
|
||||
}
|
||||
|
@ -358,10 +352,9 @@ class BasketSnapshot extends DbObject
|
|||
}
|
||||
|
||||
/**
|
||||
* @return BasketContent
|
||||
* @throws \Icinga\Exception\NotFoundError
|
||||
*/
|
||||
protected function getContent()
|
||||
protected function getContent(): BasketContent
|
||||
{
|
||||
if ($this->content === null) {
|
||||
$this->content = BasketContent::load($this->get('content_checksum'), $this->getConnection());
|
||||
|
@ -380,26 +373,25 @@ class BasketSnapshot extends DbObject
|
|||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
* @throws \Icinga\Exception\NotFoundError
|
||||
* @throws \Icinga\Exception\NotFoundError|JsonEncodeException
|
||||
*/
|
||||
public function getJsonSummary()
|
||||
public function getJsonSummary(): string
|
||||
{
|
||||
if ($this->hasBeenLoadedFromDb()) {
|
||||
return $this->getContent()->get('summary');
|
||||
}
|
||||
|
||||
return Json::encode($this->getSummary(), JSON_PRETTY_PRINT);
|
||||
return JsonString::encode($this->getSummary(), JSON_PRETTY_PRINT);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array|mixed
|
||||
* @throws \Icinga\Exception\NotFoundError
|
||||
* @throws \Icinga\Exception\NotFoundError|JsonDecodeException
|
||||
*/
|
||||
public function getSummary()
|
||||
{
|
||||
if ($this->hasBeenLoadedFromDb()) {
|
||||
return Json::decode($this->getContent()->get('summary'));
|
||||
return JsonString::decode($this->getContent()->get('summary'));
|
||||
}
|
||||
|
||||
$summary = [];
|
||||
|
@ -412,7 +404,7 @@ class BasketSnapshot extends DbObject
|
|||
|
||||
/**
|
||||
* @return string
|
||||
* @throws \Icinga\Exception\NotFoundError
|
||||
* @throws \Icinga\Exception\NotFoundError|JsonEncodeException
|
||||
*/
|
||||
public function getJsonDump()
|
||||
{
|
||||
|
@ -493,21 +485,17 @@ class BasketSnapshot extends DbObject
|
|||
*/
|
||||
public static function instanceByIdentifier($typeName, $identifier, Db $connection)
|
||||
{
|
||||
/** @var class-string<DbObject> $class */
|
||||
$class = static::getClassForType($typeName);
|
||||
if (substr($class, -13) === 'IcingaService') {
|
||||
if ($class === IcingaService::class) {
|
||||
$identifier = [
|
||||
'object_type' => 'template',
|
||||
'object_name' => $identifier,
|
||||
];
|
||||
}
|
||||
/** @var ExportInterface $object */
|
||||
if ($class::exists($identifier, $connection)) {
|
||||
$object = $class::load($identifier, $connection);
|
||||
} else {
|
||||
$object = null;
|
||||
}
|
||||
|
||||
return $object;
|
||||
/** @var ExportInterface $object */
|
||||
return $class::loadOptional($identifier, $connection);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
Loading…
Reference in New Issue