[CalendarServer-changes] [14490] CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/common /datastore
source_changes at macosforge.org
source_changes at macosforge.org
Sat Feb 28 14:26:20 PST 2015
Revision: 14490
http://trac.calendarserver.org//changeset/14490
Author: cdaboo at apple.com
Date: 2015-02-28 14:26:20 -0800 (Sat, 28 Feb 2015)
Log Message:
-----------
Checkpoint: final sync steps.
Modified Paths:
--------------
CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/common/datastore/podding/migration/home_sync.py
CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/common/datastore/podding/migration/test/test_home_sync.py
CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/common/datastore/podding/store_api.py
CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/common/datastore/podding/util.py
CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/common/datastore/sql.py
CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/common/datastore/sql_external.py
CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/common/datastore/sql_notification.py
Modified: CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/common/datastore/podding/migration/home_sync.py
===================================================================
--- CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/common/datastore/podding/migration/home_sync.py 2015-02-28 16:40:03 UTC (rev 14489)
+++ CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/common/datastore/podding/migration/home_sync.py 2015-02-28 22:26:20 UTC (rev 14490)
@@ -25,8 +25,8 @@
from txdav.caldav.datastore.sql import ManagedAttachment, CalendarBindRecord
from txdav.common.datastore.sql_external import NotificationCollectionExternal
from txdav.common.datastore.sql_notification import NotificationCollection
-from txdav.common.datastore.sql_tables import _HOME_STATUS_EXTERNAL, \
- _HOME_STATUS_MIGRATING
+from txdav.common.datastore.sql_tables import _HOME_STATUS_MIGRATING, _HOME_STATUS_DISABLED, \
+ _HOME_STATUS_EXTERNAL, _HOME_STATUS_NORMAL
from txdav.common.idirectoryservice import DirectoryRecordNotFoundError
from uuid import uuid4
@@ -95,6 +95,7 @@
self.store = store
self.diruid = diruid
+ self.disabledRemote = False
def label(self, detail):
@@ -190,25 +191,48 @@
pass
+ @inTransactionWrapper
@inlineCallbacks
- def disableRemoteHome(self):
+ def disableRemoteHome(self, txn):
"""
Mark the remote home as disabled.
"""
- # TODO: implement API on CommonHome to rename the ownerUID column and
- # change the status column.
- pass
+ # Calendar home
+ remote_home = yield self._remoteHome(txn)
+ yield remote_home.setStatus(_HOME_STATUS_DISABLED)
+ # Notification home
+ notifications = yield self._remoteNotificationsHome(txn)
+ yield notifications.setStatus(_HOME_STATUS_DISABLED)
+ self.disabledRemote = True
+
+
+ @inTransactionWrapper
@inlineCallbacks
- def enableLocalHome(self):
+ def enableLocalHome(self, txn):
"""
- Mark the local home as enabled.
+ Mark the local home as enabled and remove any previously existing external home.
"""
- # TODO: implement API on CommonHome to rename the ownerUID column and
- # change the status column. Also adjust NotificationCollection.
+ # Disable any local external homes
+ oldhome = yield txn.calendarHomeWithUID(self.diruid, status=_HOME_STATUS_EXTERNAL)
+ if oldhome is not None:
+ yield oldhome.setStatus(_HOME_STATUS_DISABLED)
+ oldnotifications = yield txn.notificationsWithUID(self.diruid, status=_HOME_STATUS_EXTERNAL)
+ if oldnotifications:
+ yield oldnotifications.setStatus(_HOME_STATUS_DISABLED)
+
+ # Enable the migrating ones
+ newhome = yield txn.calendarHomeWithUID(self.diruid, status=_HOME_STATUS_MIGRATING)
+ if newhome is not None:
+ yield newhome.setStatus(_HOME_STATUS_NORMAL)
+ newnotifications = yield txn.notificationsWithUID(self.diruid, status=_HOME_STATUS_MIGRATING)
+ if newnotifications:
+ yield newnotifications.setStatus(_HOME_STATUS_NORMAL)
+
+ # TODO: purge the old ones
pass
@@ -256,7 +280,7 @@
Make sure the home meta-data (alarms, default calendars) is properly sync'd
"""
- remote_home = yield self._remoteHome(txn=txn)
+ remote_home = yield self._remoteHome(txn)
yield remote_home.readMetaData()
calendars = yield CalendarMigrationRecord.querysimple(txn, calendarHomeResourceID=self.homeId)
@@ -273,11 +297,25 @@
"""
from txdav.caldav.datastore.sql_external import CalendarHomeExternal
- resourceID = yield txn.store().conduit.send_home_resource_id(txn, self.record)
+ resourceID = yield txn.store().conduit.send_home_resource_id(txn, self.record, migrating=True)
home = CalendarHomeExternal.makeSyntheticExternalHome(txn, self.record.uid, resourceID) if resourceID is not None else None
+ if self.disabledRemote:
+ home._migratingHome = True
returnValue(home)
+ @inlineCallbacks
+ def _remoteNotificationsHome(self, txn):
+ """
+ Create a synthetic external home object that maps to the actual remote home.
+ """
+
+ notifications = yield NotificationCollectionExternal.notificationsWithUID(txn, self.diruid, create=True)
+ if self.disabledRemote:
+ notifications._migratingHome = True
+ returnValue(notifications)
+
+
def _localHome(self, txn):
"""
Get the home on this pod that will have data migrated to it.
@@ -917,7 +955,7 @@
Get all the existing L{NotificationObjectRecord}'s from the remote store.
"""
- notifications = yield NotificationCollectionExternal.notificationsWithUID(txn, self.diruid, True)
+ notifications = yield self._remoteNotificationsHome(txn)
records = yield notifications.notificationObjectRecords()
for record in records:
# This needs to be reset when added to the local store
@@ -936,7 +974,7 @@
Create L{NotificationObjectRecord} records in the local store.
"""
- notifications = yield NotificationCollection.notificationsWithUID(txn, self.diruid, True, _HOME_STATUS_EXTERNAL)
+ notifications = yield NotificationCollection.notificationsWithUID(txn, self.diruid, status=_HOME_STATUS_MIGRATING, create=True)
for record in records:
# Do this via the "write" API so that sync revisions are updated properly, rather than just
# inserting the records directly.
Modified: CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/common/datastore/podding/migration/test/test_home_sync.py
===================================================================
--- CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/common/datastore/podding/migration/test/test_home_sync.py 2015-02-28 16:40:03 UTC (rev 14489)
+++ CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/common/datastore/podding/migration/test/test_home_sync.py 2015-02-28 22:26:20 UTC (rev 14490)
@@ -806,7 +806,8 @@
self.assertEqual(changed, set(((yield _mapLocalIDToRemote(id0_1)), (yield _mapLocalIDToRemote(id0_2)),)))
self.assertEqual(removed, set())
- # Link attachments
+ # Link attachments (after home is disabled)
+ yield syncer.disableRemoteHome()
len_links = yield syncer.linkAttachments()
self.assertEqual(len_links, 3)
@@ -891,6 +892,7 @@
# Sync from remote side
syncer = CrossPodHomeSync(self.theStoreUnderTest(1), "user01")
yield syncer.loadRecord()
+ yield syncer.disableRemoteHome()
yield syncer.delegateReconcile()
# Now have local delegates
@@ -952,6 +954,7 @@
syncer = CrossPodHomeSync(self.theStoreUnderTest(1), "user01")
yield syncer.loadRecord()
yield syncer.prepareCalendarHome()
+ yield syncer.disableRemoteHome()
changes = yield syncer.notificationsReconcile()
self.assertEqual(changes, 2)
@@ -959,8 +962,7 @@
notifications = yield NotificationCollection.notificationsWithUID(
self.theTransactionUnderTest(1),
"user01",
- True,
- _HOME_STATUS_EXTERNAL
+ status=_HOME_STATUS_MIGRATING,
)
results = yield notifications.notificationObjects()
self.assertEqual(len(results), 2)
@@ -1059,6 +1061,7 @@
syncer = CrossPodHomeSync(self.theStoreUnderTest(1), "user01")
yield syncer.loadRecord()
yield syncer.sync()
+ yield syncer.disableRemoteHome()
changes = yield syncer.sharedByCollectionsReconcile()
self.assertEqual(changes, 2)
changes = yield syncer.sharedToCollectionsReconcile()
@@ -1125,6 +1128,7 @@
syncer = CrossPodHomeSync(self.theStoreUnderTest(1), "user01")
yield syncer.loadRecord()
yield syncer.sync()
+ yield syncer.disableRemoteHome()
changes = yield syncer.sharedByCollectionsReconcile()
self.assertEqual(changes, 3)
changes = yield syncer.sharedToCollectionsReconcile()
Modified: CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/common/datastore/podding/store_api.py
===================================================================
--- CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/common/datastore/podding/store_api.py 2015-02-28 16:40:03 UTC (rev 14489)
+++ CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/common/datastore/podding/store_api.py 2015-02-28 22:26:20 UTC (rev 14490)
@@ -18,6 +18,7 @@
from txdav.caldav.datastore.scheduling.freebusy import generateFreeBusyInfo
from txdav.common.datastore.podding.util import UtilityConduitMixin
+from txdav.common.datastore.sql_tables import _HOME_STATUS_DISABLED
from twistedcaldav.caldavxml import TimeRange
@@ -28,17 +29,20 @@
"""
@inlineCallbacks
- def send_home_resource_id(self, txn, recipient):
+ def send_home_resource_id(self, txn, recipient, migrating=False):
"""
Lookup the remote resourceID matching the specified directory uid.
@param ownerUID: directory record for user whose home is needed
@type ownerUID: L{DirectroryRecord}
+ @param migrating: if L{True} then also return a disbaled home
+ @type migrating: L{bool}
"""
request = {
"action": "home-resource_id",
"ownerUID": recipient.uid,
+ "migrating": migrating,
}
response = yield self.sendRequest(txn, recipient, request)
@@ -55,6 +59,8 @@
"""
home = yield txn.calendarHomeWithUID(request["ownerUID"])
+ if home is None and request["migrating"]:
+ home = yield txn.calendarHomeWithUID(request["ownerUID"], status=_HOME_STATUS_DISABLED)
returnValue(home.id() if home is not None else None)
@@ -154,8 +160,9 @@
# These are the actions on store objects we need to expose via the conduit api
# Calls on L{CommonHome} objects
-UtilityConduitMixin._make_simple_action(StoreAPIConduitMixin, "home_metadata", "serialize", classMethod=False)
-UtilityConduitMixin._make_simple_action(StoreAPIConduitMixin, "home_get_all_group_attendees", "getAllGroupAttendees", classMethod=False, transform_recv_result=StoreAPIConduitMixin._to_serialize_pair_list)
+UtilityConduitMixin._make_simple_action(StoreAPIConduitMixin, "home_metadata", "serialize")
+UtilityConduitMixin._make_simple_action(StoreAPIConduitMixin, "home_set_status", "setStatus")
+UtilityConduitMixin._make_simple_action(StoreAPIConduitMixin, "home_get_all_group_attendees", "getAllGroupAttendees", transform_recv_result=StoreAPIConduitMixin._to_serialize_pair_list)
UtilityConduitMixin._make_simple_action(StoreAPIConduitMixin, "home_shared_to_records", "sharedToBindRecords", transform_recv_result=StoreAPIConduitMixin._to_serialize_dict_list_serialized_value)
# Calls on L{CommonHomeChild} objects
@@ -185,4 +192,5 @@
UtilityConduitMixin._make_simple_action(StoreAPIConduitMixin, "objectresource_remove", "remove")
# Calls on L{NotificationCollection} objects
-UtilityConduitMixin._make_simple_action(StoreAPIConduitMixin, "notification_all_records", "notificationObjectRecords", classMethod=False, transform_recv_result=UtilityConduitMixin._to_serialize_list)
+UtilityConduitMixin._make_simple_action(StoreAPIConduitMixin, "notification_set_status", "setStatus")
+UtilityConduitMixin._make_simple_action(StoreAPIConduitMixin, "notification_all_records", "notificationObjectRecords", transform_recv_result=UtilityConduitMixin._to_serialize_list)
Modified: CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/common/datastore/podding/util.py
===================================================================
--- CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/common/datastore/podding/util.py 2015-02-28 16:40:03 UTC (rev 14489)
+++ CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/common/datastore/podding/util.py 2015-02-28 22:26:20 UTC (rev 14490)
@@ -83,6 +83,8 @@
if viewer_home:
result["homeType"] = viewer_home._homeType
result["homeUID"] = viewer_home.uid()
+ if getattr(viewer_home, "_migratingHome", False):
+ result["allowDisabledHome"] = True
if home_child:
if home_child.owned():
result["homeChildID"] = home_child.id()
@@ -97,6 +99,8 @@
elif notification:
result["notificationUID"] = notification.uid()
+ if getattr(notification, "_migratingHome", False):
+ result["allowDisabledHome"] = True
recipient = yield self.store.directoryService().recordWithUID(notification.uid())
returnValue((txn, result, recipient.server(),))
@@ -111,6 +115,9 @@
returnObject = txn
classObject = None
+ if "allowDisabledHome" in request:
+ txn._allowDisabled = True
+
if "homeUID" in request:
home = yield txn.homeWithUID(request["homeType"], request["homeUID"])
if home is None:
Modified: CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/common/datastore/sql.py
===================================================================
--- CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/common/datastore/sql.py 2015-02-28 16:40:03 UTC (rev 14489)
+++ CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/common/datastore/sql.py 2015-02-28 22:26:20 UTC (rev 14490)
@@ -64,7 +64,8 @@
from txdav.common.datastore.sql_notification import NotificationCollection
from txdav.common.datastore.sql_tables import _BIND_MODE_OWN, _BIND_STATUS_ACCEPTED, \
_HOME_STATUS_EXTERNAL, _HOME_STATUS_NORMAL, \
- _HOME_STATUS_PURGING, schema, splitSQLString, _HOME_STATUS_MIGRATING
+ _HOME_STATUS_PURGING, schema, splitSQLString, _HOME_STATUS_MIGRATING, \
+ _HOME_STATUS_DISABLED
from txdav.common.datastore.sql_util import _SharedSyncLogic
from txdav.common.datastore.sql_sharing import SharingHomeMixIn, SharingMixIn
from txdav.common.icommondatastore import ConcurrentModification, \
@@ -584,6 +585,7 @@
self._bumpedRevisionAlready = set()
self._label = label
self._migrating = migrating
+ self._allowDisabled = False
self._primaryHomeType = None
self._disableCache = disableCache or not store.queryCachingEnabled()
if disableCache:
@@ -764,11 +766,11 @@
@memoizedKey("uid", "_notificationHomes")
- def notificationsWithUID(self, uid, create=True):
+ def notificationsWithUID(self, uid, status=None, create=True):
"""
Implement notificationsWithUID.
"""
- return NotificationCollection.notificationsWithUID(self, uid, create)
+ return NotificationCollection.notificationsWithUID(self, uid, create=create)
@memoizedKey("rid", "_notificationHomes")
@@ -1811,6 +1813,8 @@
cacheKeys.append(queryCacher.keyForHomeWithUID(cls._homeType, uid, status))
else:
statusSet = (_HOME_STATUS_NORMAL, _HOME_STATUS_EXTERNAL, _HOME_STATUS_PURGING)
+ if txn._allowDisabled:
+ statusSet += (_HOME_STATUS_DISABLED,)
query = query.And(cls._homeSchema.STATUS.In(statusSet))
if queryCacher:
for item in statusSet:
@@ -1826,7 +1830,7 @@
else:
result = None
- # If nothing in thr cache, do the SQL query and cache the result
+ # If nothing in the cache, do the SQL query and cache the result
if result is None:
results = yield Select(
cls.homeColumns(),
@@ -1834,12 +1838,14 @@
Where=query,
).on(txn)
- # Pick the internal one
if len(results) > 1:
- if results[0][cls.homeColumns().index(cls._homeSchema.STATUS)] == _HOME_STATUS_NORMAL:
- result = results[0]
- else:
- result = results[1]
+ # Pick the best one in order: normal, disabled and external
+ byStatus = dict([(result[cls.homeColumns().index(cls._homeSchema.STATUS)], result) for result in results])
+ result = byStatus.get(_HOME_STATUS_NORMAL)
+ if result is None:
+ result = byStatus.get(_HOME_STATUS_DISABLED)
+ if result is None:
+ result = byStatus.get(_HOME_STATUS_EXTERNAL)
elif results:
result = results[0]
else:
Modified: CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/common/datastore/sql_external.py
===================================================================
--- CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/common/datastore/sql_external.py 2015-02-28 16:40:03 UTC (rev 14489)
+++ CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/common/datastore/sql_external.py 2015-02-28 22:26:20 UTC (rev 14490)
@@ -85,6 +85,10 @@
self.deserialize(mapping)
+ def setStatus(self, newStatus):
+ return self._txn.store().conduit.send_home_set_status(self, newStatus)
+
+
def external(self):
"""
Is this an external home.
@@ -493,11 +497,24 @@
"""
@classmethod
- def notificationsWithUID(cls, txn, uid, create):
- return super(NotificationCollectionExternal, cls).notificationsWithUID(txn, uid, create, expected_status=_HOME_STATUS_EXTERNAL)
+ def notificationsWithUID(cls, txn, uid, create=False):
+ return super(NotificationCollectionExternal, cls).notificationsWithUID(txn, uid, status=_HOME_STATUS_EXTERNAL, create=create)
+ def initFromStore(self):
+ """
+ NoOp for an external share as there are no properties.
+ """
+ return succeed(self)
+
+
@inlineCallbacks
def notificationObjectRecords(self):
results = yield self._txn.store().conduit.send_notification_all_records(self)
returnValue(map(NotificationObjectRecord.deserialize, results))
+
+
+ def setStatus(self, newStatus):
+ return self._txn.store().conduit.send_notification_set_status(self, newStatus)
+
+NotificationCollection._externalClass = NotificationCollectionExternal
Modified: CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/common/datastore/sql_notification.py
===================================================================
--- CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/common/datastore/sql_notification.py 2015-02-28 16:40:03 UTC (rev 14489)
+++ CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/common/datastore/sql_notification.py 2015-02-28 22:26:20 UTC (rev 14490)
@@ -27,7 +27,7 @@
from twistedcaldav.dateops import datetimeMktime
from txdav.base.propertystore.sql import PropertyStore
from txdav.common.datastore.sql_tables import schema, _HOME_STATUS_NORMAL, \
- _HOME_STATUS_EXTERNAL
+ _HOME_STATUS_EXTERNAL, _HOME_STATUS_DISABLED, _HOME_STATUS_MIGRATING
from txdav.common.datastore.sql_util import _SharedSyncLogic
from txdav.common.icommondatastore import RecordNotAllowedError
from txdav.common.idirectoryservice import DirectoryRecordNotFoundError
@@ -49,20 +49,74 @@
implements(INotificationCollection)
compareAttributes = (
- "_uid",
+ "_ownerUID",
"_resourceID",
)
_revisionsSchema = schema.NOTIFICATION_OBJECT_REVISIONS
_homeSchema = schema.NOTIFICATION_HOME
+ _externalClass = None
- def __init__(self, txn, uid, resourceID, status):
+ @classmethod
+ def makeClass(cls, transaction, homeData):
+ """
+ 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 homeData: home table column data
+ @type homeData: C{list}
+ """
+
+ status = homeData[cls.homeColumns().index(cls._homeSchema.STATUS)]
+ if status == _HOME_STATUS_EXTERNAL:
+ home = cls._externalClass(transaction, homeData)
+ else:
+ home = cls(transaction, homeData)
+ return home.initFromStore()
+
+
+ @classmethod
+ def homeColumns(cls):
+ """
+ Return a list of column names to retrieve when doing an ownerUID->home lookup.
+ """
+
+ # Common behavior is to have created and modified
+
+ return (
+ cls._homeSchema.RESOURCE_ID,
+ cls._homeSchema.OWNER_UID,
+ cls._homeSchema.STATUS,
+ )
+
+
+ @classmethod
+ def homeAttributes(cls):
+ """
+ Return a list of attributes names to map L{homeColumns} to.
+ """
+
+ # Common behavior is to have created and modified
+
+ return (
+ "_resourceID",
+ "_ownerUID",
+ "_status",
+ )
+
+
+ def __init__(self, txn, homeData):
+
self._txn = txn
- self._uid = uid
- self._resourceID = resourceID
- self._status = status
+
+ for attr, value in zip(self.homeAttributes(), homeData):
+ setattr(self, attr, value)
+
+ self._txn = txn
self._dataVersion = None
self._notifications = {}
self._notificationNames = None
@@ -72,25 +126,15 @@
# as well as the home it is in
self._notifiers = dict([(factory_name, factory.newNotifier(self),) for factory_name, factory in txn._notifierFactories.items()])
- _resourceIDFromUIDQuery = Select(
- [_homeSchema.RESOURCE_ID, _homeSchema.STATUS],
- From=_homeSchema,
- Where=_homeSchema.OWNER_UID == Parameter("uid")
- )
- _UIDFromResourceIDQuery = Select(
- [_homeSchema.OWNER_UID],
- From=_homeSchema,
- Where=_homeSchema.RESOURCE_ID == Parameter("rid")
- )
+ @inlineCallbacks
+ def initFromStore(self):
+ """
+ Initialize this object from the store.
+ """
- _provisionNewNotificationsQuery = Insert(
- {
- _homeSchema.OWNER_UID: Parameter("uid"),
- _homeSchema.STATUS: Parameter("status"),
- },
- Return=_homeSchema.RESOURCE_ID
- )
+ yield self._loadPropertyStore()
+ returnValue(self)
@property
@@ -103,28 +147,78 @@
@classmethod
+ def notificationsWithUID(cls, txn, uid, status=None, create=True):
+ return cls.notificationsWith(txn, None, uid, status, create=create)
+
+
+ @classmethod
+ def notificationsWithResourceID(cls, txn, rid):
+ return cls.notificationsWith(txn, rid, None)
+
+
+ @classmethod
@inlineCallbacks
- def notificationsWithUID(cls, txn, uid, create, expected_status=_HOME_STATUS_NORMAL):
+ def notificationsWith(cls, txn, rid, uid, status=None, create=True):
"""
@param uid: I'm going to assume uid is utf-8 encoded bytes
"""
- rows = yield cls._resourceIDFromUIDQuery.on(txn, uid=uid)
+ if rid is not None:
+ query = cls._homeSchema.RESOURCE_ID == rid
+ elif uid is not None:
+ query = cls._homeSchema.OWNER_UID == uid
+ if status is not None:
+ query = query.And(cls._homeSchema.STATUS == status)
+ else:
+ statusSet = (_HOME_STATUS_NORMAL, _HOME_STATUS_EXTERNAL,)
+ if txn._allowDisabled:
+ statusSet += (_HOME_STATUS_DISABLED,)
+ query = query.And(cls._homeSchema.STATUS.In(statusSet))
+ else:
+ raise AssertionError("One of rid or uid must be set")
- if rows:
- resourceID = rows[0][0]
- status = rows[0][1]
- if status != expected_status:
- raise RecordNotAllowedError("Notifications status mismatch: {} != {}".format(status, expected_status))
- created = False
- elif create:
+ results = yield Select(
+ cls.homeColumns(),
+ From=cls._homeSchema,
+ Where=query,
+ ).on(txn)
+
+ if len(results) > 1:
+ # Pick the best one in order: normal, disabled and external
+ byStatus = dict([(result[cls.homeColumns().index(cls._homeSchema.STATUS)], result) for result in results])
+ result = byStatus.get(_HOME_STATUS_NORMAL)
+ if result is None:
+ result = byStatus.get(_HOME_STATUS_DISABLED)
+ if result is None:
+ result = byStatus.get(_HOME_STATUS_EXTERNAL)
+ elif results:
+ result = results[0]
+ else:
+ result = None
+
+ if result:
+ # Return object that already exists in the store
+ homeObject = yield cls.makeClass(txn, result)
+ returnValue(homeObject)
+ else:
+ # Can only create when uid is specified
+ if not create or uid is None:
+ returnValue(None)
+
# Determine if the user is local or external
record = yield txn.directoryService().recordWithUID(uid.decode("utf-8"))
if record is None:
raise DirectoryRecordNotFoundError("Cannot create home for UID since no directory record exists: {}".format(uid))
- status = _HOME_STATUS_NORMAL if record.thisServer() else _HOME_STATUS_EXTERNAL
- if status != expected_status:
- raise RecordNotAllowedError("Notifications status mismatch: {} != {}".format(status, expected_status))
+ if status is None:
+ createStatus = _HOME_STATUS_NORMAL if record.thisServer() else _HOME_STATUS_EXTERNAL
+ elif status == _HOME_STATUS_MIGRATING:
+ if record.thisServer():
+ raise RecordNotAllowedError("Cannot migrate a user data for a user already hosted on this server")
+ createStatus = status
+ elif status in (_HOME_STATUS_NORMAL, _HOME_STATUS_EXTERNAL,):
+ createStatus = status
+ else:
+ raise RecordNotAllowedError("Cannot create home with status {}: {}".format(status, uid))
# Use savepoint so we can do a partial rollback if there is a race
# condition where this row has already been inserted
@@ -132,55 +226,52 @@
yield savepoint.acquire(txn)
try:
- resourceID = str((
- yield cls._provisionNewNotificationsQuery.on(txn, uid=uid, status=status)
- )[0][0])
+ resourceid = (yield Insert(
+ {
+ cls._homeSchema.OWNER_UID: uid,
+ cls._homeSchema.STATUS: createStatus,
+ },
+ Return=cls._homeSchema.RESOURCE_ID
+ ).on(txn))[0][0]
except Exception:
# FIXME: Really want to trap the pg.DatabaseError but in a non-
# DB specific manner
yield savepoint.rollback(txn)
# Retry the query - row may exist now, if not re-raise
- rows = yield cls._resourceIDFromUIDQuery.on(txn, uid=uid)
- if rows:
- resourceID = rows[0][0]
- status = rows[0][1]
- if status != expected_status:
- raise RecordNotAllowedError("Notifications status mismatch: {} != {}".format(status, expected_status))
- created = False
+ results = yield Select(
+ cls.homeColumns(),
+ From=cls._homeSchema,
+ Where=query,
+ ).on(txn)
+ if results:
+ homeObject = yield cls.makeClass(txn, results[0])
+ returnValue(homeObject)
else:
raise
else:
- created = True
yield savepoint.release(txn)
- else:
- returnValue(None)
- collection = cls(txn, uid, resourceID, status)
- yield collection._loadPropertyStore()
- if created:
- yield collection._initSyncToken()
- yield collection.notifyChanged()
- returnValue(collection)
+ # Note that we must not cache the owner_uid->resource_id
+ # mapping in the query cacher when creating as we don't want that to appear
+ # until AFTER the commit
+ results = yield Select(
+ cls.homeColumns(),
+ From=cls._homeSchema,
+ Where=cls._homeSchema.RESOURCE_ID == resourceid,
+ ).on(txn)
+ homeObject = yield cls.makeClass(txn, results[0])
+ if homeObject.normal():
+ yield homeObject._initSyncToken()
+ yield homeObject.notifyChanged()
+ returnValue(homeObject)
- @classmethod
- @inlineCallbacks
- def notificationsWithResourceID(cls, txn, rid):
- rows = yield cls._UIDFromResourceIDQuery.on(txn, rid=rid)
- if rows:
- uid = rows[0][0]
- result = (yield cls.notificationsWithUID(txn, uid, create=False))
- returnValue(result)
- else:
- returnValue(None)
-
-
@inlineCallbacks
def _loadPropertyStore(self):
self._propertyStore = yield PropertyStore.load(
- self._uid,
- self._uid,
+ self._ownerUID,
+ self._ownerUID,
None,
self._txn,
self._resourceID,
@@ -224,9 +315,41 @@
def uid(self):
- return self._uid
+ return self._ownerUID
+ @inlineCallbacks
+ def setStatus(self, newStatus):
+ """
+ Mark this home as being purged.
+ """
+ # Only if different
+ if self._status != newStatus:
+ yield Update(
+ {self._homeSchema.STATUS: newStatus},
+ Where=(self._homeSchema.RESOURCE_ID == self._resourceID),
+ ).on(self._txn)
+ self._status = newStatus
+
+
+ def normal(self):
+ """
+ Is this an normal (internal) home.
+
+ @return: a L{bool}.
+ """
+ return self._status == _HOME_STATUS_NORMAL
+
+
+ def external(self):
+ """
+ Is this an external home.
+
+ @return: a L{bool}.
+ """
+ return self._status == _HOME_STATUS_EXTERNAL
+
+
def owned(self):
return True
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20150228/35be6c0d/attachment-0001.html>
More information about the calendarserver-changes
mailing list