DBus server: add source status and progress for restore (MB#8144)

Generate and send source statuses and progress to dbus clients.
For source modes in statuses, 'restore-from-backup' is sent once
a source is restored. For progress, the granularity is source level.
Thus, the progress is simply calculated based on the number of
sources and how many of them that have been completed.

To report source status and progress, SyncContext::restore
should have a mechanism to give out events to dbus server.
Here 'displaySourceProgress' is re-used. Some synthesis events are
simulated in restore. To follow up the way of 'displaySourceProgress',
a pseudo mode 'SYNC_RESTORE_FROM_BACKUP' is defined for source sync
mode. This is treated as a special sync mode only for restore from
backup.

Also add test code in testRestoreByRef to check source
status and progress.
This commit is contained in:
Zhu, Yongsheng 2009-12-22 16:47:31 +08:00
parent 419199562f
commit 1d309f51af
5 changed files with 191 additions and 88 deletions

View File

@ -907,6 +907,10 @@ class Session : public DBusObjectHelper,
/** restore used */
string m_restoreDir;
bool m_restoreBefore;
/** the total number of sources to be restored */
int m_restoreSrcTotal;
/** the number of sources that have been restored */
int m_restoreSrcEnd;
enum RunOperation {
OP_SYNC = 0,
@ -1855,6 +1859,8 @@ Session::Session(DBusServer &server,
m_statusTimer(100),
m_progressTimer(50),
m_restoreBefore(true),
m_restoreSrcTotal(0),
m_restoreSrcEnd(0),
m_runOperation(OP_SYNC),
emitStatus(*this, "StatusChanged"),
emitProgress(*this, "ProgressChanged")
@ -1940,58 +1946,96 @@ void Session::sourceProgress(sysync::TProgressEventEnum type,
SyncSource &source,
int32_t extra1, int32_t extra2, int32_t extra3)
{
SourceProgress &progress = m_sourceProgress[source.getName()];
SourceStatus &status = m_sourceStatus[source.getName()];
switch(type) {
case sysync::PEV_SYNCSTART:
if(source.getFinalSyncMode() != SYNC_NONE) {
m_progData.setStep(ProgressData::PRO_SYNC_UNINIT);
fireProgress();
switch(m_runOperation) {
case OP_SYNC: {
SourceProgress &progress = m_sourceProgress[source.getName()];
SourceStatus &status = m_sourceStatus[source.getName()];
switch(type) {
case sysync::PEV_SYNCSTART:
if(source.getFinalSyncMode() != SYNC_NONE) {
m_progData.setStep(ProgressData::PRO_SYNC_UNINIT);
fireProgress();
}
break;
case sysync::PEV_SYNCEND:
if(source.getFinalSyncMode() != SYNC_NONE) {
status.set(PrettyPrintSyncMode(source.getFinalSyncMode()), "done", extra1);
fireStatus(true);
}
break;
case sysync::PEV_PREPARING:
if(source.getFinalSyncMode() != SYNC_NONE) {
progress.m_phase = "preparing";
progress.m_prepareCount = extra1;
progress.m_prepareTotal = extra2;
m_progData.itemPrepare();
fireProgress(true);
}
break;
case sysync::PEV_ITEMSENT:
if(source.getFinalSyncMode() != SYNC_NONE) {
progress.m_phase = "sending";
progress.m_sendCount = extra1;
progress.m_sendTotal = extra2;
fireProgress(true);
}
break;
case sysync::PEV_ITEMRECEIVED:
if(source.getFinalSyncMode() != SYNC_NONE) {
progress.m_phase = "receiving";
progress.m_receiveCount = extra1;
progress.m_receiveTotal = extra2;
m_progData.itemReceive(source.getName(), extra1, extra2);
fireProgress(true);
}
break;
case sysync::PEV_ALERTED:
if(source.getFinalSyncMode() != SYNC_NONE) {
status.set(PrettyPrintSyncMode(source.getFinalSyncMode()), "running", 0);
fireStatus(true);
m_progData.setStep(ProgressData::PRO_SYNC_DATA);
m_progData.addSyncMode(source.getFinalSyncMode());
fireProgress();
}
break;
default:
;
}
break;
case sysync::PEV_SYNCEND:
if(source.getFinalSyncMode() != SYNC_NONE) {
status.set(PrettyPrintSyncMode(source.getFinalSyncMode()), "done", extra1);
fireStatus(true);
}
break;
case sysync::PEV_PREPARING:
if(source.getFinalSyncMode() != SYNC_NONE) {
progress.m_phase = "preparing";
progress.m_prepareCount = extra1;
progress.m_prepareTotal = extra2;
m_progData.itemPrepare();
fireProgress(true);
}
break;
case sysync::PEV_ITEMSENT:
if(source.getFinalSyncMode() != SYNC_NONE) {
progress.m_phase = "sending";
progress.m_sendCount = extra1;
progress.m_sendTotal = extra2;
fireProgress(true);
}
break;
case sysync::PEV_ITEMRECEIVED:
if(source.getFinalSyncMode() != SYNC_NONE) {
progress.m_phase = "receiving";
progress.m_receiveCount = extra1;
progress.m_receiveTotal = extra2;
m_progData.itemReceive(source.getName(), extra1, extra2);
fireProgress(true);
}
break;
case sysync::PEV_ALERTED:
if(source.getFinalSyncMode() != SYNC_NONE) {
status.set(PrettyPrintSyncMode(source.getFinalSyncMode()), "running", 0);
fireStatus(true);
m_progData.setStep(ProgressData::PRO_SYNC_DATA);
m_progData.addSyncMode(source.getFinalSyncMode());
fireProgress();
}
case OP_RESTORE: {
switch(type) {
case sysync::PEV_ALERTED:
// count the total number of sources to be restored
m_restoreSrcTotal++;
break;
case sysync::PEV_SYNCSTART: {
if (source.getFinalSyncMode() != SYNC_NONE) {
SourceStatus &status = m_sourceStatus[source.getName()];
// set statuses as 'restore-from-backup'
status.set(PrettyPrintSyncMode(source.getFinalSyncMode()), "running", 0);
fireStatus(true);
}
break;
}
case sysync::PEV_SYNCEND: {
if (source.getFinalSyncMode() != SYNC_NONE) {
m_restoreSrcEnd++;
SourceStatus &status = m_sourceStatus[source.getName()];
status.set(PrettyPrintSyncMode(source.getFinalSyncMode()), "done", 0);
m_progress = 100 * m_restoreSrcEnd / m_restoreSrcTotal;
fireStatus(true);
fireProgress(true);
}
break;
}
default:
break;
}
break;
}
default:
;
break;
}
}
@ -2087,6 +2131,14 @@ void Session::restore(const string &dir, bool before, const std::vector<std::str
m_restoreDir = dir;
m_runOperation = OP_RESTORE;
// initiate status and progress and sourceProgress is not calculated currently
BOOST_FOREACH(const std::string source,
m_sync->getSyncSources()) {
m_sourceStatus[source];
}
fireProgress(true);
fireStatus(true);
g_main_loop_quit(loop);
}

View File

@ -1049,51 +1049,57 @@ void SyncContext::displaySourceProgress(sysync::TProgressEventEnum type,
/* datastore alerted (extra1=0 for normal, 1 for slow, 2 for first time slow,
extra2=1 for resumed session,
extra3 0=twoway, 1=fromserver, 2=fromclient */
SE_LOG_INFO(NULL, NULL, "%s: %s %s sync%s",
source.getName(),
extra2 ? "resuming" : "starting",
extra1 == 0 ? "normal" :
extra1 == 1 ? "slow" :
extra1 == 2 ? "first time" :
"unknown",
extra3 == 0 ? ", two-way" :
extra3 == 1 ? " from server" :
extra3 == 2 ? " from client" :
", unknown direction");
SyncMode mode = SYNC_NONE;
switch (extra1) {
case 0:
switch (extra3) {
// -1 is used for alerting a restore from backup. Synthesis won't use this
if (extra1 != -1) {
SE_LOG_INFO(NULL, NULL, "%s: %s %s sync%s",
source.getName(),
extra2 ? "resuming" : "starting",
extra1 == 0 ? "normal" :
extra1 == 1 ? "slow" :
extra1 == 2 ? "first time" :
"unknown",
extra3 == 0 ? ", two-way" :
extra3 == 1 ? " from server" :
extra3 == 2 ? " from client" :
", unknown direction");
SyncMode mode = SYNC_NONE;
switch (extra1) {
case 0:
mode = SYNC_TWO_WAY;
switch (extra3) {
case 0:
mode = SYNC_TWO_WAY;
break;
case 1:
mode = SYNC_ONE_WAY_FROM_SERVER;
break;
case 2:
mode = SYNC_ONE_WAY_FROM_CLIENT;
break;
}
break;
case 1:
mode = SYNC_ONE_WAY_FROM_SERVER;
break;
case 2:
mode = SYNC_ONE_WAY_FROM_CLIENT;
switch (extra3) {
case 0:
mode = SYNC_SLOW;
break;
case 1:
mode = SYNC_REFRESH_FROM_SERVER;
break;
case 2:
mode = SYNC_REFRESH_FROM_CLIENT;
break;
}
break;
}
break;
case 1:
case 2:
switch (extra3) {
case 0:
mode = SYNC_SLOW;
break;
case 1:
mode = SYNC_REFRESH_FROM_SERVER;
break;
case 2:
mode = SYNC_REFRESH_FROM_CLIENT;
break;
}
break;
source.recordFinalSyncMode(mode);
source.recordFirstSync(extra1 == 2);
source.recordResumeSync(extra2 == 1);
} else {
SE_LOG_INFO(NULL, NULL, "%s: restore from backup", source.getName());
source.recordFinalSyncMode(SYNC_RESTORE_FROM_BACKUP);
}
source.recordFinalSyncMode(mode);
source.recordFirstSync(extra1 == 2);
source.recordResumeSync(extra2 == 1);
break;
}
case sysync::PEV_SYNCSTART:
@ -1141,6 +1147,10 @@ void SyncContext::displaySourceProgress(sysync::TProgressEventEnum type,
extra3=1 for resumed session) */
if (source.getFinalSyncMode() == SYNC_NONE) {
SE_LOG_INFO(NULL, NULL, "%s: inactive", source.getName());
} else if(source.getFinalSyncMode() == SYNC_RESTORE_FROM_BACKUP) {
SE_LOG_INFO(NULL, NULL, "%s: restore done %s",
source.getName(),
extra1 ? "unsuccessfully" : "successfully" );
} else {
SE_LOG_INFO(NULL, NULL, "%s: %s%s sync done %s",
source.getName(),
@ -2808,6 +2818,8 @@ void SyncContext::restore(const string &dirname, RestoreDatabase database)
string datadump = database == DATABASE_BEFORE_SYNC ? "before" : "after";
BOOST_FOREACH(SyncSource *source, sourceList) {
// fake a source alert event
displaySourceProgress(sysync::PEV_ALERTED, *source, -1, 0, 0);
source->open();
}
@ -2826,12 +2838,12 @@ void SyncContext::restore(const string &dirname, RestoreDatabase database)
BOOST_FOREACH(SyncSource *source, sourceList) {
SyncSourceReport sourcereport;
try {
SE_LOG_DEBUG(NULL, NULL, "Restoring %s...", source->getName());
displaySourceProgress(sysync::PEV_SYNCSTART, *source, 0, 0, 0);
sourceList.restoreDatabase(*source,
datadump,
m_dryrun,
sourcereport);
SE_LOG_DEBUG(NULL, NULL, "... %s restored.", source->getName());
displaySourceProgress(sysync::PEV_SYNCEND, *source, 0, 0, 0);
report.addSyncSourceReport(source->getName(), sourcereport);
} catch (...) {
sourcereport.recordStatus(STATUS_FATAL);

View File

@ -56,6 +56,8 @@ std::string PrettyPrintSyncMode(SyncMode mode, bool userVisible)
case SYNC_REFRESH_FROM_SERVER:
case SA_SYNC_REFRESH_FROM_SERVER:
return userVisible ? "refresh-from-server" : "SYNC_REFRESH_FROM_SERVER";
case SYNC_RESTORE_FROM_BACKUP:
return userVisible ? "restore-from-backup" : "SYNC_RESTORE_FROM_BACKUP";
default:
std::stringstream res;

View File

@ -48,7 +48,10 @@ enum SyncMode {
SA_SYNC_REFRESH_FROM_CLIENT = 208,
SA_SYNC_ONE_WAY_FROM_SERVER = 209,
SA_SYNC_REFRESH_FROM_SERVER = 210,
// used by restore backend with backup data, a pseudo mode
SYNC_RESTORE_FROM_BACKUP = 211,
SYNC_LAST = 220,
/** error situation (in contrast to SYNC_NONE) */
SYNC_INVALID = 255

View File

@ -1095,6 +1095,40 @@ class TestSessionAPIsDummy(unittest.TestCase, DBusUtil):
self.session.Restore(dir, True, [], utf8_strings=True)
loop.run()
self.session.Detach()
# check recorded events in DBusUtil.events, first filter them
statuses = []
progresses = []
for item in DBusUtil.events:
if item[0] == "status":
statuses.append(item[1])
elif item[0] == "progress":
progresses.append(item[1])
lastStatus = ""
lastSources = {}
statusPairs = {"": 0, "idle": 1, "running" : 2, "done" : 3}
for status, error, sources in statuses:
self.failIf(status == lastStatus and lastSources == sources)
# no error
self.failUnlessEqual(error, 0)
for sourcename, value in sources.items():
# no error
self.failUnlessEqual(value[2], 0)
# keep order: source status must also be unchanged or the next status
if lastSources.has_key(sourcename):
lastValue = lastSources[sourcename]
self.failUnless(statusPairs[value[1]] >= statusPairs[lastValue[1]])
lastStatus = status
lastSources = sources
# check increasing progress percentage
lastPercent = 0
for percent, sources in progresses:
self.failIf(percent < lastPercent)
lastPercent = percent
session.SetConfig(False, False, self.config, utf8_strings=True)
#restore data after this session
session.Restore(dir, False, ["addressbook", "calendar"], utf8_strings=True)