[CalendarServer-changes] [12419] CalendarServer/branches/users/cdaboo/pod-migration/txdav
source_changes at macosforge.org
source_changes at macosforge.org
Wed Mar 12 11:17:38 PDT 2014
Revision: 12419
http://trac.calendarserver.org//changeset/12419
Author: cdaboo at apple.com
Date: 2014-01-22 10:11:52 -0800 (Wed, 22 Jan 2014)
Log Message:
-----------
Checkpoint: sync of home metadata.
Modified Paths:
--------------
CalendarServer/branches/users/cdaboo/pod-migration/txdav/caldav/datastore/sql.py
CalendarServer/branches/users/cdaboo/pod-migration/txdav/caldav/datastore/sql_external.py
CalendarServer/branches/users/cdaboo/pod-migration/txdav/common/datastore/podding/conduit.py
CalendarServer/branches/users/cdaboo/pod-migration/txdav/common/datastore/podding/test/test_conduit.py
CalendarServer/branches/users/cdaboo/pod-migration/txdav/common/datastore/podding/test/test_migration.py
CalendarServer/branches/users/cdaboo/pod-migration/txdav/common/datastore/sql.py
Modified: CalendarServer/branches/users/cdaboo/pod-migration/txdav/caldav/datastore/sql.py
===================================================================
--- CalendarServer/branches/users/cdaboo/pod-migration/txdav/caldav/datastore/sql.py 2014-01-22 04:07:03 UTC (rev 12418)
+++ CalendarServer/branches/users/cdaboo/pod-migration/txdav/caldav/datastore/sql.py 2014-01-22 18:11:52 UTC (rev 12419)
@@ -936,6 +936,42 @@
yield inbox.notifyPropertyChanged()
+ @inlineCallbacks
+ def postSyncChildren(self, externalHome, final):
+ """
+ Synchronize the metadata from the external side.
+ """
+
+ yield super(CalendarHome, self).postSyncChildren(externalHome, final)
+
+ # Default collections
+ children = yield self.loadChildren()
+ id_map = dict([(child.external_id(), child.id()) for child in children if child.owned()])
+
+ chm = self._homeMetaDataSchema
+ for component, attr in self._componentDefaultAttribute.items():
+ external_id = getattr(externalHome, attr)
+ internal_id = id_map.get(external_id)
+ if internal_id != getattr(self, attr):
+ setattr(self, attr, internal_id)
+ yield Update(
+ {self._componentDefaultColumn[component]: internal_id},
+ Where=chm.RESOURCE_ID == self._resourceID,
+ ).on(self._txn)
+
+ # Default alarms
+ for vevent in (True, False):
+ for timed in (True, False):
+ external_default = externalHome.getDefaultAlarm(vevent, timed)
+ if external_default != self.getDefaultAlarm(vevent, timed):
+ yield self.setDefaultAlarm(external_default, vevent, timed)
+
+ # Availability
+ external_availability = externalHome.getAvailability()
+ if external_availability != self.getAvailability():
+ yield self.setAvailability(external_availability)
+
+
CalendarHome._register(ECALENDARTYPE)
Modified: CalendarServer/branches/users/cdaboo/pod-migration/txdav/caldav/datastore/sql_external.py
===================================================================
--- CalendarServer/branches/users/cdaboo/pod-migration/txdav/caldav/datastore/sql_external.py 2014-01-22 04:07:03 UTC (rev 12418)
+++ CalendarServer/branches/users/cdaboo/pod-migration/txdav/caldav/datastore/sql_external.py 2014-01-22 18:11:52 UTC (rev 12419)
@@ -110,27 +110,6 @@
raise AssertionError("CommonHomeExternal: not supported")
- def defaultCalendar(self, componentType, create=True):
- """
- No children.
- """
- raise AssertionError("CommonHomeExternal: not supported")
-
-
- def isDefaultCalendar(self, calendar):
- """
- No children.
- """
- raise AssertionError("CommonHomeExternal: not supported")
-
-
- def getDefaultAlarm(self, vevent, timed):
- """
- No children.
- """
- raise AssertionError("CommonHomeExternal: not supported")
-
-
def setDefaultAlarm(self, alarm, vevent, timed):
"""
No children.
@@ -138,13 +117,6 @@
raise AssertionError("CommonHomeExternal: not supported")
- def getAvailability(self):
- """
- No children.
- """
- raise AssertionError("CommonHomeExternal: not supported")
-
-
def setAvailability(self, availability):
"""
No children.
Modified: CalendarServer/branches/users/cdaboo/pod-migration/txdav/common/datastore/podding/conduit.py
===================================================================
--- CalendarServer/branches/users/cdaboo/pod-migration/txdav/common/datastore/podding/conduit.py 2014-01-22 04:07:03 UTC (rev 12418)
+++ CalendarServer/branches/users/cdaboo/pod-migration/txdav/common/datastore/podding/conduit.py 2014-01-22 18:11:52 UTC (rev 12419)
@@ -993,8 +993,11 @@
target = yield self._migrate_recv(txn, message, actionName)
try:
- # Operate on the L{CommonHomeChild}
- value = yield getattr(target, method)(*message.get("arguments", ()), **message.get("keywords", {}))
+ # Operate on the target
+ if method is not None:
+ value = yield getattr(target, method)(*message.get("arguments", ()), **message.get("keywords", {}))
+ else:
+ value = target
except Exception as e:
returnValue({
"result": "exception",
@@ -1024,6 +1027,7 @@
)
# Migrate calls
+PoddingConduit._make_migrate_action("get", None, transform_recv=PoddingConduit._to_externalize)
PoddingConduit._make_migrate_action("loadchildren", "loadChildren", transform_recv=PoddingConduit._to_externalize_list)
# Calls on L{CommonHomeChild} objects
Modified: CalendarServer/branches/users/cdaboo/pod-migration/txdav/common/datastore/podding/test/test_conduit.py
===================================================================
--- CalendarServer/branches/users/cdaboo/pod-migration/txdav/common/datastore/podding/test/test_conduit.py 2014-01-22 04:07:03 UTC (rev 12418)
+++ CalendarServer/branches/users/cdaboo/pod-migration/txdav/common/datastore/podding/test/test_conduit.py 2014-01-22 18:11:52 UTC (rev 12419)
@@ -47,6 +47,7 @@
from txdav.common.icommondatastore import ObjectResourceNameAlreadyExistsError, \
ObjectResourceNameNotAllowedError
from txdav.common.idirectoryservice import DirectoryRecordNotFoundError
+from txdav.caldav.datastore.sql_external import CalendarHomeExternal
class TestConduit (CommonCommonTests, txweb2.dav.test.util.TestCase):
@@ -1091,6 +1092,23 @@
"""
@inlineCallbacks
+ def test_get(self):
+ """
+ Test that action=loadallobjects works.
+ """
+
+ yield self.homeUnderTest(txn=self.newOtherTransaction(), name="puser01", create=True)
+ yield self.otherCommit()
+
+ remote_home = yield self.homeUnderTest(name="puser01", create=True)
+ remote_home._migration = _MIGRATION_STATUS_MIGRATING
+
+ result = yield remote_home.get()
+ self.assertTrue(isinstance(result, CalendarHomeExternal))
+ self.assertEqual(result.name(), remote_home.name())
+
+
+ @inlineCallbacks
def test_loadallobjects(self):
"""
Test that action=loadallobjects works.
Modified: CalendarServer/branches/users/cdaboo/pod-migration/txdav/common/datastore/podding/test/test_migration.py
===================================================================
--- CalendarServer/branches/users/cdaboo/pod-migration/txdav/common/datastore/podding/test/test_migration.py 2014-01-22 04:07:03 UTC (rev 12418)
+++ CalendarServer/branches/users/cdaboo/pod-migration/txdav/common/datastore/podding/test/test_migration.py 2014-01-22 18:11:52 UTC (rev 12419)
@@ -21,12 +21,103 @@
from txdav.common.datastore.podding.migration import MigrationController, \
UserAlreadyBeingMigrated
from txdav.common.datastore.sql import ECALENDARTYPE
+from twistedcaldav.ical import Component
class TestCalendarMigration(MultiStoreConduitTest):
"""
Test that the migration api works for migration.
"""
+ alarmhome1 = """BEGIN:VALARM
+ACTION:AUDIO
+TRIGGER;RELATED=START:-PT1M
+END:VALARM
+"""
+
+ alarmhome2 = """BEGIN:VALARM
+ACTION:AUDIO
+TRIGGER;RELATED=START:-PT2M
+END:VALARM
+"""
+
+ alarmhome3 = """BEGIN:VALARM
+ACTION:AUDIO
+TRIGGER;RELATED=START:-PT3M
+END:VALARM
+"""
+
+ alarmhome4 = """BEGIN:VALARM
+ACTION:AUDIO
+TRIGGER;RELATED=START:-PT4M
+END:VALARM
+"""
+
+ av1 = Component.fromString("""BEGIN:VCALENDAR
+VERSION:2.0
+CALSCALE:GREGORIAN
+PRODID:-//calendarserver.org//Zonal//EN
+BEGIN:VAVAILABILITY
+ORGANIZER:mailto:user01 at example.com
+UID:1 at example.com
+DTSTAMP:20061005T133225Z
+DTEND:20140101T000000Z
+BEGIN:AVAILABLE
+UID:1-1 at example.com
+DTSTAMP:20061005T133225Z
+SUMMARY:Monday to Friday from 9:00 to 17:00
+DTSTART:20130101T090000Z
+DTEND:20130101T170000Z
+RRULE:FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR
+END:AVAILABLE
+END:VAVAILABILITY
+END:VCALENDAR
+""")
+
+ alarmhome1_changed = """BEGIN:VALARM
+ACTION:AUDIO
+TRIGGER;RELATED=START:-PT5M
+END:VALARM
+"""
+
+ alarmhome2_changed = """BEGIN:VALARM
+ACTION:AUDIO
+TRIGGER;RELATED=START:-PT6M
+END:VALARM
+"""
+
+ alarmhome3_changed = """BEGIN:VALARM
+ACTION:AUDIO
+TRIGGER;RELATED=START:-PT7M
+END:VALARM
+"""
+
+ alarmhome4_changed = """BEGIN:VALARM
+ACTION:AUDIO
+TRIGGER;RELATED=START:-PT8M
+END:VALARM
+"""
+
+ av1_changed = Component.fromString("""BEGIN:VCALENDAR
+VERSION:2.0
+CALSCALE:GREGORIAN
+PRODID:-//calendarserver.org//Zonal//EN
+BEGIN:VAVAILABILITY
+ORGANIZER:mailto:user01 at example.com
+UID:2 at example.com
+DTSTAMP:20061005T133225Z
+DTEND:20140101T000000Z
+BEGIN:AVAILABLE
+UID:2-1 at example.com
+DTSTAMP:20061005T133225Z
+SUMMARY:Monday to Friday from 9:00 to 17:00
+DTSTART:20130101T090000Z
+DTEND:20130101T170000Z
+RRULE:FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR
+END:AVAILABLE
+END:VAVAILABILITY
+END:VCALENDAR
+""")
+
@inlineCallbacks
def _provision_remote(self):
"""
@@ -50,8 +141,7 @@
# Verify local home is not visible to normal api calls
local_home = yield self.homeUnderTest(name="puser01")
- self.assertTrue(local_home is not None)
- self.assertTrue(local_home.external())
+ self.assertTrue(local_home is None)
yield self.commit()
# Verify local migrating items exist
@@ -64,6 +154,57 @@
@inlineCallbacks
+ def test_step1_home_metadata(self):
+ """
+ Test that step1 works for sync'ing home metadata.
+ """
+
+ yield self._provision_remote()
+
+ # Setup remote metadata
+ txn = self.otherTransactionUnderTest()
+ remote_home = yield self.homeUnderTest(txn, name="puser01")
+ new_calendar = yield remote_home.createCalendarWithName("new_calendar")
+ remote_home.setDefaultCalendar(new_calendar, "VEVENT")
+ tasks_calendar = yield remote_home.calendarWithName("tasks")
+ remote_home.setDefaultCalendar(tasks_calendar, "VTODO")
+ yield remote_home.setDefaultAlarm(self.alarmhome1, True, True)
+ yield remote_home.setDefaultAlarm(self.alarmhome2, True, False)
+ yield remote_home.setDefaultAlarm(self.alarmhome3, False, True)
+ yield remote_home.setDefaultAlarm(self.alarmhome4, False, False)
+ yield remote_home.setAvailability(self.av1)
+ yield self.otherCommit()
+
+ migrator = MigrationController(self.storeUnderTest(), homeTypes=(ECALENDARTYPE,))
+ yield migrator.step1("puser01")
+
+ # Verify local home is not visible to normal api calls
+ local_home = yield self.homeUnderTest(name="puser01")
+ self.assertTrue(local_home is None)
+ yield self.commit()
+
+ # Verify local migrating items exist
+ local_home = yield self.homeUnderTest(name="puser01", migration=_MIGRATION_STATUS_MIGRATING)
+ self.assertTrue(local_home is not None)
+ self.assertTrue(not local_home.external())
+
+ results = yield local_home.loadChildren()
+ results = dict([(child.name(), child) for child in results])
+ self.assertEqual(set(results.keys()), set(("new_calendar", "calendar", "tasks", "inbox",)))
+
+ # Verify metadata
+ self.assertTrue(local_home.defaultCalendar("VEVENT"), results["new_calendar"].id())
+ self.assertTrue(local_home.defaultCalendar("VTODO"), results["tasks"].id())
+
+ self.assertTrue(local_home.getDefaultAlarm(True, True), self.alarmhome1)
+ self.assertTrue(local_home.getDefaultAlarm(True, False), self.alarmhome2)
+ self.assertTrue(local_home.getDefaultAlarm(False, True), self.alarmhome3)
+ self.assertTrue(local_home.getDefaultAlarm(False, False), self.alarmhome4)
+
+ self.assertTrue(local_home.getAvailability(), self.av1)
+
+
+ @inlineCallbacks
def test_step1_twice(self):
"""
Test that step1 fails a second time.
@@ -87,7 +228,7 @@
@inlineCallbacks
def test_step2_no_change(self):
"""
- Test that step1 fails a second time.
+ Test that step2 works when there are no remote changes.
"""
yield self._provision_remote()
@@ -115,7 +256,7 @@
@inlineCallbacks
def test_step2_changes(self):
"""
- Test that step1 fails a second time.
+ Test that step2 syncs changes.
"""
yield self._provision_remote()
@@ -198,3 +339,87 @@
self.assertEqual(rids, rids2)
tasks = yield local_home.calendarWithName("todos")
self.assertTrue(tasks.isUsedForFreeBusy())
+
+
+ @inlineCallbacks
+ def test_step2_changes_home_metadata(self):
+ """
+ Test that step2 syncs home metadata changes.
+ """
+
+ yield self._provision_remote()
+
+ # Setup remote metadata
+ txn = self.otherTransactionUnderTest()
+ remote_home = yield self.homeUnderTest(txn, name="puser01")
+ event_calendar = yield remote_home.calendarWithName("calendar")
+ remote_home.setDefaultCalendar(event_calendar, "VEVENT")
+ tasks_calendar = yield remote_home.calendarWithName("tasks")
+ remote_home.setDefaultCalendar(tasks_calendar, "VTODO")
+ yield remote_home.setDefaultAlarm(self.alarmhome1, True, True)
+ yield remote_home.setDefaultAlarm(self.alarmhome2, True, False)
+ yield remote_home.setDefaultAlarm(self.alarmhome3, False, True)
+ yield remote_home.setDefaultAlarm(self.alarmhome4, False, False)
+ yield remote_home.setAvailability(self.av1)
+ yield self.otherCommit()
+
+ migrator = MigrationController(self.storeUnderTest(), homeTypes=(ECALENDARTYPE,))
+ yield migrator.step1("puser01")
+
+ # Verify local migrating items exist
+ local_home = yield self.homeUnderTest(name="puser01", migration=_MIGRATION_STATUS_MIGRATING)
+ self.assertTrue(local_home is not None)
+ self.assertTrue(not local_home.external())
+
+ results = yield local_home.loadChildren()
+ results = dict([(child.name(), child) for child in results])
+ self.assertEqual(set(results.keys()), set(("calendar", "tasks", "inbox",)))
+
+ # Verify metadata
+ self.assertTrue(local_home.defaultCalendar("VEVENT"), results["calendar"].id())
+ self.assertTrue(local_home.defaultCalendar("VTODO"), results["tasks"].id())
+
+ self.assertTrue(local_home.getDefaultAlarm(True, True), self.alarmhome1)
+ self.assertTrue(local_home.getDefaultAlarm(True, False), self.alarmhome2)
+ self.assertTrue(local_home.getDefaultAlarm(False, True), self.alarmhome3)
+ self.assertTrue(local_home.getDefaultAlarm(False, False), self.alarmhome4)
+
+ self.assertTrue(local_home.getAvailability(), self.av1)
+
+ # Create new calendar and change metadata
+ txn = self.otherTransactionUnderTest()
+ remote_home = yield self.homeUnderTest(txn, name="puser01")
+ new_calendar = yield remote_home.createCalendarWithName("new_calendar")
+ remote_home.setDefaultCalendar(new_calendar, "VEVENT")
+ tasks_calendar = yield remote_home.calendarWithName("tasks")
+ remote_home.setDefaultCalendar(tasks_calendar, "VTODO")
+ yield remote_home.setDefaultAlarm(self.alarmhome1_changed, True, True)
+ yield remote_home.setDefaultAlarm(self.alarmhome2_changed, True, False)
+ yield remote_home.setDefaultAlarm(self.alarmhome3_changed, False, True)
+ yield remote_home.setDefaultAlarm(self.alarmhome4_changed, False, False)
+ yield remote_home.setAvailability(self.av1_changed)
+
+ yield self.otherCommit()
+
+ migrator = MigrationController(self.storeUnderTest(), homeTypes=(ECALENDARTYPE,))
+ yield migrator.step2("puser01")
+
+ # Verify local migrating items exist
+ local_home = yield self.homeUnderTest(name="puser01", migration=_MIGRATION_STATUS_MIGRATING)
+ self.assertTrue(local_home is not None)
+ self.assertTrue(not local_home.external())
+
+ results = yield local_home.loadChildren()
+ results = dict([(child.name(), child) for child in results])
+ self.assertEqual(set(results.keys()), set(("new_calendar", "calendar", "tasks", "inbox",)))
+
+ # Verify metadata
+ self.assertTrue(local_home.defaultCalendar("VEVENT"), results["new_calendar"].id())
+ self.assertTrue(local_home.defaultCalendar("VTODO"), results["tasks"].id())
+
+ self.assertTrue(local_home.getDefaultAlarm(True, True), self.alarmhome1_changed)
+ self.assertTrue(local_home.getDefaultAlarm(True, False), self.alarmhome2_changed)
+ self.assertTrue(local_home.getDefaultAlarm(False, True), self.alarmhome3_changed)
+ self.assertTrue(local_home.getDefaultAlarm(False, False), self.alarmhome4_changed)
+
+ self.assertTrue(local_home.getAvailability(), self.av1_changed)
Modified: CalendarServer/branches/users/cdaboo/pod-migration/txdav/common/datastore/sql.py
===================================================================
--- CalendarServer/branches/users/cdaboo/pod-migration/txdav/common/datastore/sql.py 2014-01-22 04:07:03 UTC (rev 12418)
+++ CalendarServer/branches/users/cdaboo/pod-migration/txdav/common/datastore/sql.py 2014-01-22 18:11:52 UTC (rev 12419)
@@ -1640,13 +1640,13 @@
@classmethod
@inlineCallbacks
- def makeClass(cls, transaction, homeData, metadataData):
+ def makeClass(cls, txn, homeData, metadataData):
"""
Build the actual home class taking into account the possibility that we might need to
switch in the external version of the class.
- @param transaction: transaction
- @type transaction: L{CommonStoreTransaction}
+ @param txn: transaction
+ @type txn: L{CommonStoreTransaction}
@param ownerUID: owner UID of home to load
@type ownerUID: C{str}
@param migration: migration status for home to load
@@ -1663,9 +1663,9 @@
# If the status is external we need to convert this object to a CommonHomeExternal class which will
# have the right behavior for non-hosted external users.
if status == _HOME_STATUS_EXTERNAL and migration == _MIGRATION_STATUS_NONE:
- home = cls._externalClass(transaction, ownerUID, resourceID)
+ home = cls._externalClass(txn, ownerUID, resourceID)
else:
- home = cls(transaction, ownerUID, migration=migration)
+ home = cls(txn, ownerUID, migration=migration)
for attr, value in zip(cls.homeAttributes(), homeData):
setattr(home, attr, value)
@@ -1675,7 +1675,7 @@
yield home._loadPropertyStore()
- for factory_type, factory in transaction._notifierFactories.items():
+ for factory_type, factory in txn._notifierFactories.items():
home.addNotifier(factory_type, factory.newNotifier(home))
yield home.made()
@@ -1685,12 +1685,12 @@
@classmethod
@inlineCallbacks
- def _getDBData(cls, transaction, ownerUID, migration=_MIGRATION_STATUS_NONE, no_cache=False):
+ def _getDBData(cls, txn, ownerUID, migration=_MIGRATION_STATUS_NONE, no_cache=False):
"""
Given a set of identifying information, load the metadataData rows for the object.
- @param transaction: transaction
- @type transaction: L{CommonStoreTransaction}
+ @param txn: transaction
+ @type txn: L{CommonStoreTransaction}
@param ownerUID: owner UID of home to load
@type ownerUID: C{str}
@param migration: migration status for home to load
@@ -1699,14 +1699,14 @@
@type no_cache: C{bool}
"""
- queryCacher = transaction._queryCacher
+ queryCacher = txn._queryCacher
homeData = None
if queryCacher:
cacheKey = queryCacher.keyForHomeData(cls._homeType, ownerUID, migration)
homeData = yield queryCacher.get(cacheKey)
if homeData is None:
- homeData = yield cls._homeColumnsFromOwnerQuery.on(transaction, ownerUID=ownerUID, migration=migration)
+ homeData = yield cls._homeColumnsFromOwnerQuery.on(txn, ownerUID=ownerUID, migration=migration)
if homeData:
homeData = homeData[0]
if not no_cache and queryCacher:
@@ -1725,20 +1725,20 @@
if metadataData is None:
# Don't have a cached copy
- metadataData = (yield cls._metaDataQuery.on(transaction, resourceID=resourceID))
+ metadataData = (yield cls._metaDataQuery.on(txn, resourceID=resourceID))
if metadataData:
metadataData = metadataData[0]
else:
metadataData = None
if queryCacher:
# Cache the metadataData
- yield queryCacher.setAfterCommit(transaction, cacheKey, metadataData)
+ yield queryCacher.setAfterCommit(txn, cacheKey, metadataData)
returnValue((homeData, metadataData))
- def __init__(self, transaction, ownerUID, migration=_MIGRATION_STATUS_NONE):
- self._txn = transaction
+ def __init__(self, txn, ownerUID, migration=_MIGRATION_STATUS_NONE):
+ self._txn = txn
self._ownerUID = ownerUID
self._resourceID = None
self._status = _HOME_STATUS_NORMAL
@@ -1770,6 +1770,34 @@
return succeed(None)
+ def externalize(self):
+ """
+ Create a dictionary mapping key attributes so this object can be sent over a cross-pod call
+ and reconstituted at the other end. Note that the other end may have a different schema so
+ the attributes may not match exactly and will need to be processed accordingly.
+ """
+ serialized = {}
+ serialized["home"] = dict([(attr[1:], getattr(self, attr, None)) for attr in self.homeAttributes()])
+ serialized["metadata"] = dict([(attr[1:], getattr(self, attr, None)) for attr in self.metadataAttributes()])
+ return serialized
+
+
+ @classmethod
+ @inlineCallbacks
+ def internalize(cls, txn, mapping):
+ """
+ Given a mapping generated by L{externalize}, convert the values into an array of database
+ like items that conforms to the ordering of L{_allColumns} so it can be fed into L{makeClass}.
+ Note that there may be a schema mismatch with the external data, so treat missing items as
+ C{None} and ignore extra items.
+ """
+
+ home = [mapping["home"].get(row[1:]) for row in cls.homeAttributes()]
+ metadata = [mapping["metadata"].get(row[1:]) for row in cls.metadataAttributes()]
+ child = yield cls.makeClass(txn, home, metadata)
+ returnValue(child)
+
+
def quotaAllowedBytes(self):
return self._txn.store().quota
@@ -2714,19 +2742,66 @@
assert self._migration == _MIGRATION_STATUS_MIGRATING
- # Get external home for the user (create if needed)
- otherHome = yield self._txn.homeWithUID(self._homeType, user, create=True)
- assert otherHome._status == _HOME_STATUS_EXTERNAL
+ # Get external home for the user. This is a "virtual" home in that it does not exist in
+ # the local DB - it is a representation of the remote object.
+ externalHome = yield self.getExternal()
+ assert externalHome._status == _HOME_STATUS_EXTERNAL
# Force the external home to look like it is migrating. This will enable certain external API calls
# that are normally disabled for sharing (e.g., ability to load all child resources).
- otherHome._migration = _MIGRATION_STATUS_MIGRATING
+ externalHome._migration = _MIGRATION_STATUS_MIGRATING
+ # Do sequence of sync operations - note that we may need to sync the home both before and after
+ # the children are sync'd
+ yield self.preSyncChildren(externalHome, final)
+ yield self.syncChildren(externalHome, final)
+ yield self.postSyncChildren(externalHome, final)
+
+
+ @inlineCallbacks
+ def getExternal(self):
+ """
+ Get a new L{CommonHomeExternal} object initialized to look like the remote object, i.e., with
+ all the remote attributes/metadata set as per the remote data.
+
+ @return: a L{CommonHomeExternal}.
+ """
+ remote = yield self._txn.store().conduit.send_get(self)
+ remote["home"]["status"] = _HOME_STATUS_EXTERNAL
+ remote = yield self.internalize(self._txn, remote)
+ returnValue(remote)
+
+
+ def preSyncChildren(self, externalHome, final):
+ """
+ Synchronize the external home with this home. This is called before the children are
+ sync'd - it needs to setup this home with attributes from the remote needed to sync children.
+ """
+ return succeed(None)
+
+
+ def postSyncChildren(self, externalHome, final):
+ """
+ Synchronize the external home with this home. This is called after the children are
+ sync'd - so attributes of the home that depend on child are valid and can be copied or
+ translated to their local equivalents (e.g., references to child resource-ids).
+ """
+
+ # TODO: quota setting
+ return succeed(None)
+
+
+ @inlineCallbacks
+ def syncChildren(self, externalHome, final):
+ """
+ Synchronize the remote children of the local external home.
+ """
+
local_children = yield self.loadChildren()
local_children = dict([(child.external_id(), child) for child in local_children if child.owned()])
# Get list of owned child collections
- remote_children = yield otherHome.loadChildren()
+ remote_children = yield externalHome.loadChildren()
remote_children = dict([(child.id(), child) for child in remote_children if child.owned()])
# Remove local ones to longer present on remote
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20140312/11dde774/attachment.html>
More information about the calendarserver-changes
mailing list