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;
|
namespace Icinga\Module\Director\DirectorObject\Automation;
|
||||||
|
|
||||||
|
use gipfl\Json\JsonDecodeException;
|
||||||
use gipfl\Json\JsonEncodeException;
|
use gipfl\Json\JsonEncodeException;
|
||||||
use gipfl\Json\JsonString;
|
use gipfl\Json\JsonString;
|
||||||
use Icinga\Module\Director\Core\Json;
|
|
||||||
use Icinga\Module\Director\Data\Exporter;
|
use Icinga\Module\Director\Data\Exporter;
|
||||||
|
use Icinga\Module\Director\Data\ObjectImporter;
|
||||||
use Icinga\Module\Director\Db;
|
use Icinga\Module\Director\Db;
|
||||||
use Icinga\Module\Director\Data\Db\DbObject;
|
use Icinga\Module\Director\Data\Db\DbObject;
|
||||||
use Icinga\Module\Director\Objects\DirectorDatafield;
|
use Icinga\Module\Director\Objects\DirectorDatafield;
|
||||||
|
@ -30,6 +31,7 @@ use Icinga\Module\Director\Objects\ImportSource;
|
||||||
use Icinga\Module\Director\Objects\SyncRule;
|
use Icinga\Module\Director\Objects\SyncRule;
|
||||||
use InvalidArgumentException;
|
use InvalidArgumentException;
|
||||||
use RuntimeException;
|
use RuntimeException;
|
||||||
|
use stdClass;
|
||||||
|
|
||||||
class BasketSnapshot extends DbObject
|
class BasketSnapshot extends DbObject
|
||||||
{
|
{
|
||||||
|
@ -240,7 +242,7 @@ class BasketSnapshot extends DbObject
|
||||||
'basket_uuid' => $basket->get('uuid')
|
'basket_uuid' => $basket->get('uuid')
|
||||||
]);
|
]);
|
||||||
$snapshot->objects = [];
|
$snapshot->objects = [];
|
||||||
foreach ((array) Json::decode($string) as $type => $objects) {
|
foreach ((array) JsonString::decode($string) as $type => $objects) {
|
||||||
$snapshot->objects[$type] = (array) $objects;
|
$snapshot->objects[$type] = (array) $objects;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -251,21 +253,19 @@ class BasketSnapshot extends DbObject
|
||||||
{
|
{
|
||||||
$snapshot = new static();
|
$snapshot = new static();
|
||||||
$snapshot->restoreObjects(
|
$snapshot->restoreObjects(
|
||||||
Json::decode($string),
|
JsonString::decode($string),
|
||||||
$connection,
|
$connection,
|
||||||
$replace
|
$replace
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param $all
|
|
||||||
* @param Db $connection
|
|
||||||
* @param bool $replace
|
|
||||||
* @throws \Icinga\Module\Director\Exception\DuplicateKeyException
|
* @throws \Icinga\Module\Director\Exception\DuplicateKeyException
|
||||||
* @throws \Zend_Db_Adapter_Exception
|
* @throws \Zend_Db_Adapter_Exception
|
||||||
* @throws \Icinga\Exception\NotFoundError
|
* @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 = $connection->getDbAdapter();
|
||||||
$db->beginTransaction();
|
$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\Exception\NotFoundError
|
||||||
* @throws \Icinga\Module\Director\Exception\DuplicateKeyException
|
* @throws \Icinga\Module\Director\Exception\DuplicateKeyException
|
||||||
* @throws \Zend_Db_Adapter_Exception
|
* @throws \Zend_Db_Adapter_Exception
|
||||||
|
* @throws JsonDecodeException
|
||||||
*/
|
*/
|
||||||
public function restoreType(
|
public function restoreType(
|
||||||
&$all,
|
stdClass $all,
|
||||||
$typeName,
|
string $typeName,
|
||||||
BasketSnapshotFieldResolver $fieldResolver,
|
BasketSnapshotFieldResolver $fieldResolver,
|
||||||
Db $connection,
|
Db $connection,
|
||||||
$replace
|
bool $replace
|
||||||
) {
|
) {
|
||||||
if (isset($all->$typeName)) {
|
if (isset($all->$typeName)) {
|
||||||
$objects = (array) $all->$typeName;
|
$objects = (array) $all->$typeName;
|
||||||
|
@ -302,11 +298,10 @@ class BasketSnapshot extends DbObject
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$class = static::getClassForType($typeName);
|
$class = static::getClassForType($typeName);
|
||||||
|
$importer = new ObjectImporter($connection);
|
||||||
$changed = [];
|
$changed = [];
|
||||||
foreach ($objects as $key => $object) {
|
foreach ($objects as $object) {
|
||||||
/** @var DbObject $new */
|
$new = $importer->import($class, $object);
|
||||||
$new = $class::import($object, $connection, $replace);
|
|
||||||
if ($new->hasBeenModified()) {
|
if ($new->hasBeenModified()) {
|
||||||
if ($new instanceof IcingaObject && $new->supportsImports()) {
|
if ($new instanceof IcingaObject && $new->supportsImports()) {
|
||||||
/** @var ExportInterface $new */
|
/** @var ExportInterface $new */
|
||||||
|
@ -325,7 +320,6 @@ class BasketSnapshot extends DbObject
|
||||||
$fieldResolver->relinkObjectFields($new, $object);
|
$fieldResolver->relinkObjectFields($new, $object);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$allObjects[spl_object_hash($new)] = $object;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @var IcingaObject $object */
|
/** @var IcingaObject $object */
|
||||||
|
@ -334,7 +328,7 @@ class BasketSnapshot extends DbObject
|
||||||
}
|
}
|
||||||
foreach ($changed as $key => $new) {
|
foreach ($changed as $key => $new) {
|
||||||
// Store related fields. As objects might have formerly been
|
// 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) {
|
if ($new instanceof IcingaObject) {
|
||||||
$fieldResolver->relinkObjectFields($new, $objects[$key]);
|
$fieldResolver->relinkObjectFields($new, $objects[$key]);
|
||||||
}
|
}
|
||||||
|
@ -358,10 +352,9 @@ class BasketSnapshot extends DbObject
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return BasketContent
|
|
||||||
* @throws \Icinga\Exception\NotFoundError
|
* @throws \Icinga\Exception\NotFoundError
|
||||||
*/
|
*/
|
||||||
protected function getContent()
|
protected function getContent(): BasketContent
|
||||||
{
|
{
|
||||||
if ($this->content === null) {
|
if ($this->content === null) {
|
||||||
$this->content = BasketContent::load($this->get('content_checksum'), $this->getConnection());
|
$this->content = BasketContent::load($this->get('content_checksum'), $this->getConnection());
|
||||||
|
@ -380,26 +373,25 @@ class BasketSnapshot extends DbObject
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return string
|
* @throws \Icinga\Exception\NotFoundError|JsonEncodeException
|
||||||
* @throws \Icinga\Exception\NotFoundError
|
|
||||||
*/
|
*/
|
||||||
public function getJsonSummary()
|
public function getJsonSummary(): string
|
||||||
{
|
{
|
||||||
if ($this->hasBeenLoadedFromDb()) {
|
if ($this->hasBeenLoadedFromDb()) {
|
||||||
return $this->getContent()->get('summary');
|
return $this->getContent()->get('summary');
|
||||||
}
|
}
|
||||||
|
|
||||||
return Json::encode($this->getSummary(), JSON_PRETTY_PRINT);
|
return JsonString::encode($this->getSummary(), JSON_PRETTY_PRINT);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return array|mixed
|
* @return array|mixed
|
||||||
* @throws \Icinga\Exception\NotFoundError
|
* @throws \Icinga\Exception\NotFoundError|JsonDecodeException
|
||||||
*/
|
*/
|
||||||
public function getSummary()
|
public function getSummary()
|
||||||
{
|
{
|
||||||
if ($this->hasBeenLoadedFromDb()) {
|
if ($this->hasBeenLoadedFromDb()) {
|
||||||
return Json::decode($this->getContent()->get('summary'));
|
return JsonString::decode($this->getContent()->get('summary'));
|
||||||
}
|
}
|
||||||
|
|
||||||
$summary = [];
|
$summary = [];
|
||||||
|
@ -412,7 +404,7 @@ class BasketSnapshot extends DbObject
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return string
|
* @return string
|
||||||
* @throws \Icinga\Exception\NotFoundError
|
* @throws \Icinga\Exception\NotFoundError|JsonEncodeException
|
||||||
*/
|
*/
|
||||||
public function getJsonDump()
|
public function getJsonDump()
|
||||||
{
|
{
|
||||||
|
@ -493,21 +485,17 @@ class BasketSnapshot extends DbObject
|
||||||
*/
|
*/
|
||||||
public static function instanceByIdentifier($typeName, $identifier, Db $connection)
|
public static function instanceByIdentifier($typeName, $identifier, Db $connection)
|
||||||
{
|
{
|
||||||
|
/** @var class-string<DbObject> $class */
|
||||||
$class = static::getClassForType($typeName);
|
$class = static::getClassForType($typeName);
|
||||||
if (substr($class, -13) === 'IcingaService') {
|
if ($class === IcingaService::class) {
|
||||||
$identifier = [
|
$identifier = [
|
||||||
'object_type' => 'template',
|
'object_type' => 'template',
|
||||||
'object_name' => $identifier,
|
'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