Sync: cleanup, improve array handling, split logic
This should for example fix sync for multiple group memberships
This commit is contained in:
parent
8f71ca034d
commit
facaeb8aac
|
@ -23,56 +23,34 @@ use Icinga\Exception\IcingaException;
|
||||||
|
|
||||||
class Sync
|
class Sync
|
||||||
{
|
{
|
||||||
/**
|
/** @var SyncRule */
|
||||||
* @var SyncRule
|
|
||||||
*/
|
|
||||||
protected $rule;
|
protected $rule;
|
||||||
|
|
||||||
/**
|
/** @var Db */
|
||||||
* @var Db
|
|
||||||
*/
|
|
||||||
protected $db;
|
protected $db;
|
||||||
|
|
||||||
/**
|
/** @var array Related ImportSource objects */
|
||||||
* Related ImportSource objects
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected $sources;
|
protected $sources;
|
||||||
|
|
||||||
/**
|
/** @var array Source columns we want to fetch from our sources */
|
||||||
* Source columns we want to fetch from our sources
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected $sourceColumns;
|
protected $sourceColumns;
|
||||||
|
|
||||||
/**
|
/** @var array Imported data */
|
||||||
* Imported data
|
|
||||||
*/
|
|
||||||
protected $imported;
|
protected $imported;
|
||||||
|
|
||||||
/**
|
/** @var IcingaObject[] Objects to work with */
|
||||||
* Objects to work with
|
|
||||||
*
|
|
||||||
* @var IcingaObject[]
|
|
||||||
*/
|
|
||||||
protected $objects;
|
protected $objects;
|
||||||
|
|
||||||
/**
|
/** @var bool Whether we already prepared your sync */
|
||||||
* Whether we already prepared your sync
|
|
||||||
*
|
|
||||||
* @var bool
|
|
||||||
*/
|
|
||||||
protected $isPrepared = false;
|
protected $isPrepared = false;
|
||||||
|
|
||||||
protected $modify = array();
|
protected $modify = [];
|
||||||
|
|
||||||
protected $remove = array();
|
protected $remove = [];
|
||||||
|
|
||||||
protected $create = array();
|
protected $create = [];
|
||||||
|
|
||||||
protected $errors = array();
|
protected $errors = [];
|
||||||
|
|
||||||
/** @var SyncProperty[] */
|
/** @var SyncProperty[] */
|
||||||
protected $syncProperties;
|
protected $syncProperties;
|
||||||
|
@ -91,15 +69,12 @@ class Sync
|
||||||
protected $runStartTime;
|
protected $runStartTime;
|
||||||
|
|
||||||
/** @var Filter[] */
|
/** @var Filter[] */
|
||||||
protected $columnFilters = array();
|
protected $columnFilters = [];
|
||||||
|
|
||||||
/** @var HostGroupMembershipResolver|bool */
|
/** @var HostGroupMembershipResolver|bool */
|
||||||
protected $hostGroupMembershipResolver;
|
protected $hostGroupMembershipResolver;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor. No direct initialization allowed right now. Please use one
|
|
||||||
* of the available static factory methods
|
|
||||||
*
|
|
||||||
* @param SyncRule $rule
|
* @param SyncRule $rule
|
||||||
*/
|
*/
|
||||||
public function __construct(SyncRule $rule)
|
public function __construct(SyncRule $rule)
|
||||||
|
@ -112,6 +87,7 @@ class Sync
|
||||||
* Whether the given sync rule would apply modifications
|
* Whether the given sync rule would apply modifications
|
||||||
*
|
*
|
||||||
* @return boolean
|
* @return boolean
|
||||||
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
public function hasModifications()
|
public function hasModifications()
|
||||||
{
|
{
|
||||||
|
@ -126,7 +102,7 @@ class Sync
|
||||||
*/
|
*/
|
||||||
public function getExpectedModifications()
|
public function getExpectedModifications()
|
||||||
{
|
{
|
||||||
$modified = array();
|
$modified = [];
|
||||||
$objects = $this->prepare();
|
$objects = $this->prepare();
|
||||||
foreach ($objects as $object) {
|
foreach ($objects as $object) {
|
||||||
if ($object->hasBeenModified()) {
|
if ($object->hasBeenModified()) {
|
||||||
|
@ -151,9 +127,9 @@ class Sync
|
||||||
if (is_array($value)) {
|
if (is_array($value)) {
|
||||||
return $value;
|
return $value;
|
||||||
} elseif ($value === null) {
|
} elseif ($value === null) {
|
||||||
return array();
|
return [];
|
||||||
} else {
|
} else {
|
||||||
return array($value);
|
return [$value];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -225,10 +201,12 @@ class Sync
|
||||||
* Instantiates all related ImportSource objects
|
* Instantiates all related ImportSource objects
|
||||||
*
|
*
|
||||||
* @return self
|
* @return self
|
||||||
|
* @throws IcingaException
|
||||||
|
* @throws \Icinga\Exception\NotFoundError
|
||||||
*/
|
*/
|
||||||
protected function prepareRelatedImportSources()
|
protected function prepareRelatedImportSources()
|
||||||
{
|
{
|
||||||
$this->sources = array();
|
$this->sources = [];
|
||||||
foreach ($this->syncProperties as $p) {
|
foreach ($this->syncProperties as $p) {
|
||||||
$id = $p->source_id;
|
$id = $p->source_id;
|
||||||
if (! array_key_exists($id, $this->sources)) {
|
if (! array_key_exists($id, $this->sources)) {
|
||||||
|
@ -246,13 +224,13 @@ class Sync
|
||||||
*/
|
*/
|
||||||
protected function prepareSourceColumns()
|
protected function prepareSourceColumns()
|
||||||
{
|
{
|
||||||
// $fieldMap = array();
|
// $fieldMap = [];
|
||||||
$this->sourceColumns = array();
|
$this->sourceColumns = [];
|
||||||
|
|
||||||
foreach ($this->syncProperties as $p) {
|
foreach ($this->syncProperties as $p) {
|
||||||
$sourceId = $p->source_id;
|
$sourceId = $p->source_id;
|
||||||
if (! array_key_exists($sourceId, $this->sourceColumns)) {
|
if (! array_key_exists($sourceId, $this->sourceColumns)) {
|
||||||
$this->sourceColumns[$sourceId] = array();
|
$this->sourceColumns[$sourceId] = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (SyncUtils::extractVariableNames($p->source_expression) as $varname) {
|
foreach (SyncUtils::extractVariableNames($p->source_expression) as $varname) {
|
||||||
|
@ -276,7 +254,7 @@ class Sync
|
||||||
$this->serviceOverrideKeyName = $this->db->settings()->override_services_varname;
|
$this->serviceOverrideKeyName = $this->db->settings()->override_services_varname;
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->imported = array();
|
$this->imported = [];
|
||||||
|
|
||||||
$sourceKeyPattern = $this->rule->getSourceKeyPattern();
|
$sourceKeyPattern = $this->rule->getSourceKeyPattern();
|
||||||
$combinedKey = $this->rule->hasCombinedKey();
|
$combinedKey = $this->rule->hasCombinedKey();
|
||||||
|
@ -292,7 +270,7 @@ class Sync
|
||||||
|
|
||||||
$usedColumns = SyncUtils::getRootVariables($this->sourceColumns[$sourceId]);
|
$usedColumns = SyncUtils::getRootVariables($this->sourceColumns[$sourceId]);
|
||||||
|
|
||||||
$filterColumns = array();
|
$filterColumns = [];
|
||||||
foreach ($this->columnFilters as $filter) {
|
foreach ($this->columnFilters as $filter) {
|
||||||
foreach ($filter->listFilteredColumns() as $column) {
|
foreach ($filter->listFilteredColumns() as $column) {
|
||||||
$filterColumns[$column] = $column;
|
$filterColumns[$column] = $column;
|
||||||
|
@ -314,7 +292,7 @@ class Sync
|
||||||
$rows = $run->fetchRows($usedColumns);
|
$rows = $run->fetchRows($usedColumns);
|
||||||
Benchmark::measure(sprintf('Fetched source %s', $source->source_name));
|
Benchmark::measure(sprintf('Fetched source %s', $source->source_name));
|
||||||
|
|
||||||
$this->imported[$sourceId] = array();
|
$this->imported[$sourceId] = [];
|
||||||
foreach ($rows as $row) {
|
foreach ($rows as $row) {
|
||||||
if ($combinedKey) {
|
if ($combinedKey) {
|
||||||
$key = SyncUtils::fillVariables($sourceKeyPattern, $row);
|
$key = SyncUtils::fillVariables($sourceKeyPattern, $row);
|
||||||
|
@ -358,7 +336,11 @@ class Sync
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: This is rubbish, we need to filter at fetch time
|
/**
|
||||||
|
* TODO: This is rubbish, we need to filter at fetch time
|
||||||
|
*
|
||||||
|
* @throws IcingaException
|
||||||
|
*/
|
||||||
protected function removeForeignListEntries()
|
protected function removeForeignListEntries()
|
||||||
{
|
{
|
||||||
$listId = null;
|
$listId = null;
|
||||||
|
@ -374,7 +356,7 @@ class Sync
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
$no = array();
|
$no = [];
|
||||||
foreach ($this->objects as $k => $o) {
|
foreach ($this->objects as $k => $o) {
|
||||||
if ((int) $o->list_id !== (int) $listId) {
|
if ((int) $o->list_id !== (int) $listId) {
|
||||||
$no[] = $k;
|
$no[] = $k;
|
||||||
|
@ -386,13 +368,17 @@ class Sync
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return $this
|
||||||
|
* @throws IcingaException
|
||||||
|
*/
|
||||||
protected function loadExistingObjects()
|
protected function loadExistingObjects()
|
||||||
{
|
{
|
||||||
Benchmark::measure('Begin loading existing objects');
|
Benchmark::measure('Begin loading existing objects');
|
||||||
|
|
||||||
// TODO: Make object_type (template, object...) and object_name mandatory?
|
// TODO: Make object_type (template, object...) and object_name mandatory?
|
||||||
if ($this->rule->hasCombinedKey()) {
|
if ($this->rule->hasCombinedKey()) {
|
||||||
$this->objects = array();
|
$this->objects = [];
|
||||||
$destinationKeyPattern = $this->rule->getDestinationKeyPattern();
|
$destinationKeyPattern = $this->rule->getDestinationKeyPattern();
|
||||||
|
|
||||||
foreach (IcingaObject::loadAllByType(
|
foreach (IcingaObject::loadAllByType(
|
||||||
|
@ -439,98 +425,106 @@ class Sync
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array
|
||||||
|
* @throws IcingaException
|
||||||
|
* @throws \Icinga\Exception\NotFoundError
|
||||||
|
* @throws \Icinga\Exception\ProgrammingError
|
||||||
|
*/
|
||||||
protected function prepareNewObjects()
|
protected function prepareNewObjects()
|
||||||
{
|
{
|
||||||
$newObjects = array();
|
$objects = [];
|
||||||
|
|
||||||
foreach ($this->sources as $source) {
|
foreach ($this->sources as $source) {
|
||||||
$sourceId = $source->id;
|
$sourceId = $source->id;
|
||||||
|
|
||||||
foreach ($this->imported[$sourceId] as $key => $row) {
|
foreach ($this->imported[$sourceId] as $key => $row) {
|
||||||
$newProps = array();
|
if (! array_key_exists($key, $objects)) {
|
||||||
|
// Safe default values for object_type and object_name
|
||||||
$newVars = array();
|
if ($this->rule->object_type === 'datalistEntry') {
|
||||||
$imports = array();
|
$props = [];
|
||||||
|
|
||||||
foreach ($this->syncProperties as $propertyKey => $p) {
|
|
||||||
if ($p->source_id !== $sourceId) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (! $this->rowMatchesPropertyFilter($row, $propertyKey)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$prop = $p->destination_field;
|
|
||||||
|
|
||||||
$val = SyncUtils::fillVariables($p->source_expression, $row);
|
|
||||||
|
|
||||||
if (substr($prop, 0, 5) === 'vars.') {
|
|
||||||
$varName = substr($prop, 5);
|
|
||||||
if (substr($varName, -2) === '[]') {
|
|
||||||
$varName = substr($varName, 0, -2);
|
|
||||||
$val = $this->wantArray($val);
|
|
||||||
}
|
|
||||||
$newVars[$varName] = $val;
|
|
||||||
} else {
|
} else {
|
||||||
if ($prop === 'import') {
|
$props = [
|
||||||
if (is_array($val)) {
|
'object_type' => 'object',
|
||||||
$imports = array_merge($imports, $val);
|
'object_name' => $key
|
||||||
} elseif (!is_null($val)) {
|
];
|
||||||
$imports[] = $val;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$newProps[$prop] = $val;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if (! array_key_exists($key, $newObjects)) {
|
$objects[$key] = IcingaObject::createByType(
|
||||||
$newObjects[$key] = IcingaObject::createByType(
|
|
||||||
$this->rule->object_type,
|
$this->rule->object_type,
|
||||||
array(),
|
$props,
|
||||||
$this->db
|
$this->db
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
$object = $newObjects[$key];
|
$object = $objects[$key];
|
||||||
|
$this->prepareNewObject($row, $object, $sourceId);
|
||||||
// Safe default values for object_type and object_name
|
|
||||||
if ($this->rule->object_type !== 'datalistEntry') {
|
|
||||||
if (! array_key_exists('object_type', $newProps)
|
|
||||||
|| $newProps['object_type'] === null
|
|
||||||
) {
|
|
||||||
$newProps['object_type'] = 'object';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (! array_key_exists('object_name', $newProps)
|
|
||||||
|| $newProps['object_name'] === null
|
|
||||||
) {
|
|
||||||
$newProps['object_name'] = $key;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return $newObjects;
|
return $objects;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param $row
|
||||||
|
* @param DbObject $object
|
||||||
|
* @param $sourceId
|
||||||
|
* @throws IcingaException
|
||||||
|
* @throws \Icinga\Exception\NotFoundError
|
||||||
|
* @throws \Icinga\Exception\ProgrammingError
|
||||||
|
*/
|
||||||
|
protected function prepareNewObject($row, DbObject $object, $sourceId)
|
||||||
|
{
|
||||||
|
foreach ($this->syncProperties as $propertyKey => $p) {
|
||||||
|
if ($p->source_id !== $sourceId) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! $this->rowMatchesPropertyFilter($row, $propertyKey)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$prop = $p->destination_field;
|
||||||
|
|
||||||
|
$val = SyncUtils::fillVariables($p->source_expression, $row);
|
||||||
|
|
||||||
|
if ($object instanceof IcingaObject) {
|
||||||
|
if ($prop === 'import') {
|
||||||
|
if ($val !== null) {
|
||||||
|
$object->imports()->add($val);
|
||||||
|
}
|
||||||
|
} elseif ($prop === 'groups') {
|
||||||
|
if ($val !== null) {
|
||||||
|
$object->groups()->add($val);
|
||||||
|
}
|
||||||
|
} elseif (substr($prop, 0, 5) === 'vars.') {
|
||||||
|
$varName = substr($prop, 5);
|
||||||
|
if (substr($varName, -2) === '[]') {
|
||||||
|
$varName = substr($varName, 0, -2);
|
||||||
|
$current = $this->wantArray($object->vars()->$varName);
|
||||||
|
$object->vars()->$varName = array_merge(
|
||||||
|
$current,
|
||||||
|
$this->wantArray($val)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
$object->vars()->$varName = $val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if ($val !== null) {
|
||||||
|
$object->set($prop, $val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return $this
|
||||||
|
* @throws IcingaException
|
||||||
|
*/
|
||||||
protected function deferResolvers()
|
protected function deferResolvers()
|
||||||
{
|
{
|
||||||
if (in_array($this->rule->get('object_type'), array('host', 'hostgroup'))) {
|
if (in_array($this->rule->get('object_type'), ['host', 'hostgroup'])) {
|
||||||
$resolver = $this->getHostGroupMembershipResolver();
|
$resolver = $this->getHostGroupMembershipResolver();
|
||||||
$resolver->defer()->setUseTransactions(false);
|
$resolver->defer()->setUseTransactions(false);
|
||||||
}
|
}
|
||||||
|
@ -541,6 +535,7 @@ class Sync
|
||||||
/**
|
/**
|
||||||
* @param DbObject $object
|
* @param DbObject $object
|
||||||
* @return $this
|
* @return $this
|
||||||
|
* @throws IcingaException
|
||||||
*/
|
*/
|
||||||
protected function setResolver($object)
|
protected function setResolver($object)
|
||||||
{
|
{
|
||||||
|
@ -554,6 +549,10 @@ class Sync
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return $this
|
||||||
|
* @throws IcingaException
|
||||||
|
*/
|
||||||
protected function notifyResolvers()
|
protected function notifyResolvers()
|
||||||
{
|
{
|
||||||
if ($resolver = $this->getHostGroupMembershipResolver()) {
|
if ($resolver = $this->getHostGroupMembershipResolver()) {
|
||||||
|
@ -565,13 +564,14 @@ class Sync
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return bool|HostGroupMembershipResolver
|
* @return bool|HostGroupMembershipResolver
|
||||||
|
* @throws IcingaException
|
||||||
*/
|
*/
|
||||||
protected function getHostGroupMembershipResolver()
|
protected function getHostGroupMembershipResolver()
|
||||||
{
|
{
|
||||||
if ($this->hostGroupMembershipResolver === null) {
|
if ($this->hostGroupMembershipResolver === null) {
|
||||||
if (in_array(
|
if (in_array(
|
||||||
$this->rule->get('object_type'),
|
$this->rule->get('object_type'),
|
||||||
array('host', 'hostgroup')
|
['host', 'hostgroup']
|
||||||
)) {
|
)) {
|
||||||
$this->hostGroupMembershipResolver = new HostGroupMembershipResolver(
|
$this->hostGroupMembershipResolver = new HostGroupMembershipResolver(
|
||||||
$this->db
|
$this->db
|
||||||
|
@ -618,7 +618,7 @@ class Sync
|
||||||
}
|
}
|
||||||
|
|
||||||
Benchmark::measure('Modified objects are ready, applying purge strategy');
|
Benchmark::measure('Modified objects are ready, applying purge strategy');
|
||||||
$noAction = array();
|
$noAction = [];
|
||||||
foreach ($this->rule->purgeStrategy()->listObjectsToPurge() as $key) {
|
foreach ($this->rule->purgeStrategy()->listObjectsToPurge() as $key) {
|
||||||
if (array_key_exists($key, $newObjects)) {
|
if (array_key_exists($key, $newObjects)) {
|
||||||
// Object has been touched, do not delete
|
// Object has been touched, do not delete
|
||||||
|
@ -759,11 +759,11 @@ class Sync
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$runProperties = array(
|
$runProperties = [
|
||||||
'objects_created' => $created,
|
'objects_created' => $created,
|
||||||
'objects_deleted' => $deleted,
|
'objects_deleted' => $deleted,
|
||||||
'objects_modified' => $modified,
|
'objects_modified' => $modified,
|
||||||
);
|
];
|
||||||
|
|
||||||
if ($created + $deleted + $modified > 0) {
|
if ($created + $deleted + $modified > 0) {
|
||||||
// TODO: What if this has been the very first activity?
|
// TODO: What if this has been the very first activity?
|
||||||
|
|
Loading…
Reference in New Issue