From 3cc8b5b88a755f00b82f0b0a67c0325ad005d920 Mon Sep 17 00:00:00 2001 From: Denis Flaven Date: Fri, 18 Nov 2011 11:42:29 +0000 Subject: [PATCH] Enhancement: when an (optional) external key cannot be reconciled, log a warning on the replica. the replicas containing a warning are then processed everytime in case the ext key changes Also improved the search/display of replicas SVN:1.2[1678] --- css/light-grey.css | 4 + dictionaries/dictionary.itop.core.php | 6 +- dictionaries/fr.dictionary.itop.core.php | 4 + synchro/replica.php | 4 +- synchro/synchro_exec.php | 8 +- synchro/synchro_import.php | 8 +- synchro/synchrodatasource.class.inc.php | 196 ++++++++++++++++++++--- 7 files changed, 200 insertions(+), 30 deletions(-) diff --git a/css/light-grey.css b/css/light-grey.css index 94d34a377..d5ee9858b 100644 --- a/css/light-grey.css +++ b/css/light-grey.css @@ -176,6 +176,10 @@ legend { -webkit-border-radius: 6px; border-radius: 6px; } +.ui-widget-content td legend a, .ui-widget-content td legend a:hover, .ui-widget-content td legend a:visited { + color: #fff; +} + .ui-widget-content td a, p a, p a:visited, td a, td a:visited { text-decoration:none; color: #1C94C4; diff --git a/dictionaries/dictionary.itop.core.php b/dictionaries/dictionary.itop.core.php index 2af632e40..adc9d23b9 100644 --- a/dictionaries/dictionary.itop.core.php +++ b/dictionaries/dictionary.itop.core.php @@ -639,7 +639,8 @@ Dict::Add('EN US', 'English', 'English', array( 'Core:SyncDataSourceObsolete' => 'The data source is marked as obsolete. Operation cancelled.', 'Core:SyncDataSourceAccessRestriction' => 'Only adminstrators or the user specified in the data source can execute this operation. Operation cancelled.', 'Core:SyncTooManyMissingReplicas' => 'All records have been untouched for some time (all of the objects could be deleted). Please check that the process that writes into the synchronization table is still running. Operation cancelled.', - + 'Core:Synchro:ListReplicas_AllReplicas_Errors_Warnings' => '%1$s replicas, %2$s error(s), %3$s warning(s).', + 'Core:SynchroReplica:TargetObject' => 'Synchronized Object: %1$s', 'Class:AsyncSendEmail' => 'Email (asynchronous)', 'Class:AsyncSendEmail/Attribute:to' => 'To', 'Class:AsyncSendEmail/Attribute:subject' => 'Subject', @@ -717,7 +718,7 @@ Dict::Add('EN US', 'English', 'English', array( 'Class:SynchroReplica/Attribute:sync_source_id' => 'Synchro Data Source', 'Class:SynchroReplica/Attribute:dest_id' => 'Destination object (ID)', 'Class:SynchroReplica/Attribute:dest_class' => 'Destination type', - 'Class:SynchroReplica/Attribute:status_last_seen' => 'Lat seen', + 'Class:SynchroReplica/Attribute:status_last_seen' => 'Last seen', 'Class:SynchroReplica/Attribute:status' => 'Status', 'Class:SynchroReplica/Attribute:status/Value:modified' => 'Modified', 'Class:SynchroReplica/Attribute:status/Value:new' => 'New', @@ -726,6 +727,7 @@ Dict::Add('EN US', 'English', 'English', array( 'Class:SynchroReplica/Attribute:status/Value:synchronized' => 'Synchronized', 'Class:SynchroReplica/Attribute:status_dest_creator' => 'Object Created ?', 'Class:SynchroReplica/Attribute:status_last_error' => 'Last Error', + 'Class:SynchroReplica/Attribute:status_last_warning' => 'Warnings', 'Class:SynchroReplica/Attribute:info_creation_date' => 'Creation Date', 'Class:SynchroReplica/Attribute:info_last_modified' => 'Last Modified Date', 'Class:appUserPreferences' => 'User Preferences', diff --git a/dictionaries/fr.dictionary.itop.core.php b/dictionaries/fr.dictionary.itop.core.php index 77bd4377a..3569a38d0 100644 --- a/dictionaries/fr.dictionary.itop.core.php +++ b/dictionaries/fr.dictionary.itop.core.php @@ -386,6 +386,8 @@ Dict::Add('FR FR', 'French', 'Français', array( 'Class:SynchroReplica/Attribute:status_dest_creator+' => '', 'Class:SynchroReplica/Attribute:status_last_error' => 'Dernière erreur', 'Class:SynchroReplica/Attribute:status_last_error+' => '', + 'Class:SynchroReplica/Attribute:status_last_warning' => 'Avertissements', + 'Class:SynchroReplica/Attribute:status_last_warning+' => '', 'Class:SynchroReplica/Attribute:info_creation_date' => 'Date de création', 'Class:SynchroReplica/Attribute:info_creation_date+' => '', 'Class:SynchroReplica/Attribute:info_last_modified' => 'Date de dernière modification', @@ -605,6 +607,8 @@ Opérateurs :
'Core:SyncDataSourceObsolete' => 'Cette source de données est obsolète. Opération annulée.', 'Core:SyncDataSourceAccessRestriction' => 'Seuls les administrateurs et l\'utilisateur spécifié dans la source de données peuvent exécuter cette synchronisation. Opération annulée.', 'Core:SyncTooManyMissingReplicas' => 'Tous les réplicas sont absents de l\'import. L\'import a-t-il réellement tourné. Opération annulée.', + 'Core:Synchro:ListReplicas_AllReplicas_Errors_Warnings' => '%1$s replicas, %2$s erreur(s), %3$s avertissement(s).', + 'Core:SynchroReplica:TargetObject' => 'Objet Synchronisé : %1$s', 'Core:Duration_Seconds' => '%1$ds', 'Core:Duration_Minutes_Seconds' => '%1$dmin %2$ds', 'Core:Duration_Hours_Minutes_Seconds' => '%1$dh %2$dmin %3$ds', diff --git a/synchro/replica.php b/synchro/replica.php index 5c1836951..d4fa4f376 100644 --- a/synchro/replica.php +++ b/synchro/replica.php @@ -53,6 +53,9 @@ try { throw new ApplicationException(Dict::Format('UI:Error:1ParametersMissing', 'oql')); } + $oFilter = DBObjectSearch::FromOQL($sOQL); + $oBlock1 = new DisplayBlock($oFilter, 'search', false, array('menu'=>false)); + $oBlock1->Display($oP, 0); $oP->add(''); $iSourceId = utils::ReadParam('datasource', null); if ($iSourceId != null) @@ -60,7 +63,6 @@ try $oSource = MetaModel::GetObject('SynchroDataSource', $iSourceId); $oP->p(Dict::Format('Core:SynchroReplica:BackToDataSource', $oSource->GetHyperlink()).''); } - $oFilter = DBObjectSearch::FromOQL($sOQL); $oBlock = new DisplayBlock($oFilter, 'list', false, array('menu'=>false)); $oBlock->Display($oP, 1); break; diff --git a/synchro/synchro_exec.php b/synchro/synchro_exec.php index 636dae5f1..ad732da3a 100644 --- a/synchro/synchro_exec.php +++ b/synchro/synchro_exec.php @@ -166,12 +166,12 @@ foreach(explode(',', $sDataSourcesList) as $iSDS) $oP->p("Objects deletion errors: ".$oStatLog->Get('stats_nb_obj_deleted_errors')); $oP->p("Objects obsoleted: ".$oStatLog->Get('stats_nb_obj_obsoleted')); $oP->p("Objects obsolescence errors: ".$oStatLog->Get('stats_nb_obj_obsoleted_errors')); - $oP->p("Objects created: ".$oStatLog->Get('stats_nb_obj_created')); + $oP->p("Objects created: ".$oStatLog->Get('stats_nb_obj_created')." (".$oStatLog->Get('stats_nb_obj_created_warnings')." warnings)"); $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 updated: ".$oStatLog->Get('stats_nb_obj_updated')." (".$oStatLog->Get('stats_nb_obj_updated_warnings')." warnings)"); $oP->p("Objects update errors: ".$oStatLog->Get('stats_nb_obj_updated_errors')); - $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 reconciled (updated): ".$oStatLog->Get('stats_nb_obj_new_updated')." (".$oStatLog->Get('stats_nb_obj_new_updated_warnings')." warnings)"); + $oP->p("Objects reconciled (unchanged): ".$oStatLog->Get('stats_nb_obj_new_unchanged')." (".$oStatLog->Get('stats_nb_obj_new_updated_warnings')." warnings)"); $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')); } diff --git a/synchro/synchro_import.php b/synchro/synchro_import.php index 781265dc8..e79ca665a 100644 --- a/synchro/synchro_import.php +++ b/synchro/synchro_import.php @@ -633,12 +633,12 @@ try $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 created: ".$oStatLog->Get('stats_nb_obj_created')." (".$oStatLog->Get('stats_nb_obj_created_warnings')." warnings)"); $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 updated: ".$oStatLog->Get('stats_nb_obj_updated')." (".$oStatLog->Get('stats_nb_obj_updated_warnings')." warnings)"); $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 reconciled (updated): ".$oStatLog->Get('stats_nb_obj_new_updated')." (".$oStatLog->Get('stats_nb_obj_new_updated_warnings')." warnings)"); + $oP->add_comment("Objects reconciled (unchanged): ".$oStatLog->Get('stats_nb_obj_new_unchanged')." (".$oStatLog->Get('stats_nb_obj_new_updated_warnings')." warnings)"); $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')); } diff --git a/synchro/synchrodatasource.class.inc.php b/synchro/synchrodatasource.class.inc.php index ca374afeb..e5a871ee8 100644 --- a/synchro/synchrodatasource.class.inc.php +++ b/synchro/synchrodatasource.class.inc.php @@ -251,6 +251,19 @@ class SynchroDataSource extends cmdbAbstractObject $sEndDate = $oLastLog->Get('end_date'); $iLastLog = $oLastLog->GetKey(); $oPage->p('

'.Dict::Format('Core:Synchro:SynchroEndedOn_Date', $sEndDate).'

'); + $sOQL = "SELECT SynchroReplica WHERE sync_source_id=$iDSid"; + $oSet = new DBObjectSet(DBObjectSearch::FromOQL($sOQL)); + $iCountAllReplicas = $oSet->Count(); + $sAllReplicas = "$iCountAllReplicas"; + $sOQL = "SELECT SynchroReplica WHERE sync_source_id=$iDSid AND status_last_error !=''"; + $oSet = new DBObjectSet(DBObjectSearch::FromOQL($sOQL)); + $iCountAllErrors = $oSet->Count(); + $sAllErrors = "$iCountAllErrors"; + $sOQL = "SELECT SynchroReplica WHERE sync_source_id=$iDSid AND status_last_warning !=''"; + $oSet = new DBObjectSet(DBObjectSearch::FromOQL($sOQL)); + $iCountAllWarnings = $oSet->Count(); + $sAllWarnings = "$iCountAllWarnings"; + $oPage->p('

'.Dict::Format('Core:Synchro:ListReplicas_AllReplicas_Errors_Warnings', $sAllReplicas, $sAllErrors, $sAllWarnings).'

'); } $oPage->add('"; } @@ -387,11 +422,15 @@ EOF '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_warnings' => $oLastLog->Get('stats_nb_obj_updated_warnings'), 'obj_updated_errors' => $oLastLog->Get('stats_nb_obj_updated_errors'), 'obj_new_updated' => $oLastLog->Get('stats_nb_obj_new_updated'), + 'obj_new_updated_warnings' => $oLastLog->Get('stats_nb_obj_new_updated_warnings'), 'obj_new_unchanged' => $oLastLog->Get('stats_nb_obj_new_unchanged'), 'obj_created' => $oLastLog->Get('stats_nb_obj_created'), + 'obj_created_warnings' => $oLastLog->Get('stats_nb_obj_created_warnings'), 'obj_created_errors' => $oLastLog->Get('stats_nb_obj_created_errors'), + 'obj_unchanged_warnings' => $oLastLog->Get('stats_nb_obj_unchanged_warnings'), ); $iReconciledErrors = $oLastLog->Get('stats_nb_replica_reconciled_errors'); $iDisappeared = $aData['obj_disappeared_errors'] + $aData['obj_obsoleted'] + $aData['obj_deleted'] + $aData['obj_disappeared_no_action']; @@ -949,13 +988,18 @@ EOF $oStatLog->Set('stats_nb_obj_obsoleted_errors', 0); $oStatLog->Set('stats_nb_obj_created', 0); $oStatLog->Set('stats_nb_obj_created_errors', 0); + $oStatLog->Set('stats_nb_obj_created_warnings', 0); $oStatLog->Set('stats_nb_obj_updated', 0); + $oStatLog->Set('stats_nb_obj_updated_warnings', 0); $oStatLog->Set('stats_nb_obj_updated_errors', 0); -// $oStatLog->Set('stats_nb_replica_reconciled', 0); + $oStatLog->Set('stats_nb_obj_unchanged_warnings', 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_updated_warnings', 0); $oStatLog->Set('stats_nb_obj_new_unchanged',0); + $oStatLog->Set('stats_nb_obj_new_unchanged_warnings',0); $sSelectTotal = "SELECT SynchroReplica WHERE sync_source_id = :source_id"; $oSetTotal = new DBObjectSet(DBObjectSearch::FromOQL($sSelectTotal), array() /* order by*/, array('source_id' => $this->GetKey())); @@ -989,14 +1033,14 @@ EOF $sStatistics .= "
  • ".$oStatLog->GetLabel('stats_nb_obj_deleted_errors').": ".$oStatLog->Get('stats_nb_obj_deleted_errors')."
  • \n"; $sStatistics .= "
  • ".$oStatLog->GetLabel('stats_nb_obj_obsoleted').": ".$oStatLog->Get('stats_nb_obj_obsoleted')."
  • \n"; $sStatistics .= "
  • ".$oStatLog->GetLabel('stats_nb_obj_obsoleted_errors').": ".$oStatLog->Get('stats_nb_obj_obsoleted_errors')."
  • \n"; - $sStatistics .= "
  • ".$oStatLog->GetLabel('stats_nb_obj_created').": ".$oStatLog->Get('stats_nb_obj_created')."
  • \n"; + $sStatistics .= "
  • ".$oStatLog->GetLabel('stats_nb_obj_created').": ".$oStatLog->Get('stats_nb_obj_created')." (".$oStatLog->Get('stats_nb_obj_created_warnings')." warnings)"."
  • \n"; $sStatistics .= "
  • ".$oStatLog->GetLabel('stats_nb_obj_created_errors').": ".$oStatLog->Get('stats_nb_obj_created_errors')."
  • \n"; - $sStatistics .= "
  • ".$oStatLog->GetLabel('stats_nb_obj_updated').": ".$oStatLog->Get('stats_nb_obj_updated')."
  • \n"; + $sStatistics .= "
  • ".$oStatLog->GetLabel('stats_nb_obj_updated').": ".$oStatLog->Get('stats_nb_obj_updated')." (".$oStatLog->Get('stats_nb_obj_updated_warnings')." warnings)"."
  • \n"; $sStatistics .= "
  • ".$oStatLog->GetLabel('stats_nb_obj_updated_errors').": ".$oStatLog->Get('stats_nb_obj_updated_errors')."
  • \n"; $sStatistics .= "
  • ".$oStatLog->GetLabel('stats_nb_replica_reconciled_errors').": ".$oStatLog->Get('stats_nb_replica_reconciled_errors')."
  • \n"; $sStatistics .= "
  • ".$oStatLog->GetLabel('stats_nb_replica_disappeared_no_action').": ".$oStatLog->Get('stats_nb_replica_disappeared_no_action')."
  • \n"; - $sStatistics .= "
  • ".$oStatLog->GetLabel('stats_nb_obj_new_updated').": ".$oStatLog->Get('stats_nb_obj_new_updated')."
  • \n"; - $sStatistics .= "
  • ".$oStatLog->GetLabel('stats_nb_obj_new_unchanged').": ".$oStatLog->Get('stats_nb_obj_new_unchanged')."
  • \n"; + $sStatistics .= "
  • ".$oStatLog->GetLabel('stats_nb_obj_new_updated').": ".$oStatLog->Get('stats_nb_obj_new_updated')." (".$oStatLog->Get('stats_nb_obj_new_updated_warnings')." warnings)"."
  • \n"; + $sStatistics .= "
  • ".$oStatLog->GetLabel('stats_nb_obj_new_unchanged').": ".$oStatLog->Get('stats_nb_obj_new_unchanged')." (".$oStatLog->Get('stats_nb_obj_new_unchanged_warnings')." warnings)"."
  • \n"; $sStatistics .= "\n"; $this->SendNotification("errors ($iErrors)", "

    The synchronization has been executed, $iErrors errors have been encountered. Click here to see the records being currently in error.

    ".$sStatistics); @@ -1180,10 +1224,10 @@ EOF $oSetSeen = new DBObjectSet(DBObjectSearch::FromOQL($sSelectSeen), array() /* order by*/, array('source_id' => $this->GetKey(), 'last_import' => $sLimitDate)); $oStatLog->Set('stats_nb_replica_seen', $oSetSeen->Count()); - // Get all the replicas that are 'new' or modified + // Get all the replicas that are 'new' or modified or synchronized with a warning // - $sSelectToSync = "SELECT SynchroReplica WHERE (status = 'new' OR status = 'modified') AND sync_source_id = :source_id"; - $oSetToSync = new DBObjectSet(DBObjectSearch::FromOQL($sSelectToSync), array() /* order by*/, array('source_id' => $this->GetKey()) /* aArgs */, $aExtDataSpec, 0 /* limitCount */, 0 /* limitStart */); + $sSelectToSync = "SELECT SynchroReplica WHERE (status = 'new' OR status = 'modified' OR (status = 'synchronized' AND status_last_warning != '')) AND sync_source_id = :source_id AND status_last_seen >= :last_import"; + $oSetToSync = new DBObjectSet(DBObjectSearch::FromOQL($sSelectToSync), array() /* order by*/, array('source_id' => $this->GetKey(), 'last_import' => $sLimitDate) /* aArgs */, $aExtDataSpec, 0 /* limitCount */, 0 /* limitStart */); while($oReplica = $oSetToSync->Fetch()) { @@ -1484,15 +1528,20 @@ class SynchroLog extends DBObject MetaModel::Init_AddAttribute(new AttributeInteger("stats_nb_obj_obsoleted_errors", array("allowed_values"=>null, "sql"=>"stats_nb_obj_obsoleted_errors", "default_value"=>0, "is_null_allowed"=>false, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeInteger("stats_nb_obj_created", array("allowed_values"=>null, "sql"=>"stats_nb_obj_created", "default_value"=>0, "is_null_allowed"=>false, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeInteger("stats_nb_obj_created_errors", array("allowed_values"=>null, "sql"=>"stats_nb_obj_created_errors", "default_value"=>0, "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeInteger("stats_nb_obj_created_warnings", array("allowed_values"=>null, "sql"=>"stats_nb_obj_created_warnings", "default_value"=>0, "is_null_allowed"=>false, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeInteger("stats_nb_obj_updated", array("allowed_values"=>null, "sql"=>"stats_nb_obj_updated", "default_value"=>0, "is_null_allowed"=>false, "depends_on"=>array()))); 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_obj_updated_warnings", array("allowed_values"=>null, "sql"=>"stats_nb_obj_updated_warnings", "default_value"=>0, "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeInteger("stats_nb_obj_unchanged_warnings", array("allowed_values"=>null, "sql"=>"stats_nb_obj_unchanged_warnings", "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_updated_warnings", array("allowed_values"=>null, "sql"=>"stats_nb_obj_new_updated_warnings", "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()))); - + MetaModel::Init_AddAttribute(new AttributeInteger("stats_nb_obj_new_unchanged_warnings", array("allowed_values"=>null, "sql"=>"stats_nb_obj_new_unchanged_warnings", "default_value"=>0, "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeText("last_error", array("allowed_values"=>null, "sql"=>"last_error", "default_value"=>'', "is_null_allowed"=>true, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeLongText("traces", array("allowed_values"=>null, "sql"=>"traces", "default_value"=>'', "is_null_allowed"=>true, "depends_on"=>array()))); @@ -1596,6 +1645,7 @@ class SynchroLog extends DBObject class SynchroReplica extends DBObject implements iDisplay { static $aSearches = array(); // Cache of OQL queries used for reconciliation (per data source) + protected $aWarnings; public static function Init() { @@ -1624,7 +1674,8 @@ class SynchroReplica extends DBObject implements iDisplay MetaModel::Init_AddAttribute(new AttributeEnum("status", array("allowed_values"=>new ValueSetEnum('new,synchronized,modified,orphan,obsolete'), "sql"=>"status", "default_value"=>"new", "is_null_allowed"=>false, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeBoolean("status_dest_creator", array("allowed_values"=>null, "sql"=>"status_dest_creator", "default_value"=>0, "is_null_allowed"=>true, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeString("status_last_error", array("allowed_values"=>null, "sql"=>"status_last_error", "default_value"=>'', "is_null_allowed"=>true, "depends_on"=>array()))); - + MetaModel::Init_AddAttribute(new AttributeString("status_last_warning", array("allowed_values"=>null, "sql"=>"status_last_warning", "default_value"=>'', "is_null_allowed"=>true, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeDateTime("info_creation_date", array("allowed_values"=>null, "sql"=>"info_creation_date", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeDateTime("info_last_modified", array("allowed_values"=>null, "sql"=>"info_last_modified", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); @@ -1632,17 +1683,65 @@ class SynchroReplica extends DBObject implements iDisplay MetaModel::Init_SetZListItems('details', array('' . 'col:0'=> array( 'fieldset:SynchroDataSource:Definition' => array('sync_source_id','dest_id','dest_class'), - 'fieldset:SynchroDataSource:Status' => array('status','status_last_seen','status_dest_creator','status_last_error'), + 'fieldset:SynchroDataSource:Status' => array('status','status_last_seen','status_dest_creator','status_last_error','status_last_warning'), 'fieldset:SynchroDataSource:Information' => array('info_creation_date','info_last_modified')) ) ); - MetaModel::Init_SetZListItems('list', array('sync_source_id', 'dest_id', 'dest_class', 'status_last_seen', 'status', 'status_dest_creator', 'status_last_error')); // Attributes to be displayed for a list + MetaModel::Init_SetZListItems('list', array('sync_source_id', 'dest_id', 'dest_class', 'status_last_seen', 'status', 'status_dest_creator', 'status_last_error', 'status_last_warning')); // Attributes to be displayed for a list // Search criteria - MetaModel::Init_SetZListItems('standard_search', array('sync_source_id', 'status_last_seen', 'status', 'status_dest_creator', 'dest_class', 'dest_id', 'status_last_error')); // Criteria of the std search form + MetaModel::Init_SetZListItems('standard_search', array('sync_source_id', 'status_last_seen', 'status', 'status_dest_creator', 'dest_class', 'dest_id', 'status_last_error', 'status_last_warning')); // Criteria of the std search form // MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form } + + public function __construct($aRow = null, $sClassAlias = '', $aAttToLoad = null, $aExtendedDataSpec = null) + { + parent::__construct($aRow, $sClassAlias, $aAttToLoad, $aExtendedDataSpec); + $this->aWarnings = array(); + } - public function DBInsert() + protected function AddWarning($sWarningMessage) + { + $this->aWarnings[] = $sWarningMessage; + } + + protected function ResetWarnings() + { + $this->aWarnings = array(); + } + + protected function HasWarnings() + { + return (count($this->aWarnings) > 0); + } + + protected function RecordWarnings() + { + $sWarningMessage = ''; + $MAX_WARNING_LENGTH = 255; + switch(count($this->aWarnings)) + { + case 0: + $sWarningMessage = ''; + break; + + case 1: + $sWarningMessage = $this->aWarnings[0]; + break; + + default: + $sWarningMessage = count($this->aWarnings)." warnings: ".implode(' '.$this->aWarnings); + break; + } + + if (strlen($sWarningMessage) > $MAX_WARNING_LENGTH) + { + $sWarningMessage = substr($sWarningMessage, 0, $MAX_WARNING_LENGTH - 3).'...'; + } + + $this->Set('status_last_warning', $sWarningMessage); + } + + public function DBInsert() { throw new CoreException('A synchronization replica must be created only by the mean of triggers'); } @@ -1688,6 +1787,7 @@ class SynchroReplica extends DBObject implements iDisplay public function Synchro($oDataSource, $aReconciliationKeys, $aAttributes, $oChange, &$oStatLog) { + $this->ResetWarnings(); switch($this->Get('status')) { case 'new': @@ -1714,6 +1814,7 @@ class SynchroReplica extends DBObject implements iDisplay } else { + // TO DO: can we retry this ?? // Reconciliation could not be performed - log and EXIT $oStatLog->AddTrace("Could not reconcile on null value for attribute '$sFilterCode'", $this); $this->SetLastError("Could not reconcile on null value for attribute '$sFilterCode'"); @@ -1737,6 +1838,10 @@ class SynchroReplica extends DBObject implements iDisplay if ($oDataSource->Get('action_on_zero') == 'create') { $this->CreateObjectFromReplica($oDataSource->GetTargetClass(), $aAttributes, $oChange, $oStatLog); + if ($this->HasWarnings()) + { + $oStatLog->Inc('stats_nb_obj_created_warnings'); + } } else // assumed to be 'error' { @@ -1751,9 +1856,20 @@ class SynchroReplica extends DBObject implements iDisplay if ($oDataSource->Get('action_on_one') == 'update') { $oDestObj = $oDestSet->Fetch(); - $this->UpdateObjectFromReplica($oDestObj, $aAttributes, $oChange, $oStatLog, 'stats_nb_obj_new', 'stats_nb_replica_reconciled_errors'); + $bModified = $this->UpdateObjectFromReplica($oDestObj, $aAttributes, $oChange, $oStatLog, 'stats_nb_obj_new', 'stats_nb_replica_reconciled_errors'); $this->Set('dest_id', $oDestObj->GetKey()); $this->Set('dest_class', get_class($oDestObj)); + if ($this->HasWarnings()) + { + if ($bModified) + { + $oStatLog->Inc('stats_nb_obj_new_updated_warnings'); + } + else + { + $oStatLog->Inc('stats_nb_obj_new_unchanged_warnings'); + } + } } else { @@ -1775,20 +1891,37 @@ class SynchroReplica extends DBObject implements iDisplay elseif ($oDataSource->Get('action_on_multiple') == 'create') { $this->CreateObjectFromReplica($oDataSource->GetTargetClass(), $aAttributes, $oChange, $oStatLog); + if ($this->HasWarnings()) + { + $oStatLog->Inc('stats_nb_obj_created_warnings'); + } } else { // assumed to be 'take_first' $oDestObj = $oDestSet->Fetch(); - $this->UpdateObjectFromReplica($oDestObj, $aAttributes, $oChange, $oStatLog, 'stats_nb_obj_new', 'stats_nb_replica_reconciled_errors'); + $bModified = $this->UpdateObjectFromReplica($oDestObj, $aAttributes, $oChange, $oStatLog, 'stats_nb_obj_new', 'stats_nb_replica_reconciled_errors'); $this->Set('dest_id', $oDestObj->GetKey()); $this->Set('dest_class', get_class($oDestObj)); + if ($this->HasWarnings()) + { + if ($bModified) + { + $oStatLog->Inc('stats_nb_obj_new_updated_warnings'); + } + else + { + $oStatLog->Inc('stats_nb_obj_new_unchanged_warnings'); + } + } } } + $this->RecordWarnings(); break; + case 'synchronized': // try to recover synchronized replicas with warnings case 'modified': - $oDestObj = MetaModel::GetObject($oDataSource->GetTargetClass(), $this->Get('dest_id')); + $oDestObj = MetaModel::GetObject($oDataSource->GetTargetClass(), $this->Get('dest_id')); if ($oDestObj == null) { $this->Set('status', 'orphan'); // The destination object has been deleted ! @@ -1797,8 +1930,20 @@ class SynchroReplica extends DBObject implements iDisplay } else { - $this->UpdateObjectFromReplica($oDestObj, $aAttributes, $oChange, $oStatLog, 'stats_nb_obj', 'stats_nb_obj_updated_errors'); + $bModified = $this->UpdateObjectFromReplica($oDestObj, $aAttributes, $oChange, $oStatLog, 'stats_nb_obj', 'stats_nb_obj_updated_errors'); + if ($this->HasWarnings()) + { + if ($bModified) + { + $oStatLog->Inc('stats_nb_obj_updated_warnings'); + } + else + { + $oStatLog->Inc('stats_nb_obj_unchanged_warnings'); + } + } } + $this->RecordWarnings(); break; default: // Do nothing in all other cases @@ -1811,6 +1956,7 @@ class SynchroReplica extends DBObject implements iDisplay protected function UpdateObjectFromReplica($oDestObj, $aAttributes, $oChange, &$oStatLog, $sStatsCode, $sStatsCodeError) { $aValueTrace = array(); + $bModified = false; try { foreach($aAttributes as $sAttCode => $oSyncAtt) @@ -1826,6 +1972,7 @@ class SynchroReplica extends DBObject implements iDisplay if ($oDestObj->IsModified()) { $oDestObj->DBUpdateTracked($oChange); + $bModified = true; $oStatLog->AddTrace('Updated object - Values: {'.implode(', ', $aValueTrace).'}', $this); if (($sStatsCode != '') &&(MetaModel::IsValidAttCode(get_class($oStatLog), $sStatsCode.'_updated'))) { @@ -1851,6 +1998,7 @@ class SynchroReplica extends DBObject implements iDisplay $this->SetLastError('Unable to update destination object: ', $e); $oStatLog->Inc($sStatsCodeError); } + return $bModified; } /** @@ -1983,6 +2131,7 @@ class SynchroReplica extends DBObject implements iDisplay // $sExtAttCode is a valid attribute code // $sClass = $this->Get('base_class'); + $oAttDef = MetaModel::GetAttributeDef($sClass, $sExtAttCode); if (!is_null($oSyncAtt) && ($oSyncAtt instanceof SynchroAttExtKey)) @@ -2007,6 +2156,7 @@ class SynchroReplica extends DBObject implements iDisplay { // Note: differs from null (in which case the value would be left unchanged) $oStatLog->AddTrace("Could not find [unique] object for '$sExtAttCode': searched on $sReconcAttCode = '$rawValue'", $this); + $this->AddWarning("Could not find [unique] object for '$sExtAttCode': searched on $sReconcAttCode = '$rawValue'"); $retValue = 0; } } @@ -2108,6 +2258,14 @@ class SynchroReplica extends DBObject implements iDisplay } $oPage->Details($aDetails); $oPage->add(''); + $oDestObj = MetaModel::GetObject($this->Get('dest_class'), $this->Get('dest_id'), false); + if (is_object($oDestObj)) + { + $oPage->add('
    '); + $oPage->add(''.Dict::Format('Core:SynchroReplica:TargetObject', $oDestObj->GetHyperlink()).''); + $oDestObj->DisplayBareProperties($oPage, false, $aExtraParams); + $oPage->add('
    '); + } $oPage->add('
    '); @@ -273,6 +286,18 @@ class SynchroDataSource extends cmdbAbstractObject $sScript .= "};\n"; $sScript .= << 0); + ToggleSynoptics('#cw_obj_new_updated_warnings', aValues['obj_new_updated_warnings'] > 0); + ToggleSynoptics('#cw_obj_new_unchanged_warnings', aValues['obj_new_unchanged_warnings'] > 0); + ToggleSynoptics('#cw_obj_updated_warnings', aValues['obj_updated_warnings'] > 0); + ToggleSynoptics('#cw_obj_unchanged_warnings', aValues['obj_unchanged_warnings'] > 0); } EOF ; @@ -375,6 +406,10 @@ EOF $sCount = "$iCount"; $sLabel = Dict::Format('Core:Synchro:label_'.$sId, $sCount); $sOpacity = ($iCount==0) ? "opacity:0.3;" : ""; + if (isset($aData[$sId.'_warnings'])) + { + $sLabel .= " (".$aData[$sId.'_warnings'].")"; + } return "{$sLabel}{$sErrorLink}'); $oPage->add('
    '); $oPage->add(''.Dict::S('Core:SynchroReplica:PublicData').'');