diff --git a/dictionaries/dictionary.itop.core.php b/dictionaries/dictionary.itop.core.php index dddbfb3f4..d53457694 100644 --- a/dictionaries/dictionary.itop.core.php +++ b/dictionaries/dictionary.itop.core.php @@ -525,6 +525,7 @@ Dict::Add('EN US', 'English', 'English', array( 'Core:Synchro:label_obj_deleted' => 'Deleted (%1$s)', 'Core:Synchro:label_obj_obsoleted' => 'Obsoleted (%1$s)', 'Core:Synchro:label_obj_disappeared_errors' => 'Errors (%1$s)', + 'Core:Synchro:label_obj_disappeared_no_action' => 'No Action (%1$s)', 'Core:Synchro:label_obj_unchanged' => 'Unchanged (%1$s)', 'Core:Synchro:label_obj_updated' => 'Updated (%1$s)', 'Core:Synchro:label_obj_updated_errors' => 'Errors (%1$s)', @@ -534,7 +535,7 @@ Dict::Add('EN US', 'English', 'English', array( 'Core:Synchro:label_obj_new_errors' => 'Errors (%1$s)', 'Core:Synchro:History' => 'Synchronization History', 'Core:SynchroLogTitle' => '%1$s - %2$s', - 'Core:Synchro:Nb_Replica' => 'Replica: %1$s', + 'Core:Synchro:Nb_Replica' => 'Replica processed: %1$s', 'Core:Synchro:Nb_Class:Objects' => '%1$s: %2$s', )); ?> diff --git a/synchro/synchro_exec.php b/synchro/synchro_exec.php index 3d4c67520..80a39b2a0 100644 --- a/synchro/synchro_exec.php +++ b/synchro/synchro_exec.php @@ -136,7 +136,7 @@ foreach(explode(',', $sDataSourcesList) as $iSDS) } foreach ($aResults as $sMessage) { - $oP->p($sMessage); + $oP->p('#'.$sMessage); } if ($oStatLog->Get('status') == 'error') { @@ -152,9 +152,10 @@ foreach(explode(',', $sDataSourcesList) as $iSDS) $oP->p("Objects creation errors: ".$oStatLog->Get('stats_nb_obj_created_errors')); $oP->p("Objects updated: ".$oStatLog->Get('stats_nb_obj_updated')); $oP->p("Objects update errors: ".$oStatLog->Get('stats_nb_obj_updated_errors')); - $oP->p("Objects reconciled (updated): ".$oStatLog->Get('stats_nb_new_updated')); - $oP->p("Objects reconciled (unchanged): ".$oStatLog->Get('stats_nb_new_unchanged')); + $oP->p("Objects reconciled (updated): ".$oStatLog->Get('stats_nb_obj_new_updated')); + $oP->p("Objects reconciled (unchanged): ".$oStatLog->Get('stats_nb_obj_new_unchanged')); $oP->p("Objects reconciliation errors: ".$oStatLog->Get('stats_nb_replica_reconciled_errors')); + $oP->p("Replica disappeared, no action taken: ".$oStatLog->Get('stats_nb_replica_disappeared_no_action')); } catch(Exception $e) { diff --git a/synchro/synchro_import.php b/synchro/synchro_import.php index 82ac3f6d1..c259ca20c 100644 --- a/synchro/synchro_import.php +++ b/synchro/synchro_import.php @@ -374,6 +374,9 @@ try // try { + $oP->add_comment('Load--------------'); + $oP->add_comment('------------------'); + if ($bSimulate) { CMDBSource::Query('START TRANSACTION'); @@ -444,19 +447,62 @@ try } } + if (($sOutput == "summary") || ($sOutput == 'details')) + { + $oP->add_comment("Data Source: ".$iDataSourceId); + $oP->add_comment("Synchronize: ".($bSynchronize ? '1' : '0')); + $oP->add_comment("Class: ".$sClass); + $oP->add_comment("Separator: ".$sSep); + $oP->add_comment("Qualifier: ".$sQualifier); + $oP->add_comment("Charset Encoding:".$sCharSet); + $oP->add_comment("Data Size: ".strlen($sCSVData)); + $oP->add_comment("Data Lines: ".$iLineCount); + $oP->add_comment("Columns: ".implode(', ', $aInputColumns)); + $oP->add_comment("Output format: ".$sOutput); + // $oP->add_comment("Report level: ".$sReportLevel); + $oP->add_comment("Simulate: ".($bSimulate ? '1' : '0')); + $oP->add_comment("Change tracking comment: ".$sComment); + $oP->add_comment("Issues (before synchro): ".$iCountErrors); + // $oP->add_comment("Warnings: ".$iCountWarnings); + $oP->add_comment("Created (before synchro): ".$iCountCreations); + $oP->add_comment("Updated (before synchro): ".$iCountUpdates); + } + ////////////////////////////////////////////////// // // Synchronize // if ($bSynchronize) { - $aTraces = array(); - $oStatLog = $oDataSource->Synchronize($aTraces, $oLoadStartDate); - //echo "#@# Synchronize() returned :
\n"; - //echo "
\n";
-			//print_r($aTraces);
-			//print_r($oStatLog);
-			//echo "
\n"; + $aResults = array(); + $oStatLog = $oDataSource->Synchronize($aResults, $oLoadStartDate); + $oP->add_comment('Synchronization---'); + $oP->add_comment('------------------'); + if ($sOutput == 'details') + { + foreach ($aResults as $sMessage) + { + $oP->add_comment($sMessage); + } + } + if ($oStatLog->Get('status') == 'error') + { + $oP->p("ERROR: ".$oStatLog->Get('last_error')); + } + $oP->add_comment("Replicas: ".$oStatLog->Get('stats_nb_replica_total')); + $oP->add_comment("Replicas touched since last synchro: ".$oStatLog->Get('stats_nb_replica_seen')); + $oP->add_comment("Objects deleted: ".$oStatLog->Get('stats_nb_obj_deleted')); + $oP->add_comment("Objects deletion errors: ".$oStatLog->Get('stats_nb_obj_deleted_errors')); + $oP->add_comment("Objects obsoleted: ".$oStatLog->Get('stats_nb_obj_obsoleted')); + $oP->add_comment("Objects obsolescence errors: ".$oStatLog->Get('stats_nb_obj_obsoleted_errors')); + $oP->add_comment("Objects created: ".$oStatLog->Get('stats_nb_obj_created')); + $oP->add_comment("Objects creation errors: ".$oStatLog->Get('stats_nb_obj_created_errors')); + $oP->add_comment("Objects updated: ".$oStatLog->Get('stats_nb_obj_updated')); + $oP->add_comment("Objects update errors: ".$oStatLog->Get('stats_nb_obj_updated_errors')); + $oP->add_comment("Objects reconciled (updated): ".$oStatLog->Get('stats_nb_obj_new_updated')); + $oP->add_comment("Objects reconciled (unchanged): ".$oStatLog->Get('stats_nb_obj_new_unchanged')); + $oP->add_comment("Objects reconciliation errors: ".$oStatLog->Get('stats_nb_replica_reconciled_errors')); + $oP->add_comment("Replica disappeared, no action taken: ".$oStatLog->Get('stats_nb_replica_disappeared_no_action')); } } catch(Exception $e) @@ -480,27 +526,6 @@ try { $oP->add($iCountErrors); } - - if (($sOutput == "summary") || ($sOutput == 'details')) - { - $oP->add_comment("Data Source: ".$iDataSourceId); - $oP->add_comment("Synchronize: ".($bSynchronize ? '1' : '0')); - $oP->add_comment("Class: ".$sClass); - $oP->add_comment("Separator: ".$sSep); - $oP->add_comment("Qualifier: ".$sQualifier); - $oP->add_comment("Charset Encoding:".$sCharSet); - $oP->add_comment("Data Size: ".strlen($sCSVData)); - $oP->add_comment("Data Lines: ".$iLineCount); - $oP->add_comment("Columns: ".implode(', ', $aInputColumns)); - $oP->add_comment("Output format: ".$sOutput); -// $oP->add_comment("Report level: ".$sReportLevel); - $oP->add_comment("Simulate: ".($bSimulate ? '1' : '0')); - $oP->add_comment("Change tracking comment: ".$sComment); - $oP->add_comment("Issues: ".$iCountErrors); -// $oP->add_comment("Warnings: ".$iCountWarnings); - $oP->add_comment("Created: ".$iCountCreations); - $oP->add_comment("Updated: ".$iCountUpdates); - } } catch(ExchangeException $e) { diff --git a/synchro/synchrodatasource.class.inc.php b/synchro/synchrodatasource.class.inc.php index 2aa3de2d7..aa126e321 100644 --- a/synchro/synchrodatasource.class.inc.php +++ b/synchro/synchrodatasource.class.inc.php @@ -228,7 +228,9 @@ EOF ); $oPage->add($this->HtmlBox('repl_ignored', $aData, '#999').' '); $oPage->add("\n"); - $oPage->add($this->HtmlBox('repl_disappeared', $aData, '#630', 'rowspan="3"').'=>'.$this->HtmlBox('obj_deleted', $aData, '#000')); + $oPage->add($this->HtmlBox('repl_disappeared', $aData, '#630', 'rowspan="4"').'=>'.$this->HtmlBox('obj_disappeared_no_action', $aData, '#333')); + $oPage->add("\n"); + $oPage->add($this->HtmlBox('obj_deleted', $aData, '#000')); $oPage->add("\n"); $oPage->add($this->HtmlBox('obj_obsoleted', $aData, '#630')); $oPage->add("\n"); @@ -271,6 +273,7 @@ EOF 'obj_deleted' => $oLastLog->Get('stats_nb_obj_deleted'), 'obj_obsoleted' => $oLastLog->Get('stats_nb_obj_obsoleted'), 'obj_disappeared_errors' => $oLastLog->Get('stats_nb_obj_obsoleted_errors') + $oLastLog->Get('stats_nb_obj_deleted_errors'), + 'obj_disappeared_no_action' => $oLastLog->Get('stats_nb_replica_disappeared_no_action'), 'obj_updated' => $oLastLog->Get('stats_nb_obj_updated'), 'obj_updated_errors' => $oLastLog->Get('stats_nb_obj_updated_errors'), 'obj_new_updated' => $oLastLog->Get('stats_nb_obj_new_updated'), @@ -279,7 +282,7 @@ EOF 'obj_created_errors' => $oLastLog->Get('stats_nb_obj_created_errors'), ); $iReconciledErrors = $oLastLog->Get('stats_nb_replica_reconciled_errors'); - $iDisappeared = $aData['obj_disappeared_errors'] + $aData['obj_obsoleted'] + $aData['obj_deleted']; + $iDisappeared = $aData['obj_disappeared_errors'] + $aData['obj_obsoleted'] + $aData['obj_deleted'] + $aData['obj_disappeared_no_action']; $aData['repl_disappeared'] = $iDisappeared; $iNewErrors = $aData['obj_created_errors'] + $oLastLog->Get('stats_nb_replica_reconciled_errors'); $aData['obj_new_errors'] = $iNewErrors; @@ -499,6 +502,7 @@ EOF $oStatLog->Set('stats_nb_obj_updated_errors', 0); // $oStatLog->Set('stats_nb_replica_reconciled', 0); $oStatLog->Set('stats_nb_replica_reconciled_errors', 0); + $oStatLog->Set('stats_nb_replica_disappeared_no_action', 0); $oStatLog->Set('stats_nb_obj_new_updated', 0); $oStatLog->Set('stats_nb_obj_new_unchanged',0); @@ -545,6 +549,8 @@ EOF throw new SynchroExceptionNotStarted(Dict::S('Core:SyncDataSourceAccessRestriction')); } + $sDeletePolicy = $this->Get('delete_policy'); + // Count the replicas $sSelectAll = "SELECT SynchroReplica WHERE sync_source_id = :source_id"; $oSetAll = new DBObjectSet(DBObjectSearch::FromOQL($sSelectAll), array() /* order by*/, array('source_id' => $this->GetKey())); @@ -584,10 +590,10 @@ EOF } while($oReplica = $oSetToObsolete->Fetch()) { - // TO DO: take the appropriate action based on the 'delete_policy' field - $sUpdateOnObsolete = $this->Get('delete_policy'); - if ( ($sUpdateOnObsolete == 'update') || ($sUpdateOnObsolete == 'update_then_delete') ) + switch ($sDeletePolicy) { + case 'update': + case 'update_then_delete': $aTraces[] = "Destination object: (dest_id:".$oReplica->Get('dest_id').") to be updated"; $aToUpdate = array(); $aToUpdate = explode(';', $this->Get('delete_policy_update')); //ex: 'status:obsolete;description:stopped', @@ -601,10 +607,16 @@ EOF $aToUpdate[$sAttCode] = $sValue; } } - $oReplica->UpdateDestObject($aToUpdate, $oMyChange, $oStatLog, $aTraces, 'stats_nb_obj_obsoleted'); + $oReplica->UpdateDestObject($aToUpdate, $oMyChange, $oStatLog, $aTraces); + $oReplica->Set('status', 'obsolete'); + $oReplica->DBUpdateTracked($oMyChange); + break; + + case 'delete': + default: + $aTraces[] = "Destination object: (dest_id:".$oReplica->Get('dest_id').") to be DELETED"; + $oReplica->DeleteDestObject($oMyChange, $oStatLog, $aTraces); } - $oReplica->Set('status', 'obsolete'); - $oReplica->DBUpdateTracked($oMyChange); } //Count "seen" objects @@ -674,37 +686,34 @@ EOF // Get all the replicas that are to be deleted // - $oDeletionDate = $oLastFullLoadStartDate; - $sDeleteRetention = trim($this->Get('delete_policy_retention')); - if (strlen($sDeleteRetention) > 0) + if ($sDeletePolicy == 'update_then_delete') { - $sInterval = '-'.$sDeleteRetention; - // Note: the PHP doc states that Modify return FALSE in case of error - // but, this is actually NOT the case - // Therefore, I do compare before and after, considering that the - // format is incorrect when the datetime remains unchanged - $sBefore = $oDeletionDate->Format('Y-m-d H:i:s'); - $oDeletionDate->Modify($sInterval); - $sAfter = $oDeletionDate->Format('Y-m-d H:i:s'); - if ($sBefore == $sAfter) + $oDeletionDate = $oLastFullLoadStartDate; + $sDeleteRetention = trim($this->Get('delete_policy_retention')); // MUST NOT BE NULL + if (strlen($sDeleteRetention) > 0) { - throw new SynchroExceptionNotStarted("Data exchange: Wrong interval specification", array('interval' => $sInterval, 'source_id' => $this->GetKey())); + $sInterval = '-'.$sDeleteRetention; + // Note: the PHP doc states that Modify return FALSE in case of error + // but, this is actually NOT the case + // Therefore, I do compare before and after, considering that the + // format is incorrect when the datetime remains unchanged + $sBefore = $oDeletionDate->Format('Y-m-d H:i:s'); + $oDeletionDate->Modify($sInterval); + $sAfter = $oDeletionDate->Format('Y-m-d H:i:s'); + if ($sBefore == $sAfter) + { + throw new SynchroExceptionNotStarted("Data exchange: Wrong interval specification", array('interval' => $sInterval, 'source_id' => $this->GetKey())); + } } - } - $sDeletionDate = $oDeletionDate->Format('Y-m-d H:i:s'); - $aTraces[] = "sDeletionDate: $sDeletionDate"; - $sSelectToDelete = "SELECT SynchroReplica WHERE sync_source_id = :source_id AND status IN ('obsolete') AND status_last_seen < :last_import"; - $oSetToDelete = new DBObjectSet(DBObjectSearch::FromOQL($sSelectToDelete), array() /* order by*/, array('source_id' => $this->GetKey(), 'last_import' => $sDeletionDate)); - while($oReplica = $oSetToDelete->Fetch()) - { - $sUpdateOnObsolete = $this->Get('delete_policy'); - if ( ($sUpdateOnObsolete == 'delete') || ($sUpdateOnObsolete == 'update_then_delete') ) + $sDeletionDate = $oDeletionDate->Format('Y-m-d H:i:s'); + $aTraces[] = "Deletion date: $sDeletionDate"; + $sSelectToDelete = "SELECT SynchroReplica WHERE sync_source_id = :source_id AND status IN ('obsolete') AND status_last_seen < :last_import"; + $oSetToDelete = new DBObjectSet(DBObjectSearch::FromOQL($sSelectToDelete), array() /* order by*/, array('source_id' => $this->GetKey(), 'last_import' => $sDeletionDate)); + while($oReplica = $oSetToDelete->Fetch()) { $aTraces[] = "Destination object: (dest_id:".$oReplica->Get('dest_id').") to be DELETED"; $oReplica->DeleteDestObject($oMyChange, $oStatLog, $aTraces); } - $aTraces[] = "Replica id:".$oReplica->GetKey()." (dest_id:".$oReplica->Get('dest_id').") to be deleted"; - $oReplica->DBDeleteTracked($oMyChange); } } @@ -924,6 +933,8 @@ class SynchroLog extends cmdbAbstractObject MetaModel::Init_AddAttribute(new AttributeInteger("stats_nb_obj_updated_errors", array("allowed_values"=>null, "sql"=>"stats_nb_obj_updated_errors", "default_value"=>0, "is_null_allowed"=>false, "depends_on"=>array()))); // MetaModel::Init_AddAttribute(new AttributeInteger("stats_nb_replica_reconciled", array("allowed_values"=>null, "sql"=>"stats_nb_replica_reconciled", "default_value"=>0, "is_null_allowed"=>false, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeInteger("stats_nb_replica_reconciled_errors", array("allowed_values"=>null, "sql"=>"stats_nb_replica_reconciled_errors", "default_value"=>0, "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeInteger("stats_nb_replica_disappeared_no_action", array("allowed_values"=>null, "sql"=>"stats_nb_replica_disappeared_no_action", "default_value"=>0, "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeInteger("stats_nb_obj_new_updated", array("allowed_values"=>null, "sql"=>"stats_nb_obj_new_updated", "default_value"=>0, "is_null_allowed"=>false, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeInteger("stats_nb_obj_new_unchanged", array("allowed_values"=>null, "sql"=>"stats_nb_obj_new_unchanged", "default_value"=>0, "is_null_allowed"=>false, "depends_on"=>array()))); @@ -931,7 +942,7 @@ class SynchroLog extends cmdbAbstractObject // Display lists MetaModel::Init_SetZListItems('details', array('sync_source_id', 'start_date', 'end_date', 'status', 'stats_nb_replica_total', 'stats_nb_replica_seen', 'stats_nb_obj_created', /*'stats_nb_replica_reconciled',*/ 'stats_nb_obj_updated', 'stats_nb_obj_obsoleted', 'stats_nb_obj_deleted', - 'stats_nb_obj_created_errors', 'stats_nb_replica_reconciled_errors', 'stats_nb_obj_updated_errors', 'stats_nb_obj_obsoleted_errors', 'stats_nb_obj_deleted_errors', 'stats_nb_obj_new_unchanged', 'stats_nb_obj_new_updated')); // Attributes to be displayed for the complete details + 'stats_nb_obj_created_errors', 'stats_nb_replica_reconciled_errors', 'stats_nb_replica_disappeared_no_action', 'stats_nb_obj_updated_errors', 'stats_nb_obj_obsoleted_errors', 'stats_nb_obj_deleted_errors', 'stats_nb_obj_new_unchanged', 'stats_nb_obj_new_updated')); // Attributes to be displayed for the complete details MetaModel::Init_SetZListItems('list', array('sync_source_id', 'start_date', 'end_date', 'status', 'stats_nb_replica_seen')); // Attributes to be displayed for a list // Search criteria // MetaModel::Init_SetZListItems('standard_search', array('name')); // Criteria of the std search form @@ -1032,6 +1043,7 @@ class SynchroReplica extends DBObject switch($this->Get('status')) { case 'new': + $this->Set('status_dest_creator', false); // If needed, construct the query used for the reconciliation if (!isset(self::$aSearches[$oDataSource->GetKey()])) { @@ -1092,10 +1104,7 @@ class SynchroReplica extends DBObject $oDestObj = $oDestSet->Fetch(); $this->UpdateObjectFromReplica($oDestObj, $aAttributes, $oChange, $oStatLog, $aTraces, 'stats_nb_obj_new', 'stats_nb_replica_reconciled_errors'); $this->Set('dest_id', $oDestObj->GetKey()); - $this->Set('status_dest_creator', false); $this->Set('dest_class', get_class($oDestObj)); - - $oStatLog->Inc('stats_nb_replica_reconciled'); //@@@ } else { @@ -1124,7 +1133,6 @@ class SynchroReplica extends DBObject $oDestObj = $oDestSet->Fetch(); $this->UpdateObjectFromReplica($oDestObj, $aAttributes, $oChange, $oStatLog, $aTraces, 'stats_nb_obj_new', 'stats_nb_replica_reconciled_errors'); $this->Set('dest_id', $oDestObj->GetKey()); - $this->Set('status_dest_creator', false); $this->Set('dest_class', get_class($oDestObj)); } } @@ -1230,23 +1238,31 @@ class SynchroReplica extends DBObject /** * Update the destination object with given values */ - public function UpdateDestObject($aValues, $oChange, &$oStatLog, &$aTraces, $sStatCode) + public function UpdateDestObject($aValues, $oChange, &$oStatLog, &$aTraces) { try { - $oDestObj = MetaModel::GetObject($this->Get('dest_class'), $this->Get('dest_id')); - foreach($aValues as $sAttCode => $value) + if ($this->Get('dest_class') == '') { - $oDestObj->Set($sAttCode, $value); + $this->SetLastError('No destination object to update'); + $oStatLog->Inc('stats_nb_obj_obsoleted_errors'); + } + else + { + $oDestObj = MetaModel::GetObject($this->Get('dest_class'), $this->Get('dest_id')); + foreach($aValues as $sAttCode => $value) + { + $oDestObj->Set($sAttCode, $value); + } + $oDestObj->DBUpdateTracked($oChange); + $aTraces[] = "Replica id:".$this->GetKey()." (dest_id:".$this->Get('dest_id').") marked as obsolete"; + $oStatLog->Inc('stats_nb_obj_obsoleted'); } - $oDestObj->DBUpdateTracked($oChange); - $aTraces[] = "Replica id:".$this->GetKey()." (dest_id:".$this->Get('dest_id').") marked as obsolete"; - $oStatLog->Inc($sStatCode); } catch(Exception $e) { $this->SetLastError('Unable to update the destination object: ', $e); - $oStatLog->Inc($sStatCode.'_errors'); + $oStatLog->Inc('stats_nb_obj_obsoleted_errors'); } } @@ -1257,18 +1273,26 @@ class SynchroReplica extends DBObject { if($this->Get('status_dest_creator')) { - $oDestObj = MetaModel::GetObject($this->Get('dest_class'), $this->Get('dest_id')); try { + $oDestObj = MetaModel::GetObject($this->Get('dest_class'), $this->Get('dest_id')); $oDestObj->DBDeleteTracked($oChange); + $this->DBDeleteTracked($oChange); $oStatLog->Inc('stats_nb_obj_deleted'); } catch(Exception $e) { $this->SetLastError('Unable to delete the destination object: ', $e); + $this->Set('status', 'obsolete'); + $this->DBUpdateTracked($oChange); $oStatLog->Inc('stats_nb_obj_deleted_errors'); } } + else + { + $this->DBDeleteTracked($oChange); + $oStatLog->Inc('stats_nb_replica_disappeared_no_action'); + } } /**