2015-07-24 10:51:55 +02:00
|
|
|
<?php
|
|
|
|
|
|
|
|
namespace Icinga\Module\Director\Import;
|
|
|
|
|
2015-08-04 19:52:02 +02:00
|
|
|
use Icinga\Module\Director\Objects\IcingaObject;
|
2015-07-24 10:51:55 +02:00
|
|
|
use Icinga\Module\Director\Objects\ImportSource;
|
|
|
|
use Icinga\Module\Director\Objects\SyncRule;
|
|
|
|
|
|
|
|
class Sync
|
|
|
|
{
|
|
|
|
protected function __construct()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
public static function run(SyncRule $rule)
|
|
|
|
{
|
|
|
|
$sync = new static;
|
|
|
|
return $sync->runWithRule($rule);
|
|
|
|
}
|
|
|
|
|
|
|
|
protected function extractVariableNames($string)
|
|
|
|
{
|
|
|
|
if (preg_match_all('/\${([A-Za-z0-9_-]+)}/', $string, $m, PREG_PATTERN_ORDER)) {
|
|
|
|
return $m[1];
|
|
|
|
} else {
|
|
|
|
return array();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
protected function fillVariables($string, $row)
|
|
|
|
{
|
2015-08-28 23:52:02 +02:00
|
|
|
if (preg_match('/^\${([A-Za-z0-9_-]+)}$/', $string, $m)) {
|
|
|
|
$var = $m[1];
|
|
|
|
if (! property_exists($row, $var)) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
if ($row->{$var . '__f'} === 'json') {
|
|
|
|
return json_decode($row->$var);
|
|
|
|
}
|
|
|
|
return $row->$var;
|
|
|
|
}
|
|
|
|
|
2015-07-24 10:51:55 +02:00
|
|
|
$func = function ($match) use ($row) {
|
|
|
|
return $row->{$match[1]};
|
|
|
|
};
|
|
|
|
|
|
|
|
return preg_replace_callback('/\${([A-Za-z0-9_-]+)}/', $func, $string);
|
|
|
|
}
|
|
|
|
|
|
|
|
protected function runWithRule(SyncRule $rule)
|
|
|
|
{
|
|
|
|
$db = $rule->getConnection();
|
|
|
|
$properties = $rule->fetchSyncProperties();
|
|
|
|
$sourceColumns = array();
|
|
|
|
$sources = array();
|
|
|
|
// $fieldMap = array();
|
|
|
|
|
|
|
|
foreach ($properties as $p) {
|
|
|
|
$sourceId = $p->source_id;
|
|
|
|
if (! array_key_exists($sourceId, $sources)) {
|
|
|
|
$sources[$sourceId] = ImportSource::load($sourceId, $db);
|
|
|
|
$sourceColumns[$sourceId] = array();
|
|
|
|
}
|
|
|
|
|
|
|
|
foreach ($this->extractVariableNames($p->source_expression) as $varname) {
|
|
|
|
$sourceColumns[$sourceId][$varname] = $varname;
|
|
|
|
// -> ? $fieldMap[
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
$imported = array();
|
|
|
|
foreach ($sources as $source) {
|
|
|
|
$sourceId = $source->id;
|
|
|
|
$key = $source->key_column;
|
2015-07-26 15:46:25 +02:00
|
|
|
$sourceColumns[$sourceId][$key] = $key;
|
|
|
|
$rows = $db->fetchLatestImportedRows($sourceId, $sourceColumns[$sourceId]);
|
|
|
|
|
2015-07-24 10:51:55 +02:00
|
|
|
$imported[$sourceId] = array();
|
|
|
|
foreach ($rows as $row) {
|
2015-07-24 15:27:37 +02:00
|
|
|
if (! property_exists($row, $key)) {
|
|
|
|
throw new \Exception(
|
|
|
|
sprintf(
|
|
|
|
'There is no key column "%s" in this row from "%s": %s', $key, $source->source_name, json_encode($row)
|
|
|
|
)
|
|
|
|
);
|
|
|
|
}
|
2015-07-24 10:51:55 +02:00
|
|
|
$imported[$sourceId][$row->$key] = $row;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: Filter auf object, nicht template
|
2015-08-04 19:52:02 +02:00
|
|
|
$objects = IcingaObject::loadAllByType($rule->object_type, $db);
|
|
|
|
$dummy = IcingaObject::createByType($rule->object_type, array());
|
|
|
|
$objectKey = $rule->object_type === 'datalistEntry' ? 'entry_name' : 'object_name';
|
2015-07-24 10:51:55 +02:00
|
|
|
|
2015-07-26 15:46:25 +02:00
|
|
|
foreach ($sources as $source) {
|
|
|
|
$sourceId = $source->id;
|
2015-07-24 10:51:55 +02:00
|
|
|
|
2015-07-26 15:46:25 +02:00
|
|
|
foreach ($imported[$sourceId] as $key => $row) {
|
|
|
|
$newProps = array(
|
|
|
|
'object_type' => 'object',
|
|
|
|
'object_name' => $key
|
|
|
|
);
|
2015-07-24 10:51:55 +02:00
|
|
|
|
2015-08-04 19:52:02 +02:00
|
|
|
if ($rule->object_type === 'datalistEntry') {
|
|
|
|
$newProps = array();
|
|
|
|
}
|
|
|
|
|
2015-07-26 15:46:25 +02:00
|
|
|
$newVars = array();
|
2015-08-03 13:39:55 +02:00
|
|
|
$imports = array();
|
2015-07-24 10:51:55 +02:00
|
|
|
|
2015-07-26 15:46:25 +02:00
|
|
|
foreach ($properties as $p) {
|
|
|
|
if ($p->source_id !== $sourceId) continue;
|
2015-07-24 10:51:55 +02:00
|
|
|
|
2015-07-26 15:46:25 +02:00
|
|
|
$prop = $p->destination_field;
|
|
|
|
$val = $this->fillVariables($p->source_expression, $row);
|
|
|
|
|
|
|
|
if (substr($prop, 0, 5) === 'vars.') {
|
|
|
|
$newVars[substr($prop, 5)] = $val;
|
|
|
|
} else {
|
2015-08-03 13:39:55 +02:00
|
|
|
if ($prop === 'import') {
|
|
|
|
$imports[] = $val;
|
|
|
|
} else {
|
|
|
|
$newProps[$prop] = $val;
|
|
|
|
}
|
2015-07-26 15:46:25 +02:00
|
|
|
}
|
2015-07-24 10:51:55 +02:00
|
|
|
}
|
2015-07-26 15:46:25 +02:00
|
|
|
|
|
|
|
if (array_key_exists($key, $objects)) {
|
|
|
|
switch ($rule->update_policy) {
|
|
|
|
case 'override':
|
2015-08-04 19:52:02 +02:00
|
|
|
$objects[$key] = IcingaObject::createByType(
|
|
|
|
$rule->object_type,
|
|
|
|
$newProps,
|
|
|
|
$db
|
|
|
|
);
|
|
|
|
|
2015-07-26 15:46:25 +02:00
|
|
|
foreach ($newVars as $prop => $var) {
|
|
|
|
$objects[$key]->vars()->$prop = $var;
|
|
|
|
}
|
2015-08-03 13:39:55 +02:00
|
|
|
if (! empty($imports)) {
|
|
|
|
$objects[$key]->imports()->set($imports);
|
|
|
|
}
|
2015-07-26 15:46:25 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 'merge':
|
|
|
|
$object = $objects[$key];
|
|
|
|
foreach ($newProps as $prop => $value) {
|
|
|
|
// TODO: data type?
|
|
|
|
$object->set($prop, $value);
|
|
|
|
}
|
|
|
|
foreach ($newVars as $prop => $var) {
|
|
|
|
// TODO: property merge policy
|
|
|
|
$object->vars()->$prop = $var;
|
|
|
|
}
|
2015-08-03 13:39:55 +02:00
|
|
|
if (! empty($imports)) {
|
|
|
|
// TODO: merge imports ?!
|
|
|
|
$objects[$key]->imports()->set($imports);
|
|
|
|
}
|
2015-07-26 15:46:25 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
// policy 'ignore', no action
|
|
|
|
}
|
|
|
|
} else {
|
2015-08-04 19:52:02 +02:00
|
|
|
$objects[$key] = IcingaObject::createByType($rule->object_type, $newProps, $db);
|
2015-07-26 15:46:25 +02:00
|
|
|
foreach ($newVars as $prop => $var) {
|
|
|
|
$objects[$key]->vars()->$prop = $var;
|
|
|
|
}
|
2015-08-03 15:12:46 +02:00
|
|
|
|
|
|
|
if (! empty($imports)) {
|
|
|
|
$objects[$key]->imports()->set($imports);
|
|
|
|
}
|
2015-07-24 10:51:55 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-07-26 15:46:25 +02:00
|
|
|
}
|
|
|
|
|
2015-07-24 15:29:17 +02:00
|
|
|
$dba = $db->getDbAdapter();
|
|
|
|
$dba->beginTransaction();
|
2015-07-24 10:51:55 +02:00
|
|
|
foreach ($objects as $object) {
|
2015-08-04 19:52:02 +02:00
|
|
|
if ($object instanceof IcingaObject && $object->isTemplate()) {
|
2015-07-24 15:30:09 +02:00
|
|
|
if ($object->hasBeenModified()) {
|
|
|
|
throw new \Exception(
|
|
|
|
sprintf(
|
|
|
|
'Sync is not allowed to modify template "%s"',
|
2015-08-04 19:52:02 +02:00
|
|
|
$object->$objectKey
|
2015-07-24 15:30:09 +02:00
|
|
|
)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2015-07-24 10:51:55 +02:00
|
|
|
if ($object->hasBeenLoadedFromDb() && $rule->purge_existing === 'y') {
|
2015-07-24 15:30:57 +02:00
|
|
|
$found = false;
|
|
|
|
foreach ($sources as $source) {
|
2015-08-04 19:52:02 +02:00
|
|
|
if (array_key_exists($object->$objectKey, $imported[$source->id])) {
|
2015-07-24 15:30:57 +02:00
|
|
|
$found = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (! $found) {
|
2015-07-24 10:51:55 +02:00
|
|
|
$object->delete();
|
|
|
|
}
|
|
|
|
}
|
2015-08-04 19:52:02 +02:00
|
|
|
if (! $object->$objectKey) {
|
2015-07-26 15:46:25 +02:00
|
|
|
continue;
|
|
|
|
}
|
2015-07-24 10:51:55 +02:00
|
|
|
$object->store($db);
|
|
|
|
}
|
2015-07-24 15:29:17 +02:00
|
|
|
|
|
|
|
$dba->commit();
|
2015-07-24 15:31:16 +02:00
|
|
|
return 42; // We have no sync_run history table yet
|
2015-07-24 10:51:55 +02:00
|
|
|
}
|
|
|
|
}
|