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;
|
2015-10-20 22:21:48 +02:00
|
|
|
use Icinga\Exception\IcingaException;
|
2015-07-24 10:51:55 +02:00
|
|
|
|
|
|
|
class Sync
|
|
|
|
{
|
2016-02-23 11:10:37 +01:00
|
|
|
protected $rule;
|
|
|
|
|
2015-12-23 15:10:37 +01:00
|
|
|
protected $modify = array();
|
|
|
|
|
|
|
|
protected $remove = array();
|
|
|
|
|
|
|
|
protected $create = array();
|
|
|
|
|
|
|
|
protected $errors = array();
|
|
|
|
|
2016-02-23 11:10:37 +01:00
|
|
|
protected $hasCombinedKey;
|
|
|
|
|
|
|
|
protected $sourceKeyPattern;
|
|
|
|
|
|
|
|
protected $destinationKeyPattern;
|
|
|
|
|
2016-02-23 13:03:47 +01:00
|
|
|
protected $syncProperties;
|
|
|
|
|
2015-12-08 11:51:05 +01:00
|
|
|
/**
|
|
|
|
* Constructor. No direct initialization allowed right now. Please use one
|
|
|
|
* of the available static factory methods
|
|
|
|
*/
|
2016-02-23 11:10:37 +01:00
|
|
|
protected function __construct(SyncRule $rule)
|
2015-07-24 10:51:55 +02:00
|
|
|
{
|
2016-02-23 11:10:37 +01:00
|
|
|
$this->rule = $rule;
|
2016-02-23 11:22:25 +01:00
|
|
|
$this->db = $rule->getConnection();
|
2016-02-23 13:03:47 +01:00
|
|
|
$this->syncProperties = $rule->fetchSyncProperties();
|
2015-07-24 10:51:55 +02:00
|
|
|
}
|
|
|
|
|
2015-12-08 11:51:05 +01:00
|
|
|
/**
|
|
|
|
* Run the given sync rule
|
|
|
|
*/
|
2015-07-24 10:51:55 +02:00
|
|
|
public static function run(SyncRule $rule)
|
|
|
|
{
|
2016-02-23 11:10:37 +01:00
|
|
|
$sync = new static($rule);
|
2016-02-09 19:21:17 +01:00
|
|
|
|
|
|
|
// Raise limits. TODO: do this in a failsafe way, and only if necessary
|
|
|
|
ini_set('memory_limit', '768M');
|
|
|
|
ini_set('max_execution_time', 0);
|
|
|
|
|
2016-02-23 11:22:25 +01:00
|
|
|
return $sync->apply();
|
2015-07-24 10:51:55 +02:00
|
|
|
}
|
|
|
|
|
2015-12-08 11:51:05 +01:00
|
|
|
/**
|
|
|
|
* Whether the given sync rule would apply modifications
|
|
|
|
*
|
|
|
|
* @param SyncRule $rule SyncRule object
|
|
|
|
*
|
|
|
|
* @return boolean
|
|
|
|
*/
|
2015-10-30 23:34:27 +01:00
|
|
|
public static function hasModifications(SyncRule $rule)
|
|
|
|
{
|
2016-02-23 13:41:19 +01:00
|
|
|
return count(self::getExpectedModifications()) > 0;
|
2015-10-30 23:34:27 +01:00
|
|
|
}
|
|
|
|
|
2015-12-08 11:51:05 +01:00
|
|
|
/**
|
|
|
|
* Retrieve modifications a given SyncRule would apply
|
|
|
|
*
|
|
|
|
* @param SyncRule $rule SyncRule object
|
|
|
|
*
|
|
|
|
* @return array Array of IcingaObject elements
|
|
|
|
*/
|
2015-10-30 23:34:27 +01:00
|
|
|
public static function getExpectedModifications(SyncRule $rule)
|
|
|
|
{
|
|
|
|
$modified = array();
|
2016-02-23 11:10:37 +01:00
|
|
|
$sync = new static($rule);
|
2016-02-23 11:22:25 +01:00
|
|
|
$objects = $sync->prepare();
|
2015-10-30 23:34:27 +01:00
|
|
|
foreach ($objects as $object) {
|
|
|
|
if ($object->hasBeenModified()) {
|
|
|
|
$modified[] = $object;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return $modified;
|
|
|
|
}
|
|
|
|
|
2015-12-08 11:51:05 +01:00
|
|
|
/**
|
|
|
|
* Extract variable names in the form ${var_name} from a given string
|
|
|
|
*
|
|
|
|
* @param string $string
|
|
|
|
*
|
|
|
|
* @return array List of variable names (without ${})
|
|
|
|
*/
|
2015-07-24 10:51:55 +02:00
|
|
|
protected function extractVariableNames($string)
|
|
|
|
{
|
2015-11-25 09:21:15 +01:00
|
|
|
if (preg_match_all('/\${([A-Za-z0-9\._-]+)}/', $string, $m, PREG_PATTERN_ORDER)) {
|
2015-07-24 10:51:55 +02:00
|
|
|
return $m[1];
|
|
|
|
} else {
|
|
|
|
return array();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-12-08 11:51:05 +01:00
|
|
|
/**
|
|
|
|
* Transform the given value to an array
|
|
|
|
*
|
|
|
|
* @param array|string|null $value
|
|
|
|
*
|
|
|
|
* @return array
|
|
|
|
*/
|
2015-08-28 23:56:54 +02:00
|
|
|
protected function wantArray($value)
|
|
|
|
{
|
|
|
|
if (is_array($value)) {
|
|
|
|
return $value;
|
|
|
|
} elseif ($value === null) {
|
|
|
|
return array();
|
|
|
|
} else {
|
|
|
|
return array($value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-12-08 11:51:05 +01:00
|
|
|
/**
|
|
|
|
* Recursively extract a value from a nested structure
|
|
|
|
*
|
|
|
|
* For a $val looking like
|
|
|
|
*
|
|
|
|
* { 'vars' => { 'disk' => { 'sda' => { 'size' => '256G' } } } }
|
|
|
|
*
|
|
|
|
* and a key vars.disk.sda given as [ 'vars', 'disk', 'sda' ] this would
|
|
|
|
* return { size => '255GB' }
|
|
|
|
*
|
|
|
|
* @param string $val The value to extract data from
|
|
|
|
* @param object $keys A list of nested keys pointing to desired data
|
|
|
|
*
|
|
|
|
* @return mixed
|
|
|
|
*/
|
2015-11-25 09:21:15 +01:00
|
|
|
protected function getDeepValue($val, $keys)
|
|
|
|
{
|
|
|
|
$key = array_shift($keys);
|
|
|
|
if (! property_exists($val, $key)) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (empty($keys)) {
|
|
|
|
return $val->$key;
|
|
|
|
}
|
|
|
|
|
|
|
|
return $this->getDeepValue($val->$key, $keys);
|
|
|
|
}
|
|
|
|
|
2015-12-08 11:51:05 +01:00
|
|
|
/**
|
|
|
|
* Return a specific value from a given row object
|
|
|
|
*
|
|
|
|
* Supports also keys pointing to nested structures like vars.disk.sda
|
|
|
|
*
|
|
|
|
* @param object $row stdClass object providing property values
|
|
|
|
* @param string $string Variable/property name
|
|
|
|
*
|
|
|
|
* @return mixed
|
|
|
|
*/
|
2015-12-23 15:10:37 +01:00
|
|
|
public function getSpecificValue($row, $var)
|
2015-07-24 10:51:55 +02:00
|
|
|
{
|
2015-12-02 15:59:31 +01:00
|
|
|
if (strpos($var, '.') === false) {
|
|
|
|
if (! property_exists($row, $var)) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
return $row->$var;
|
|
|
|
} else {
|
|
|
|
$parts = explode('.', $var);
|
|
|
|
$main = array_shift($parts);
|
|
|
|
if (! is_object($row->$main)) {
|
2016-01-19 16:33:04 +01:00
|
|
|
throw new IcingaException('Data is not nested, cannot access %s: %s', $var, var_export($row, 1));
|
2015-08-28 23:52:02 +02:00
|
|
|
}
|
2015-11-25 09:21:15 +01:00
|
|
|
|
2015-12-02 15:59:31 +01:00
|
|
|
return $this->getDeepValue($row->$main, $parts);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-12-08 11:51:05 +01:00
|
|
|
/**
|
|
|
|
* Fill variables in the given string pattern
|
|
|
|
*
|
|
|
|
* This replaces all occurances of ${var_name} with the corresponding
|
|
|
|
* property $row->var_name of the given row object. Missing variables are
|
|
|
|
* replaced by an empty string. This works also fine in case there are
|
|
|
|
* multiple variables to be found in your string.
|
|
|
|
*
|
|
|
|
* @param string $string String with opional variables/placeholders
|
|
|
|
* @param object $row stdClass object providing property values
|
|
|
|
*
|
|
|
|
* @return string
|
|
|
|
*/
|
2015-12-02 15:59:31 +01:00
|
|
|
protected function fillVariables($string, $row)
|
|
|
|
{
|
|
|
|
if (preg_match('/^\${([A-Za-z0-9\._-]+)}$/', $string, $m)) {
|
|
|
|
return $this->getSpecificValue($row, $m[1]);
|
2015-08-28 23:52:02 +02:00
|
|
|
}
|
|
|
|
|
2015-12-23 15:10:37 +01:00
|
|
|
// PHP 5.3 :(
|
|
|
|
$self = $this;
|
|
|
|
$func = function ($match) use ($self, $row) {
|
|
|
|
return $self->getSpecificValue($row, $match[1]);
|
2015-07-24 10:51:55 +02:00
|
|
|
};
|
|
|
|
|
2015-11-25 09:21:15 +01:00
|
|
|
return preg_replace_callback('/\${([A-Za-z0-9\._-]+)}/', $func, $string);
|
2015-07-24 10:51:55 +02:00
|
|
|
}
|
|
|
|
|
2016-02-23 13:03:47 +01:00
|
|
|
protected function perpareImportSources()
|
2015-07-24 10:51:55 +02:00
|
|
|
{
|
|
|
|
$sources = array();
|
2016-02-23 13:03:47 +01:00
|
|
|
foreach ($this->syncProperties as $p) {
|
2015-12-08 16:40:47 +01:00
|
|
|
$sourceId = $p->source_id;
|
|
|
|
if (! array_key_exists($sourceId, $sources)) {
|
2016-02-23 11:55:16 +01:00
|
|
|
$sources[$sourceId] = ImportSource::load($sourceId, $this->db);
|
2015-12-08 16:40:47 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return $sources;
|
|
|
|
}
|
2015-12-08 11:51:05 +01:00
|
|
|
|
2016-02-23 13:03:47 +01:00
|
|
|
protected function prepareSourceColumns()
|
2015-12-08 16:40:47 +01:00
|
|
|
{
|
2015-07-24 10:51:55 +02:00
|
|
|
// $fieldMap = array();
|
2015-12-08 16:40:47 +01:00
|
|
|
$columns = array();
|
2015-07-24 10:51:55 +02:00
|
|
|
|
2016-02-23 13:03:47 +01:00
|
|
|
foreach ($this->syncProperties as $p) {
|
2015-07-24 10:51:55 +02:00
|
|
|
$sourceId = $p->source_id;
|
2015-12-09 11:45:04 +01:00
|
|
|
if (! array_key_exists($sourceId, $columns)) {
|
2015-12-08 16:40:47 +01:00
|
|
|
$columns[$sourceId] = array();
|
2015-07-24 10:51:55 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
foreach ($this->extractVariableNames($p->source_expression) as $varname) {
|
2015-12-08 16:40:47 +01:00
|
|
|
$columns[$sourceId][$varname] = $varname;
|
2015-07-24 10:51:55 +02:00
|
|
|
// -> ? $fieldMap[
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-12-08 16:40:47 +01:00
|
|
|
return $columns;
|
|
|
|
}
|
|
|
|
|
2016-02-23 11:10:37 +01:00
|
|
|
protected function hasCombinedKey()
|
2015-12-08 16:40:47 +01:00
|
|
|
{
|
2016-02-23 11:10:37 +01:00
|
|
|
if ($this->hasCombinedKey === null) {
|
2015-12-08 16:40:47 +01:00
|
|
|
|
2016-02-23 11:10:37 +01:00
|
|
|
$this->hasCombinedKey = false;
|
2015-12-08 16:40:47 +01:00
|
|
|
|
2016-02-23 11:10:37 +01:00
|
|
|
if ($this->rule->object_type === 'service') {
|
|
|
|
$hasHost = false;
|
|
|
|
$hasObjectName = false;
|
2016-02-23 13:03:47 +01:00
|
|
|
foreach ($this->syncProperties as $key => $property) {
|
2016-02-23 11:10:37 +01:00
|
|
|
if ($property->destination_field === 'host') {
|
|
|
|
$hasHost = $property->source_expression;
|
|
|
|
}
|
|
|
|
if ($property->destination_field === 'object_name') {
|
|
|
|
$hasObjectName = $property->source_expression;
|
|
|
|
}
|
2016-02-23 08:58:22 +01:00
|
|
|
}
|
|
|
|
|
2016-02-23 11:10:37 +01:00
|
|
|
if ($hasHost !== false && $hasObjectName !== false) {
|
|
|
|
$this->hasCombinedKey = true;
|
|
|
|
$this->sourceKeyPattern = sprintf('%s!%s', $hasHost, $hasObjectName);
|
|
|
|
$this->destinationKeyPattern = 'host!object_name';
|
|
|
|
}
|
2016-02-23 08:58:22 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-23 11:10:37 +01:00
|
|
|
return $this->hasCombinedKey;
|
|
|
|
}
|
|
|
|
|
2016-02-23 13:03:47 +01:00
|
|
|
protected function fetchImportedData($sources)
|
2016-02-23 11:10:37 +01:00
|
|
|
{
|
|
|
|
$imported = array();
|
2016-02-23 11:22:25 +01:00
|
|
|
$rule = $this->rule;
|
2016-02-23 11:10:37 +01:00
|
|
|
|
2016-02-23 13:03:47 +01:00
|
|
|
$sourceColumns = $this->prepareSourceColumns();
|
2016-02-23 11:10:37 +01:00
|
|
|
|
2015-07-24 10:51:55 +02:00
|
|
|
foreach ($sources as $source) {
|
|
|
|
$sourceId = $source->id;
|
|
|
|
$key = $source->key_column;
|
2015-07-26 15:46:25 +02:00
|
|
|
$sourceColumns[$sourceId][$key] = $key;
|
2016-02-23 11:55:16 +01:00
|
|
|
$rows = $this->db->fetchLatestImportedRows($sourceId, $sourceColumns[$sourceId]);
|
2015-07-26 15:46:25 +02:00
|
|
|
|
2015-07-24 10:51:55 +02:00
|
|
|
$imported[$sourceId] = array();
|
|
|
|
foreach ($rows as $row) {
|
2016-02-23 11:10:37 +01:00
|
|
|
if ($this->hasCombinedKey()) {
|
|
|
|
$key = $this->fillVariables($this->sourceKeyPattern, $row);
|
2016-02-23 08:58:22 +01:00
|
|
|
if (array_key_exists($key, $imported[$sourceId])) {
|
|
|
|
throw new IcingaException(
|
|
|
|
'Trying to import row "%s" (%s) twice: %s VS %s',
|
|
|
|
$key,
|
2016-02-23 11:10:37 +01:00
|
|
|
$this->sourceKeyPattern,
|
2016-02-23 08:58:22 +01:00
|
|
|
json_encode($imported[$sourceId][$key]),
|
|
|
|
json_encode($row)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
if (! property_exists($row, $key)) {
|
|
|
|
throw new IcingaException(
|
|
|
|
'There is no key column "%s" in this row from "%s": %s',
|
|
|
|
$key,
|
|
|
|
$source->source_name,
|
|
|
|
json_encode($row)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2015-07-24 15:27:37 +02:00
|
|
|
}
|
2016-02-23 08:58:22 +01:00
|
|
|
|
2015-12-04 10:59:25 +01:00
|
|
|
if (! $rule->matches($row)) {
|
|
|
|
continue;
|
|
|
|
}
|
2016-02-23 08:58:22 +01:00
|
|
|
|
2016-02-23 11:10:37 +01:00
|
|
|
if ($this->hasCombinedKey()) {
|
2016-02-23 08:58:22 +01:00
|
|
|
$imported[$sourceId][$key] = $row;
|
|
|
|
} else {
|
|
|
|
$imported[$sourceId][$row->$key] = $row;
|
|
|
|
}
|
2015-07-24 10:51:55 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-12-08 16:40:47 +01:00
|
|
|
return $imported;
|
|
|
|
}
|
|
|
|
|
2016-02-19 12:42:02 +01:00
|
|
|
// TODO: This is rubbish, we need to filter at fetch time
|
2016-02-23 13:03:47 +01:00
|
|
|
protected function removeForeignListEntries(& $objects)
|
2016-02-19 12:42:02 +01:00
|
|
|
{
|
|
|
|
$listId = null;
|
2016-02-23 13:03:47 +01:00
|
|
|
foreach ($this->syncProperties as $prop) {
|
2016-02-19 12:42:02 +01:00
|
|
|
if ($prop->destination_field === 'list_id') {
|
|
|
|
$listId = (int) $prop->source_expression;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($listId === null) {
|
|
|
|
throw new IcingaException(
|
|
|
|
'Cannot sync datalist entry without list_ist'
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
$no = array();
|
|
|
|
foreach ($objects as $k => $o) {
|
|
|
|
if ($o->list_id !== $listId) {
|
|
|
|
$no[] = $k;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
foreach ($no as $k) {
|
|
|
|
unset($objects[$k]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-23 13:03:47 +01:00
|
|
|
protected function prepareNewObjects(& $sources, & $imported)
|
2015-12-08 16:40:47 +01:00
|
|
|
{
|
2016-02-19 15:23:44 +01:00
|
|
|
$newObjects = array();
|
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) {
|
2015-10-30 23:34:27 +01:00
|
|
|
$newProps = array();
|
2015-08-04 19:52:02 +02:00
|
|
|
|
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
|
|
|
|
2016-02-23 13:03:47 +01:00
|
|
|
foreach ($this->syncProperties as $p) {
|
2015-07-26 15:46:25 +02:00
|
|
|
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.') {
|
2015-08-28 23:56:54 +02:00
|
|
|
$varName = substr($prop, 5);
|
|
|
|
if (substr($varName, -2) === '[]') {
|
|
|
|
$varName = substr($varName, 0, -2);
|
|
|
|
$val = $this->wantArray($val);
|
|
|
|
}
|
|
|
|
$newVars[$varName] = $val;
|
2015-07-26 15:46:25 +02:00
|
|
|
} 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
|
|
|
}
|
2016-02-19 15:23:44 +01:00
|
|
|
if (! array_key_exists($key, $newObjects)) {
|
|
|
|
$newObjects[$key] = IcingaObject::createByType(
|
2016-02-23 11:55:16 +01:00
|
|
|
$this->rule->object_type,
|
2016-02-19 15:23:44 +01:00
|
|
|
array(),
|
2016-02-23 11:55:16 +01:00
|
|
|
$this->db
|
2015-12-10 12:57:11 +01:00
|
|
|
);
|
2016-02-19 15:23:44 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
$object = $newObjects[$key];
|
2015-12-10 12:57:11 +01:00
|
|
|
|
2016-02-19 15:23:44 +01:00
|
|
|
// Safe default values for object_type and object_name
|
2016-02-23 11:55:16 +01:00
|
|
|
if ($this->rule->object_type !== 'datalistEntry') {
|
2016-02-19 15:23:44 +01:00
|
|
|
if (! array_key_exists('object_type', $newProps)
|
|
|
|
|| $newProps['object_type'] === null
|
|
|
|
) {
|
|
|
|
$newProps['object_type'] = 'object';
|
2015-07-26 15:46:25 +02:00
|
|
|
}
|
2015-08-03 15:12:46 +02:00
|
|
|
|
2016-02-19 15:23:44 +01:00
|
|
|
if (! array_key_exists('object_name', $newProps)
|
|
|
|
|| $newProps['object_name'] === null
|
|
|
|
) {
|
|
|
|
$newProps['object_name'] = $key;
|
2015-08-03 15:12:46 +02:00
|
|
|
}
|
2015-07-24 10:51:55 +02:00
|
|
|
}
|
2016-02-19 15:23:44 +01:00
|
|
|
|
|
|
|
foreach ($newProps as $prop => $value) {
|
|
|
|
// TODO: data type?
|
|
|
|
$object->set($prop, $value);
|
|
|
|
}
|
|
|
|
|
|
|
|
foreach ($newVars as $prop => $var) {
|
|
|
|
$object->vars()->$prop = $var;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (! empty($imports)) {
|
|
|
|
// TODO: merge imports!!!
|
|
|
|
$object->imports()->set($imports);
|
|
|
|
}
|
2015-07-24 10:51:55 +02:00
|
|
|
}
|
2015-07-26 15:46:25 +02:00
|
|
|
}
|
|
|
|
|
2016-02-19 15:23:44 +01:00
|
|
|
return $newObjects;
|
|
|
|
}
|
2015-07-24 15:30:09 +02:00
|
|
|
|
2016-02-19 15:23:44 +01:00
|
|
|
/**
|
|
|
|
* Evaluates a SyncRule and returns a list of modified objects
|
|
|
|
*
|
|
|
|
* TODO: This needs to be splitted into smaller methods
|
|
|
|
*
|
|
|
|
* @return array List of modified IcingaObjects
|
|
|
|
*/
|
2016-02-23 11:22:25 +01:00
|
|
|
protected function prepare()
|
2016-02-19 15:23:44 +01:00
|
|
|
{
|
2016-02-23 11:22:25 +01:00
|
|
|
$rule = $this->rule;
|
2016-02-23 13:03:47 +01:00
|
|
|
$sources = $this->perpareImportSources();
|
|
|
|
$imported = $this->fetchImportedData($sources);
|
2015-07-24 15:30:57 +02:00
|
|
|
|
2016-02-19 15:23:44 +01:00
|
|
|
// TODO: Make object_type (template, object...) and object_name mandatory?
|
2016-02-23 11:59:25 +01:00
|
|
|
$objects = IcingaObject::loadAllByType($rule->object_type, $this->db);
|
2016-02-19 15:23:44 +01:00
|
|
|
|
|
|
|
// TODO: should be obsoleted by a better "loadFiltered" method
|
|
|
|
if ($rule->object_type === 'datalistEntry') {
|
2016-02-23 13:03:47 +01:00
|
|
|
$this->removeForeignListEntries($objects);
|
2016-02-19 15:23:44 +01:00
|
|
|
}
|
|
|
|
|
2016-02-23 08:58:22 +01:00
|
|
|
// TODO: directly work on existing objects, remember imported keys, then purge
|
2016-02-19 15:23:44 +01:00
|
|
|
$newObjects = $this->prepareNewObjects(
|
|
|
|
$sources,
|
|
|
|
$imported
|
|
|
|
);
|
|
|
|
|
|
|
|
foreach ($newObjects as $key => $object) {
|
|
|
|
if (array_key_exists($key, $objects)) {
|
|
|
|
switch ($rule->update_policy) {
|
|
|
|
case 'override':
|
|
|
|
$objects[$key]->replaceWith($object);
|
2015-10-20 22:22:58 +02:00
|
|
|
break;
|
|
|
|
|
2016-02-19 15:23:44 +01:00
|
|
|
case 'merge':
|
2016-02-23 09:08:14 +01:00
|
|
|
// TODO: re-evaluate merge settings. vars.x instead of
|
|
|
|
// just "vars" might suffice.
|
|
|
|
$objects[$key]->setProperties($object);
|
2016-02-19 15:23:44 +01:00
|
|
|
break;
|
2015-07-24 15:29:17 +02:00
|
|
|
|
2016-02-19 15:23:44 +01:00
|
|
|
default:
|
|
|
|
// policy 'ignore', no action
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
$objects[$key] = $object;
|
2015-10-20 23:29:29 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-19 15:23:44 +01:00
|
|
|
$objectKey = $rule->object_type === 'datalistEntry' ? 'entry_name' : 'object_name';
|
2016-02-22 11:01:37 +01:00
|
|
|
$noAction = array();
|
|
|
|
|
2016-02-19 15:23:44 +01:00
|
|
|
foreach ($objects as $key => $object) {
|
|
|
|
|
2016-02-22 11:01:37 +01:00
|
|
|
if (array_key_exists($key, $newObjects)) {
|
|
|
|
// Stats?
|
2016-02-19 15:23:44 +01:00
|
|
|
|
2016-02-22 11:01:37 +01:00
|
|
|
} elseif ($object->hasBeenLoadedFromDb() && $rule->purge_existing === 'y') {
|
|
|
|
$object->markForRemoval();
|
|
|
|
|
|
|
|
// TODO: this is for stats, preview, summary:
|
|
|
|
// $this->remove[] = $object;
|
|
|
|
} else {
|
|
|
|
$noAction[] = $key;
|
2016-02-19 15:23:44 +01:00
|
|
|
}
|
2015-10-20 23:29:29 +02:00
|
|
|
}
|
|
|
|
|
2016-02-22 11:01:37 +01:00
|
|
|
foreach ($noAction as $key) {
|
|
|
|
unset($objects[$key]);
|
|
|
|
}
|
|
|
|
|
2015-10-20 23:29:29 +02:00
|
|
|
return $objects;
|
|
|
|
}
|
|
|
|
|
2015-12-08 11:51:05 +01:00
|
|
|
/**
|
|
|
|
* Runs a SyncRule and applies all resulting changes
|
|
|
|
*
|
|
|
|
* TODO: Should return the id of the related sync_history table entry.
|
|
|
|
* Such a table does not yet exist, so 42 is the answer right now.
|
|
|
|
*
|
|
|
|
* @return int
|
|
|
|
*/
|
2016-02-23 11:22:25 +01:00
|
|
|
protected function apply()
|
2015-10-20 23:29:29 +02:00
|
|
|
{
|
2016-02-23 11:22:25 +01:00
|
|
|
$rule = $this->rule;
|
|
|
|
$db = $this->db;
|
|
|
|
|
2015-10-20 23:29:29 +02:00
|
|
|
// TODO: Evaluate whether fetching data should happen within the same transaction
|
2016-02-23 13:41:19 +01:00
|
|
|
$objects = $this->prepare();
|
2015-12-15 19:09:22 +01:00
|
|
|
|
2015-10-20 23:29:29 +02:00
|
|
|
$dba = $db->getDbAdapter();
|
|
|
|
$dba->beginTransaction();
|
|
|
|
foreach ($objects as $object) {
|
|
|
|
if ($object instanceof IcingaObject && $object->isTemplate()) {
|
|
|
|
if ($object->hasBeenModified()) {
|
|
|
|
throw new IcingaException(
|
|
|
|
'Sync is not allowed to modify template "%s"',
|
|
|
|
$object->$objectKey
|
|
|
|
);
|
|
|
|
}
|
2015-10-20 22:22:58 +02:00
|
|
|
continue;
|
|
|
|
}
|
2016-02-09 19:21:36 +01:00
|
|
|
|
|
|
|
// TODO: introduce DirectorObject with shouldBeRemoved
|
|
|
|
if ($object instanceof IcingaObject && $object->shouldBeRemoved()) {
|
2015-12-04 10:24:54 +01:00
|
|
|
$object->delete($db);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2015-11-02 09:29:03 +01:00
|
|
|
if ($object->hasBeenModified()) {
|
|
|
|
$object->store($db);
|
|
|
|
}
|
2015-10-20 22:22:58 +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
|
|
|
}
|
|
|
|
}
|